Progress : Define a marshaller: managed mimytype, class, priority OK Design a marshaller: writing methods, properties, injection OK Create a marshalling context to handle parameters OK Create a marshaller registry to register marshallers, manage their priorities OK Create a loader to manage marshaller's instanciation OK REFACTOR most of existing marshaller Automation-io : ~30% Rest-api-io : ~40% At least 10 days of work to terminate ---------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------- Problems/Questions : - annoying DTO org.nuxeo.ecm.automation.core.util.Paginable depends on org.nuxeo.ecm.platform.query.api.Aggregate org.nuxeo.ecm.automation.core.util.PaginableDocumentModelList org.nuxeo.ecm.automation.core.util.PaginablePageProvider recommendation : 1- move nuxeo-platform-query-api content to nuxeo-core-query 2- move dto to nuxeo-core-io or nuxeo-core-api - statics methods ex: org.nuxeo.ecm.automation.jaxrs.io.documents.JsonDocumentWriter.writeDocument(JsonGenerator, DocumentModel, String[], ServletRequest) - replace and remove - ContentEnricherService Should be replaced by marshaller contributed to registry: @Setup(mode = SINGLETON, priority = REFERENCE) public class ChildrenJsonEnricher extends AbstractJsonEnricher { public static final String NAME = "children"; public ChildrenJsonEnricher() { super(NAME); } @Override public void write(NxJsonGenerator jg, EnrichedDocument enriched) throws IOException { DocumentModel doc = enriched.getEntity(); CoreSession session = doc.getCoreSession(); DocumentModelList children = session.getChildren(doc.getRef()); try (Closeable resource = ctx.wrap().with(DENIED_TRANSITIVE_PROPERTY_LOADING, true).open()) { jg.writeFieldName(NAME); writeEntity(children, jg); } } } /nuxeo-core-io/src/main/resources/OSGI-INF/marshallers-contrib.xml: Core IO registered marshallers set. /nuxeo-platform-spreadsheet/src/main/resources/OSGI-INF/marshallers-contrib.xml: Core IO registered marshallers set. /nuxeo-platform-url-core/src/main/resources/OSGI-INF/marshallers-contrib.xml: Core IO registered marshallers set. ---------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------- - register JSON marshallers through an extension point or programmatically - manage registered marshaller priorities (generic marshallers, reference marshaller, overriding, alternative marshallers) /nuxeo-platform-usermanager-core/src/main/resources/OSGI-INF/marshallers-contrib.xml: Core IO registered marshallers set. org.nuxeo.ecm.platform.usermanager.io.NuxeoPrincipalJsonWriter: @Setup(mode = SINGLETON, priority = REFERENCE) public class NuxeoPrincipalJsonWriter extends AbstractJsonWriter { ... public interface Priorities { int DEFAULT = 0; int DERIVATIVE = 1000; int REFERENCE = 2000; int OVERRIDE_REFERENCE = 3000; } MarshallerRegistry registry = Framework.getService(MarshallerRegistry.class); registry.register(NuxeoPrincipalJsonWriter.class); registry.deregister(NuxeoGroupJsonWriter.class); ---------------------------------------------------------------------------------------------- - get a marshaller for a Java type / mime type RenderingContext ctx = RenderingContext.Builder.get(); // you could add parameters MarshallerRegistry registry = Framework.getService(MarshallerRegistry.class); Writer writer = registry.getWriter(ctx, DocumentModel.class, APPLICATION_JSON_TYPE); OutputStream out = ...; writer.write(document, DocumentModel.class, DocumentModel.class, APPLICATION_JSON_TYPE, out) ---------------------------------------------------------------------------------------------- - explicitly get a specific marshaller RenderingContext ctx = RenderingContext.Builder.get(); MarshallerRegistry registry = Framework.getService(MarshallerRegistry.class); DocumentModelJsonWriter writer = registry.getInstance(DocumentModelJsonWriter.class, ctx); ---------------------------------------------------------------------------------------------- - register alternative marshallers @Setup(mode = SINGLETON, priority = REFERENCE) @Supports({"application/json"}) public class DocumentModelJsonWriter extends AbstractJsonWriter { ... @Setup(mode = SINGLETON, priority = DERIVATIVE) @Supports({"application/json", "application/json+esentity"}) public class DocumentModelEsJsonWriter extends AbstractJsonWriter { ... registry.getWriter(ctx, DocumentModel.class, MediaType.valueof("application/json")); => return DocumentModelJsonWriter registry.getWriter(ctx, DocumentModel.class, MediaType.valueof("application/json+esentity")); => return DocumentModelEsJsonWriter ---------------------------------------------------------------------------------------------- - use our marshallers in JAX-RS (priority for our container) use out marshaller un JAX-RS : OK priority for our container : KO public class APIModule extends WebEngineModule { @Override public Set> getClasses() { ... } @Override public Set getSingletons() { Set result = new LinkedHashSet(); result.add(new FullCoreIODelegate()); // <<=== return result; } } @Provider @Produces(WILDCARD) public abstract class FullCoreIODelegate implements MessageBodyWriter { ... !!!!! Problem : Jersey priorise the marshallers whose marshalled typeis the nearest from the class of the marshalled object / our proxy marshall Object.class ... => Our proxy isn't priority => There's no extension point in jersey/JAX-RS to bypass JAX-RS priority (see com.sun.jersey.core.spi.factory.MessageBodyFactory) ---------------------------------------------------------------------------------------------- - use JAX-RS marshallers in our marshallers (priority for our container) Not available for now Shouldn't be a problem ---------------------------------------------------------------------------------------------- - delegate marshalling to another marshaller in a "parent" marshaller Yes, using service MarshallerRegistry ---------------------------------------------------------------------------------------------- - get current base url http://localhost:8080/nuxeo, http://nuxeo.example.org/ - get the expected locale (from the web browser in web context) - send parameters to marshallers : in a web context, from request, from headers, custom parameters | outside the web context : custom parameters public interface RenderingContext { public static final Locale DEFAULT_LOCALE = Locale.ENGLISH; public static final String DEFAULT_URL = "http://fake-url.nuxeo.com/"; public Locale getLocale(); public String getBaseUrl(); public Set getNXParam(String category); public WrappedContext wrap(); public T getParameter(String name); public boolean getBooleanParameter(String name); public List getParameters(String name); public Map> getAllParameters(); public void setParameters(String name, Object... values); public void setParameters(String name, List values); public void addParameters(String name, Object... values); public void addParameters(String name, List values); } ---------------------------------------------------------------------------------------------- - send entities from a writer to writers, we delegates marshalling parts see WrappedContext Exemple use RenderingContext ctx = ...; try (Closeable resource = ctx.wrap().with(ENTITY_ENRICHER, enricherName).open()) { writer.write(enrichedDocument, EnrichedDocument.class, EnrichedDocument.class, APPLICATION_JSON_TYPE, jg.getOutputStream()); // in the writer : String enricherName = ctx.getParameter(ENTITY_ENRICHER); } ---------------------------------------------------------------------------------------------- - write a huge quantity of entities without creating a huge number of java class to manage the marshalling public enum Instantiations { SINGLETON, PER_THREAD, EACH_TIME; } @Setup(mode = SINGLETON, priority = REFERENCE) public class DocumentModelJsonWriter extends AbstractJsonWriter { ... ---------------------------------------------------------------------------------------------- - easily use Jackson 1 in our marshallers public class NxJsonGenerator { public NxJsonGenerator(OutputStream out) { ... } public void writeEntityTypeField(String type) throws IOException { public void writePagineableFields(Paginable paginable) throws IOException { public void writeStartArray() throws IOException { public void writeEndArray() throws IOException { public void writeStartObject() throws IOException { public void writeEndObject() throws IOException { public void writeFieldName(String name) throws IOException { public void writeString(String text) throws IOException { public void writeRaw(String text) throws IOException { public void writeRawValue(String text) throws IOException { public void writeNumber(int v) throws IOException { public void writeNumber(long v) throws IOException { public void writeNumber(BigInteger v) throws IOException { public void writeNumber(double d) throws IOException { public void writeNumber(float f) throws IOException { public void writeNumber(BigDecimal dec) throws IOException { public void writeBoolean(boolean state) throws IOException { public void writeNull() throws IOException { public void writeObject(Object pojo) throws IOException { ... public void flush() throws IOException { public boolean isClosed() { public void close() throws IOException { } @Supports(APPLICATION_JSON) public abstract class AbstractJsonWriter implements Writer { ... public abstract void write(EntityType entity, Class clazz, Type genericType, MediaType mediatype, NxJsonGenerator jg) throws IOException; ... }