Category: Uncategorized

a voice of the costomer


비용을 지불하고 사용하는 서비스임에도 불구하고 VOC가, 대기업 특성상, 아주 더럽게(?) 사용될 수도 있다는 생각에 차마 SEND 버튼을 누르지는 못하였다. 아직 충분히 늙지 않았나보다.

고객센터에서 전화 응대를 받는 사람들은 문제가 없는데,

장애가 발생 문의 후 전화하는 새끼들은 싸가지가 장난이 아니다. 이건 뭐… 같은 회사 옆 부서 새끼가 툭툭대면서 전화하는 것 같다.

장애를 고치고도 (뭐 빨리 해 줄 필요도 없었는데) 우리가 마음이 좋아서 고쳐줬다.
확인해 보시든가…. 뭐 이런 자세다.

애시당초 “고쳐는 드릴께..!” 뭐 이런 느낌?

=====================================================================
두 가지를 문의드렸었습니다.

하나는 Cloud Console 에서 reseller 용 사용자 관리메뉴를 여쭤봤고
두번째는 uc????s????e-sdk에서 어떤 API를 사용하면 될지를 여쭤봤습니다.

언급하신 PDF의 해당 위치에 제가 문의드린 사항에 대한 설명이 있나요?

아래처럼 대답을 주셔야 하는 거 아닌가요?

현재 리셀러들을 위한 UI는 별도로 준비되어 있지 않습니다.
또한 uc??????-sdk 에도 해당 서비스에 대한 client API 가 준비되어 있지 않습니다.
API 를 이용한 연동방법은 아래 메뉴얼의 “제 7 장 Reseller service.” 를 참고하시기 바랍니다.

https://uc????b??.ol???.com/manual/????.pdf

문의하기 답변 제목 : Re: 리셀러 서비스에서 사용자(user)를 추가/갱신/삭제 할 수 있는 메뉴는 어디에 있나요
K? uc???? ????er를 사용해 주셔서 감사합니다.
문의하신 내용에 대한 답변을 드립니다.
[답변내용]
아래 메뉴얼의 “제 7 장 Reseller service.” 를 참고 부탁 드립니다.
https://uc????b.ol???.com/???/???.pdf
추가적인 문의 사항이 있으시면 재문의 주시기 바랍니다.
감사합니다.
답변 내용 이외의 응대에 대한 불만족 사항은 uc????b-??c@k?.?으로 보내주시기 바랍니다.

Advertisements

delegation with default methods


@Entity
public class Property extends BaseEntity {

    public static interface Owner<T extends BaseEntity & Owner<T>> {

        List<Property> getProperties();

        default void setProperties(List<Property> properties) {
            getProperties().clear();
            getProperties().addAll(properties);
        }

        @SuppressWarnings("unchecked")
        default T properties(List<Property> properties) {
            setProperties(properties);
            return (T) this;
        }

        @SuppressWarnings("unchecked")
        default T addProperty(@NotNull final Property property) {
            getProperties().add(property);
            return (T) this;
        }

        default T addProperty(@NotNull final String name,
                              @NotNull final String value) {
            return addProperty(new Property().name(name).value(value));
        }

        default Stream<Property> propertyStream() {
            return getProperties().stream();
        }

        default T filterProperties(@NotNull final java.util.function.Predicate<Property> predicate) {
            return properties(propertyStream().filter(predicate).collect(toList()));
        }
    }

    @Column(name = "NAME", nullable = false)
    private String name;

    @Column(name = "VALUE_", nullable = false)
    private String value;
}
@Entity
public class Event extends BaseEntity implements Property.Owner<Event> {

    @Override
    public List<Property> getProperties() {
        return (properties = ofNullable(properties).orElse(new ArrayList<>()));
    }

    @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER,
               mappedBy = "event", orphanRemoval = true)
    private List<Property> properties;
}

Producing ImageIO capabilities through JAX-RS, v2


import java.util.Arrays;
import java.util.logging.Logger;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.Response;
import javax.xml.bind.annotation.XmlEnum;
import static javax.imageio.ImageIO.getReaderFileSuffixes;
import static javax.imageio.ImageIO.getReaderFormatNames;
import static javax.imageio.ImageIO.getReaderMIMETypes;
import static javax.imageio.ImageIO.getWriterFileSuffixes;
import static javax.imageio.ImageIO.getWriterFormatNames;
import static javax.imageio.ImageIO.getWriterMIMETypes;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static java.util.stream.Collectors.toList;
import javax.ws.rs.MatrixParam;
import static java.util.Arrays.stream;
import static java.util.Comparator.comparing;
import static java.util.logging.Logger.getLogger;
import kr.co.mediamonster.ippl.ws.rs.BaseResource;

@Path("/miscellaneous/imageIoCapabilities")
public class ImageIoCapabilitiesResource extends BaseResource {

    @XmlEnum
    public static enum Feature {
        FileSuffix() {
            @Override
            void probe(final Set<ImageIoCapability> capabilities) {
                stream(getReaderFileSuffixes()).forEach(
                        value -> capabilities.add(
                                new ImageIoCapability().feature(this)
                                .readable(true).value(value))
                );
                stream(getWriterFileSuffixes()).forEach(
                        value -> capabilities.add(
                                capabilities.stream()
                                .filter(e -> this == e.feature
                                             && value.equals(e.value))
                                .findFirst()
                                .orElseGet(() -> new ImageIoCapability()
                                        .feature(this).value(value)
                                ).writable(true)));
            }
        }, FormatName() {
            @Override
            void probe(final Set<ImageIoCapability> capabilities) {
                stream(getReaderFormatNames()).forEach(
                        value -> capabilities.add(
                                new ImageIoCapability().feature(this)
                                .readable(true).value(value))
                );
                stream(getWriterFormatNames()).forEach(
                        value -> capabilities.add(
                                capabilities.stream()
                                .filter(e -> this == e.feature
                                             && value.equals(e.value))
                                .findFirst()
                                .orElseGet(() -> new ImageIoCapability()
                                        .feature(this).value(value)
                                ).writable(true)));
            }
        }, MIMEType() {
            @Override
            void probe(final Set<ImageIoCapability> capabilities) {
                stream(getReaderMIMETypes()).forEach(value -> capabilities.add(
                        new ImageIoCapability().feature(this).readable(true)
                        .value(value))
                );
                stream(getWriterMIMETypes()).forEach(value -> capabilities.add(
                        capabilities.stream()
                        .filter(e -> this == e.feature && value.equals(e.value))
                        .findFirst()
                        .orElseGet(() -> new ImageIoCapability().feature(this)
                                .value(value)
                        ).writable(true)));
            }
        };

        abstract void probe(Set<ImageIoCapability> capabilities);
    }

    @XmlAccessorType(XmlAccessType.NONE)
    @XmlRootElement
    public static class ImageIoCapability {

        @Override
        public String toString() {
            return "ImageIoCapability{"
                   + "feature=" + feature
                   + ", readable=" + readable
                   + ", writable=" + writable
                   + ", value=" + value
                   + "}";
        }

        @Override
        public int hashCode() {
            int hash = 5;
            hash = 83 * hash + Objects.hashCode(this.feature);
            hash = 83 * hash + Objects.hashCode(this.value);
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final ImageIoCapability other = (ImageIoCapability) obj;
            if (!Objects.equals(this.value, other.value)) {
                return false;
            }
            if (this.feature != other.feature) {
                return false;
            }
            return true;
        }

        ImageIoCapability feature(final Feature feature) {
            this.feature = feature;
            return this;
        }

        ImageIoCapability readable(final boolean readable) {
            this.readable = readable;
            return this;
        }

        ImageIoCapability writable(final boolean writable) {
            this.writable = writable;
            return this;
        }

        ImageIoCapability value(final String value) {
            this.value = value;
            return this;
        }

        @XmlAttribute
        private Feature feature;

        @XmlAttribute
        private boolean readable;

        @XmlAttribute
        private boolean writable;

        @XmlValue
        private String value;
    }

    @GET
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Response read(
            @MatrixParam("feature") final List<Feature> features) {
        final Set<ImageIoCapability> capabilities = new HashSet<>();
        Arrays.stream(Feature.values()).forEach(f -> f.probe(capabilities));
        return Response.ok(
                new GenericEntity<List<ImageIoCapability>>(
                        capabilities.stream()
                        .filter(c -> features.isEmpty()
                                     || features.contains(c.feature))
                        .sorted(comparing((ImageIoCapability c) -> c.feature)
                                .thenComparing(c -> c.value))
                        .collect(toList())) {
        }).build();
    }
}

Producing ImageIO capabilities through JAX-RS


@Path("/miscellaneous/imageIoCapabilities")
public class ImageIoCapabilitiesResource {

    @XmlEnum
    public static enum Feature {
        FileSuffix, FormatName, MIMEType
    }

    @XmlAccessorType(XmlAccessType.NONE)
    @XmlRootElement
    public static class ImageIoCapability {

        ImageIoCapability feature(final Feature feature) {
            this.feature = feature;
            return this;
        }

        ImageIoCapability readable(final boolean readable) {
            this.readable = readable;
            return this;
        }

        ImageIoCapability writable(final boolean writable) {
            this.writable = writable;
            return this;
        }

        ImageIoCapability value(final String value) {
            this.value = value;
            return this;
        }

        @XmlAttribute
        private Feature feature;

        @XmlAttribute
        private boolean readable;

        @XmlAttribute
        private boolean writable;

        @XmlValue
        private String value;
    }

    @GET
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Response read(@MatrixParam("feature") final Feature feature) {
        final Map<String, ImageIoCapability> map = new HashMap<>();
        stream(getReaderFileSuffixes()).forEach(
                value -> map.put(
                        Feature.FileSuffix.name() + value,
                        new ImageIoCapability().feature(Feature.FileSuffix)
                        .readable(true).value(value)));
        stream(getWriterFileSuffixes()).forEach(
                value -> ofNullable(map.putIfAbsent(
                        Feature.FileSuffix.name() + value,
                        new ImageIoCapability().feature(Feature.FileSuffix).
                        writable(true).value(value)))
                .ifPresent(previous -> previous.writable(true)));
        stream(getReaderFormatNames()).forEach(
                value -> map.put(
                        Feature.FormatName + value,
                        new ImageIoCapability().feature(Feature.FormatName)
                        .readable(true).value(value)));
        stream(getWriterFormatNames()).forEach(
                value -> ofNullable(map.putIfAbsent(
                        Feature.FormatName + value, new ImageIoCapability()
                        .feature(Feature.FormatName).writable(true)
                        .value(value)))
                .ifPresent(previous -> previous.writable(true)));
        stream(getReaderMIMETypes()).forEach(
                value -> map.putIfAbsent(
                        Feature.MIMEType + value, new ImageIoCapability()
                        .feature(Feature.MIMEType).readable(true)
                        .value(value)));
        stream(getWriterMIMETypes()).forEach(
                value -> ofNullable(map.putIfAbsent(
                        Feature.MIMEType + value,
                        new ImageIoCapability().feature(Feature.MIMEType)
                        .writable(true).value(value)))
                .ifPresent(previous -> previous.writable(true)));
        return Response.ok(
                new GenericEntity<List<ImageIoCapability>>(
                        map.values().stream()
                        .filter(c -> ofNullable(feature)
                                .map(f -> f.equals(c.feature)).orElse(true))
                        .sorted(comparing((ImageIoCapability c) -> c.feature)
                                .thenComparing(c -> c.value))
                        .collect(toList())) {
        }).build();
    }
}

$ http --json GET http://.../miscellaneous/imageIoCapabilities | python -m json.tool
[
    {
        "feature": "FileSuffix",
        "readable": true,
        "value": "wbmp",
        "writable": true
    },
    {
        "feature": "FileSuffix",
        "readable": true,
        "value": "jpeg",
        "writable": true
    },
    {
        "feature": "FileSuffix",
        "readable": true,
        "value": "bmp",
        "writable": true
    },
    {
        "feature": "FileSuffix",
        "readable": true,
        "value": "png",
        "writable": true
    },
    {
        "feature": "FileSuffix",
        "readable": true,
        "value": "jpg",
        "writable": true
    },
    {
        "feature": "FileSuffix",
        "readable": true,
        "value": "gif",
        "writable": true
    },
    {
        "feature": "FormatName",
        "readable": true,
        "value": "WBMP",
        "writable": true
    },
    {
        "feature": "FormatName",
        "readable": true,
        "value": "bmp",
        "writable": true
    },
    {
        "feature": "FormatName",
        "readable": true,
        "value": "GIF",
        "writable": true
    },
    {
        "feature": "FormatName",
        "readable": true,
        "value": "jpeg",
        "writable": true
    },
    {
        "feature": "FormatName",
        "readable": true,
        "value": "BMP",
        "writable": true
    },
    {
        "feature": "FormatName",
        "readable": true,
        "value": "gif",
        "writable": true
    },
    {
        "feature": "FormatName",
        "readable": true,
        "value": "png",
        "writable": true
    },
    {
        "feature": "FormatName",
        "readable": true,
        "value": "PNG",
        "writable": true
    },
    {
        "feature": "FormatName",
        "readable": true,
        "value": "jpg",
        "writable": true
    },
    {
        "feature": "FormatName",
        "readable": true,
        "value": "wbmp",
        "writable": true
    },
    {
        "feature": "FormatName",
        "readable": true,
        "value": "JPG",
        "writable": true
    },
    {
        "feature": "FormatName",
        "readable": true,
        "value": "JPEG",
        "writable": true
    },
    {
        "feature": "MIMEType",
        "readable": true,
        "value": "image/x-png",
        "writable": true
    },
    {
        "feature": "MIMEType",
        "readable": true,
        "value": "image/vnd.wap.wbmp",
        "writable": true
    },
    {
        "feature": "MIMEType",
        "readable": true,
        "value": "image/gif",
        "writable": true
    },
    {
        "feature": "MIMEType",
        "readable": true,
        "value": "image/bmp",
        "writable": true
    },
    {
        "feature": "MIMEType",
        "readable": true,
        "value": "image/png",
        "writable": true
    },
    {
        "feature": "MIMEType",
        "readable": true,
        "value": "image/jpeg",
        "writable": true
    }
]

$ http --json GET "http://.../miscellaneous/imageIoCapabilities;feature=MIMEType" | python -m json.tool
[
    {
        "feature": "MIMEType",
        "readable": true,
        "value": "image/x-png",
        "writable": true
    },
    {
        "feature": "MIMEType",
        "readable": true,
        "value": "image/vnd.wap.wbmp",
        "writable": true
    },
    {
        "feature": "MIMEType",
        "readable": true,
        "value": "image/gif",
        "writable": true
    },
    {
        "feature": "MIMEType",
        "readable": true,
        "value": "image/bmp",
        "writable": true
    },
    {
        "feature": "MIMEType",
        "readable": true,
        "value": "image/png",
        "writable": true
    },
    {
        "feature": "MIMEType",
        "readable": true,
        "value": "image/jpeg",
        "writable": true
    }
]

injecting sub-resource directly


다른 방법도 많지만 sub-resource를 직접 주입할 수도 있나보다.

@Path("/parents");
public class ParentsResource {

    @GET
    @Path("/{parentId: \\d+}/children")
    public ChildrenResource readChidren(
        @PathParam("parentId") final long parentId) {
        return childrenResource.parentId(parentId);
    }

    @Inject
    private ChildResource childResource;
}

an idea of general service for the persistence context


@Stateless
public class PersistenceContextService {

    @PersistenceContext
    private EntityManager entityManager;

    public void accept(final Consumer<EntityManager> consumer) {
        consumer.accept(entityManager);
    }

    public void accept(final BiConsumer<EntityManager, U> consumer
                       final U u) {
        consumer.accept(entityManager, u);
    }

    public <R> R apply(final Function<EnityManager, R> function) {
        return function.apply(entityManager);
    }

    public <U, R> R appply(final BiFunction<EntityManager, U, R> function
                           final U u) {
        return function.apply(entityManager, u);
    }
}

And now we can do this.

@Path("/people")
public class PeopleResource {

    @Inject
    private PersistenceContextService service;

    @POST
    @Consumes({MediaType.APPLICATION_JSON})
    public Response create(final Person person) {
        service.accept(em -> em.persist(person));
        return Response.created(URI.create(person.getId()).build();
    }

    @GET
    @Path("/{id: \\d}")
    @Produces({MediaType.APPLICATION_JSON})
    public Person read(final long id) {
        return service.apply(em -> em.find(Person.class, id));
    }
}