org.nuxeo.ecm.core.io.registry.MarshallerRegistryImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.ecm.core.io.registry.MarshallerRegistryImpl.java

Source

/*
 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 *     Nicolas Chapurlat <nchapurlat@nuxeo.com>
 */

package org.nuxeo.ecm.core.io.registry;

import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;

import javax.ws.rs.core.MediaType;

import org.apache.commons.lang3.reflect.TypeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.io.registry.context.RenderingContext;
import org.nuxeo.ecm.core.io.registry.reflect.MarshallerInspector;
import org.nuxeo.runtime.model.ComponentContext;
import org.nuxeo.runtime.model.ComponentInstance;
import org.nuxeo.runtime.model.DefaultComponent;

/**
 * Implementation of {@link MarshallerRegistry}.
 * <p>
 * This implementation is based on {@link MarshallerInspector} class which is able to create marshaller instance and
 * inject properties. This class also manage marshaller's priorities.
 * </p>
 *
 * @since 7.2
 */
public class MarshallerRegistryImpl extends DefaultComponent implements MarshallerRegistry {

    private final Log log = LogFactory.getLog(MarshallerRegistryImpl.class);

    /**
     * All {@link Writer}'s {@link MarshallerInspector} ordered by their priority.
     */
    private static final Set<MarshallerInspector> writers = new ConcurrentSkipListSet<MarshallerInspector>();

    /**
     * {@link Writer}'s {@link MarshallerInspector} organized by their managed {@link MediaType}.
     */
    private static final Map<MediaType, Set<MarshallerInspector>> writersByMediaType = new ConcurrentHashMap<MediaType, Set<MarshallerInspector>>();

    /**
     * All {@link Reader}'s {@link MarshallerInspector} ordered by their priority.
     */
    private static final Set<MarshallerInspector> readers = new ConcurrentSkipListSet<MarshallerInspector>();

    /**
     * {@link Reader}'s {@link MarshallerInspector} organized by their managed {@link MediaType}.
     */
    private static final Map<MediaType, Set<MarshallerInspector>> readersByMediaType = new ConcurrentHashMap<MediaType, Set<MarshallerInspector>>();

    /**
     * {@link MarshallerInspector} organized by their managed {@link Marshaller} class.
     */
    private static final Map<Class<?>, MarshallerInspector> marshallersByType = new ConcurrentHashMap<Class<?>, MarshallerInspector>();

    @Override
    public void activate(ComponentContext context) {
        super.activate(context);
        clear();
    }

    @Override
    public void deactivate(ComponentContext context) {
        clear();
        super.deactivate(context);
    }

    @Override
    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
        if (extensionPoint.equals("marshallers")) {
            MarshallerRegistryDescriptor mrd = (MarshallerRegistryDescriptor) contribution;
            if (mrd.isEnable()) {
                register(mrd.getClazz());
            } else {
                deregister(mrd.getClazz());
            }
        }
    }

    @Override
    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
        if (extensionPoint.equals("marshallers")) {
            MarshallerRegistryDescriptor mrd = (MarshallerRegistryDescriptor) contribution;
            if (mrd.isEnable()) {
                deregister(mrd.getClazz());
            } else {
                register(mrd.getClazz());
            }
        }
    }

    @Override
    public void register(Class<?> marshaller) {
        if (marshaller == null) {
            throw new MarshallingException("Cannot register null marshaller");
        }
        MarshallerInspector inspector = new MarshallerInspector(marshaller);
        if (!inspector.isWriter() && !inspector.isReader()) {
            throw new MarshallingException(
                    "The marshaller registry just supports Writer and Reader for now. You have to implement "
                            + Writer.class.getName() + " or " + Reader.class.getName());
        }
        if (marshallersByType.get(marshaller) != null) {
            log.warn("The marshaller " + marshaller.getName() + " is already registered.");
            return;
        } else {
            marshallersByType.put(marshaller, inspector);
        }
        if (inspector.isWriter()) {
            writers.add(inspector);
            for (MediaType mediaType : inspector.getSupports()) {
                Set<MarshallerInspector> inspectors = writersByMediaType.get(mediaType);
                if (inspectors == null) {
                    inspectors = new ConcurrentSkipListSet<MarshallerInspector>();
                    writersByMediaType.put(mediaType, inspectors);
                }
                inspectors.add(inspector);
            }
        }
        if (inspector.isReader()) {
            readers.add(inspector);
            for (MediaType mediaType : inspector.getSupports()) {
                Set<MarshallerInspector> inspectors = readersByMediaType.get(mediaType);
                if (inspectors == null) {
                    inspectors = new ConcurrentSkipListSet<MarshallerInspector>();
                    readersByMediaType.put(mediaType, inspectors);
                }
                inspectors.add(inspector);
            }
        }
    }

    @Override
    public void deregister(Class<?> marshaller) throws MarshallingException {
        if (marshaller == null) {
            log.warn("Cannot register null marshaller");
            return;
        }
        MarshallerInspector inspector = new MarshallerInspector(marshaller);
        if (!inspector.isWriter() && !inspector.isReader()) {
            throw new MarshallingException(
                    "The marshaller registry just supports Writer and Reader for now. You have to implement "
                            + Writer.class.getName() + " or " + Reader.class.getName());
        }
        marshallersByType.remove(marshaller);
        if (inspector.isWriter()) {
            writers.remove(inspector);
            for (MediaType mediaType : inspector.getSupports()) {
                Set<MarshallerInspector> inspectors = writersByMediaType.get(mediaType);
                if (inspectors != null) {
                    inspectors.remove(inspector);
                }
            }
        }
        if (inspector.isReader()) {
            readers.remove(inspector);
            for (MediaType mediaType : inspector.getSupports()) {
                Set<MarshallerInspector> inspectors = readersByMediaType.get(mediaType);
                if (inspectors != null) {
                    inspectors.remove(inspector);
                }
            }
        }
    }

    @Override
    public <T> Writer<T> getWriter(RenderingContext ctx, Class<T> marshalledClazz, Type genericType,
            MediaType mediatype) {
        Set<MarshallerInspector> candidates = writersByMediaType.get(mediatype);
        return (Writer<T>) getMarshaller(ctx, marshalledClazz, genericType, mediatype, candidates, writers, false);
    }

    @Override
    public <T> Writer<T> getUniqueWriter(RenderingContext ctx, Class<T> marshalledClazz, Type genericType,
            MediaType mediatype) {
        Set<MarshallerInspector> candidates = writersByMediaType.get(mediatype);
        return (Writer<T>) getMarshaller(ctx, marshalledClazz, genericType, mediatype, candidates, writers, true);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> Collection<Writer<T>> getAllWriters(RenderingContext ctx, Class<T> marshalledClazz, Type genericType,
            MediaType mediatype) {
        Set<MarshallerInspector> candidates = writersByMediaType.get(mediatype);
        Collection<Marshaller<T>> founds = getAllMarshallers(ctx, marshalledClazz, genericType, mediatype,
                candidates, writers);
        return (Collection<Writer<T>>) (Collection<?>) founds;
    }

    @Override
    public <T> Writer<T> getWriter(RenderingContext ctx, Class<T> marshalledClazz, MediaType mediatype) {
        return getWriter(ctx, marshalledClazz, marshalledClazz, mediatype);
    }

    @Override
    public <T> Reader<T> getReader(RenderingContext ctx, Class<T> marshalledClazz, Type genericType,
            MediaType mediatype) {
        Set<MarshallerInspector> candidates = readersByMediaType.get(mediatype);
        return (Reader<T>) getMarshaller(ctx, marshalledClazz, genericType, mediatype, candidates, readers, false);
    }

    @Override
    public <T> Reader<T> getUniqueReader(RenderingContext ctx, Class<T> marshalledClazz, Type genericType,
            MediaType mediatype) {
        Set<MarshallerInspector> candidates = readersByMediaType.get(mediatype);
        return (Reader<T>) getMarshaller(ctx, marshalledClazz, genericType, mediatype, candidates, readers, true);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> Collection<Reader<T>> getAllReaders(RenderingContext ctx, Class<T> marshalledClazz, Type genericType,
            MediaType mediatype) {
        Set<MarshallerInspector> candidates = readersByMediaType.get(mediatype);
        Collection<Marshaller<T>> founds = getAllMarshallers(ctx, marshalledClazz, genericType, mediatype,
                candidates, readers);
        return (Collection<Reader<T>>) (Collection<?>) founds;
    }

    @Override
    public <T> Reader<T> getReader(RenderingContext ctx, Class<T> marshalledClazz, MediaType mediatype) {
        return getReader(ctx, marshalledClazz, marshalledClazz, mediatype);
    }

    public <T> Marshaller<T> getMarshaller(RenderingContext ctx, Class<T> marshalledClazz, Type genericType,
            MediaType mediatype, Set<MarshallerInspector> customs, Set<MarshallerInspector> wildcards,
            boolean forceInstantiation) {
        if (customs != null) {
            Marshaller<T> found = searchCandidate(ctx, marshalledClazz, genericType, mediatype, customs,
                    forceInstantiation);
            if (found != null) {
                return found;
            }
        }
        return searchCandidate(ctx, marshalledClazz, genericType, mediatype, wildcards, forceInstantiation);
    }

    public <T> Collection<Marshaller<T>> getAllMarshallers(RenderingContext ctx, Class<T> marshalledClazz,
            Type genericType, MediaType mediatype, Set<MarshallerInspector> customs,
            Set<MarshallerInspector> wildcards) {
        Map<MarshallerInspector, Marshaller<T>> result = new HashMap<MarshallerInspector, Marshaller<T>>();
        if (customs != null) {
            result.putAll(searchAllCandidates(ctx, marshalledClazz, genericType, mediatype, customs));
        }
        result.putAll(searchAllCandidates(ctx, marshalledClazz, genericType, mediatype, wildcards));
        return result.values();
    }

    @SuppressWarnings("unchecked")
    private <T> Marshaller<T> searchCandidate(RenderingContext ctx, Class<T> marshalledClazz, Type genericType,
            MediaType mediatype, Set<MarshallerInspector> candidates, boolean forceInstantiation) {
        for (MarshallerInspector inspector : candidates) {
            // checks the managed class is compatible
            if (inspector.getMarshalledType().isAssignableFrom(marshalledClazz)) {
                // checks the generic type is compatible
                if (genericType == null || marshalledClazz.equals(inspector.getGenericType())
                        || TypeUtils.isAssignable(genericType, inspector.getGenericType())) {
                    Marshaller<T> marshaller = null;
                    if (forceInstantiation) {
                        marshaller = (Marshaller<T>) inspector.getNewInstance(ctx, false);
                    } else {
                        marshaller = inspector.getInstance(ctx);
                    }
                    // checks the marshaller accepts the request
                    if (marshaller.accept(marshalledClazz, genericType, mediatype)) {
                        return marshaller;
                    }
                }
            }
        }
        return null;
    }

    private <T> Map<MarshallerInspector, Marshaller<T>> searchAllCandidates(RenderingContext ctx,
            Class<T> marshalledClazz, Type genericType, MediaType mediatype, Set<MarshallerInspector> candidates) {
        Map<MarshallerInspector, Marshaller<T>> result = new HashMap<MarshallerInspector, Marshaller<T>>();
        for (MarshallerInspector inspector : candidates) {
            // checks the managed class is compatible
            if (inspector.getMarshalledType().isAssignableFrom(marshalledClazz)) {
                // checks the generic type is compatible
                if (genericType == null || marshalledClazz.equals(inspector.getGenericType())
                        || TypeUtils.isAssignable(genericType, inspector.getGenericType())) {
                    // checks the marshaller accepts the request
                    Marshaller<T> marshaller = inspector.getInstance(ctx);
                    if (marshaller.accept(marshalledClazz, genericType, mediatype)) {
                        result.put(inspector, marshaller);
                    }
                }
            }
        }
        return result;
    }

    @Override
    public <T> T getInstance(RenderingContext ctx, Class<T> marshallerClass) {
        MarshallerInspector inspector = marshallersByType.get(marshallerClass);
        if (inspector == null) {
            inspector = new MarshallerInspector(marshallerClass);
        }
        return inspector.getInstance(ctx);
    }

    @Override
    public <T> T getUniqueInstance(RenderingContext ctx, Class<T> marshallerClass) {
        MarshallerInspector inspector = marshallersByType.get(marshallerClass);
        if (inspector == null) {
            inspector = new MarshallerInspector(marshallerClass);
        }
        @SuppressWarnings("unchecked")
        T result = (T) inspector.getNewInstance(ctx, false);
        return result;
    }

    @Override
    public void clear() {
        marshallersByType.clear();
        writersByMediaType.clear();
        writers.clear();
        readersByMediaType.clear();
        readers.clear();
    }

}