001package co.codewizards.cloudstore.ls.core.provider;
002
003import static co.codewizards.cloudstore.core.util.ReflectionUtil.*;
004import static java.util.Objects.*;
005
006import java.io.IOException;
007import java.io.InputStream;
008import java.io.ObjectInputStream;
009import java.lang.annotation.Annotation;
010import java.lang.reflect.Type;
011import java.util.Map;
012
013import javax.ws.rs.Consumes;
014import javax.ws.rs.WebApplicationException;
015import javax.ws.rs.core.Context;
016import javax.ws.rs.core.MediaType;
017import javax.ws.rs.core.MultivaluedMap;
018import javax.ws.rs.core.SecurityContext;
019import javax.ws.rs.ext.MessageBodyReader;
020import javax.ws.rs.ext.Provider;
021
022import co.codewizards.cloudstore.core.io.NoCloseInputStream;
023import co.codewizards.cloudstore.ls.core.invoke.ForceNonTransientContainer;
024import co.codewizards.cloudstore.ls.core.invoke.ObjectGraphContainer;
025import co.codewizards.cloudstore.ls.core.invoke.ObjectRefConverter;
026import co.codewizards.cloudstore.ls.core.invoke.ObjectRefConverterFactory;
027
028/**
029 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
030 */
031@Provider
032@Consumes(MediaTypeConst.APPLICATION_JAVA_NATIVE_WITH_OBJECT_REF)
033public class JavaNativeWithObjectRefMessageBodyReader
034implements MessageBodyReader<Object>
035{
036        private final ObjectRefConverterFactory objectRefConverterFactory;
037
038        @Context
039        private SecurityContext securityContext;
040
041        public JavaNativeWithObjectRefMessageBodyReader(final ObjectRefConverterFactory objectRefConverterFactory) {
042                this.objectRefConverterFactory = requireNonNull(objectRefConverterFactory, "objectRefConverterFactory");
043        }
044
045        @Override
046        public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
047                // We return always true, because we declared our media-type already in the @Consumes above and thus don't need to check it here.
048                // At least I hope we don't get consulted for media-types that were not declared in @Consumes.
049                return true;
050        }
051
052        @Override
053        public Object readFrom(
054                        final Class<Object> type, final Type genericType,
055                        final Annotation[] annotations, final MediaType mediaType,
056                        final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream
057                        )
058                                        throws IOException, WebApplicationException
059        {
060                final ObjectRefConverter objectRefConverter = objectRefConverterFactory.createObjectRefConverter(securityContext);
061                try (ObjectInputStream oin = new ResolvingObjectInputStream(new NoCloseInputStream(entityStream), objectRefConverter);) {
062                        final Object o = oin.readObject();
063                        final ObjectGraphContainer objectGraphContainer = (ObjectGraphContainer) o;
064
065                        for (ForceNonTransientContainer forceNonTransientContainer : objectGraphContainer.getTransientFieldOwnerObject2ForceNonTransientContainer().values())
066                                restoreTransientFields(forceNonTransientContainer);
067
068                        return objectGraphContainer.getRoot();
069                } catch (ClassNotFoundException e) {
070                        throw new IOException(e);
071                }
072
073        }
074
075        private void restoreTransientFields(final ForceNonTransientContainer container) {
076                final Object ownerObject = container.getTransientFieldOwnerObject();
077
078                for (final Map.Entry<String, Object> me : container.getTransientFieldName2Value().entrySet()) {
079                        final String qualifiedFieldName = me.getKey();
080                        final Object fieldValue = me.getValue();
081                        setFieldValue(ownerObject, qualifiedFieldName, fieldValue);
082                }
083        }
084
085        private static class ResolvingObjectInputStream extends ExtObjectInputStream {
086                private final ObjectRefConverter objectRefConverter;
087
088                public ResolvingObjectInputStream(final InputStream in, final ObjectRefConverter objectRefConverter) throws IOException {
089                        super(in);
090                        this.objectRefConverter = requireNonNull(objectRefConverter, "objectRefConverter");
091                        enableResolveObject(true);
092                }
093
094                @Override
095                protected Object resolveObject(Object object) throws IOException {
096                        final Object result = objectRefConverter.convertFromObjectRefIfNeeded(object);
097                        return result;
098                }
099        }
100}