package co.codewizards.cloudstore.ls.core.provider; import static co.codewizards.cloudstore.core.util.AssertUtil.*; import static co.codewizards.cloudstore.core.util.ReflectionUtil.*; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.Map; import javax.ws.rs.Consumes; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.Provider; import co.codewizards.cloudstore.core.io.NoCloseInputStream; import co.codewizards.cloudstore.ls.core.invoke.ForceNonTransientContainer; import co.codewizards.cloudstore.ls.core.invoke.ObjectGraphContainer; import co.codewizards.cloudstore.ls.core.invoke.ObjectRefConverter; import co.codewizards.cloudstore.ls.core.invoke.ObjectRefConverterFactory; /** * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de */ @Provider @Consumes(MediaTypeConst.APPLICATION_JAVA_NATIVE_WITH_OBJECT_REF) public class JavaNativeWithObjectRefMessageBodyReader implements MessageBodyReader<Object> { private final ObjectRefConverterFactory objectRefConverterFactory; @Context private SecurityContext securityContext; public JavaNativeWithObjectRefMessageBodyReader(final ObjectRefConverterFactory objectRefConverterFactory) { this.objectRefConverterFactory = assertNotNull(objectRefConverterFactory, "objectRefConverterFactory"); } @Override public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { // We return always true, because we declared our media-type already in the @Consumes above and thus don't need to check it here. // At least I hope we don't get consulted for media-types that were not declared in @Consumes. return true; } @Override public Object readFrom( final Class<Object> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType, final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream ) throws IOException, WebApplicationException { final ObjectRefConverter objectRefConverter = objectRefConverterFactory.createObjectRefConverter(securityContext); try (ObjectInputStream oin = new ResolvingObjectInputStream(new NoCloseInputStream(entityStream), objectRefConverter);) { final Object o = oin.readObject(); final ObjectGraphContainer objectGraphContainer = (ObjectGraphContainer) o; for (ForceNonTransientContainer forceNonTransientContainer : objectGraphContainer.getTransientFieldOwnerObject2ForceNonTransientContainer().values()) restoreTransientFields(forceNonTransientContainer); return objectGraphContainer.getRoot(); } catch (ClassNotFoundException e) { throw new IOException(e); } } private void restoreTransientFields(final ForceNonTransientContainer container) { final Object ownerObject = container.getTransientFieldOwnerObject(); for (final Map.Entry<String, Object> me : container.getTransientFieldName2Value().entrySet()) { final String qualifiedFieldName = me.getKey(); final Object fieldValue = me.getValue(); setFieldValue(ownerObject, qualifiedFieldName, fieldValue); } } private static class ResolvingObjectInputStream extends ExtObjectInputStream { private final ObjectRefConverter objectRefConverter; public ResolvingObjectInputStream(final InputStream in, final ObjectRefConverter objectRefConverter) throws IOException { super(in); this.objectRefConverter = assertNotNull(objectRefConverter, "objectRefConverter"); enableResolveObject(true); } @Override protected Object resolveObject(Object object) throws IOException { final Object result = objectRefConverter.convertFromObjectRefIfNeeded(object); return result; } } }