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}