it.cnr.icar.eric.server.cms.CMSManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for it.cnr.icar.eric.server.cms.CMSManagerImpl.java

Source

/*
 * ====================================================================
 * This file is part of the ebXML Registry by Icar Cnr v3.2 
 * ("eRICv32" in the following disclaimer).
 *
 * "eRICv32" is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * "eRICv32" is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License Version 3
 * along with "eRICv32".  If not, see <http://www.gnu.org/licenses/>.
 *
 * eRICv32 is a forked, derivative work, based on:
 *    - freebXML Registry, a royalty-free, open source implementation of the ebXML Registry standard,
 *      which was published under the "freebxml License, Version 1.1";
 *   - ebXML OMAR v3.2 Edition, published under the GNU GPL v3 by S. Krushe & P. Arwanitis.
 * 
 * All derivative software changes and additions are made under
 *
 * Copyright (C) 2013 Ing. Antonio Messina <messina@pa.icar.cnr.it>
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the freebxml Software Foundation.  For more
 * information on the freebxml Software Foundation, please see
 * "http://www.freebxml.org/".
 *
 * This product includes software developed by the Apache Software
 * Foundation (http://www.apache.org/).
 *
 * ====================================================================
 */
package it.cnr.icar.eric.server.cms;

import it.cnr.icar.eric.common.BindingUtility;
import it.cnr.icar.eric.common.RepositoryItem;
import it.cnr.icar.eric.common.exceptions.InvalidConfigurationException;
import it.cnr.icar.eric.server.common.RegistryProperties;
import it.cnr.icar.eric.server.common.ServerRequestContext;
import it.cnr.icar.eric.server.util.ServerResourceBundle;

import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.xml.registry.JAXRException;
import javax.xml.registry.RegistryException;
import org.oasis.ebxml.registry.bindings.lcm.SubmitObjectsRequest;
import org.oasis.ebxml.registry.bindings.lcm.UpdateObjectsRequest;
import org.oasis.ebxml.registry.bindings.query.AdhocQueryRequest;
import org.oasis.ebxml.registry.bindings.query.ResponseOptionType;
import org.oasis.ebxml.registry.bindings.query.ResponseOptionType.ReturnType;
import org.oasis.ebxml.registry.bindings.rim.AssociationType1;
import org.oasis.ebxml.registry.bindings.rim.ClassificationNodeType;
import org.oasis.ebxml.registry.bindings.rim.ClassificationType;
import org.oasis.ebxml.registry.bindings.rim.ExtrinsicObjectType;
import org.oasis.ebxml.registry.bindings.rim.IdentifiableType;
import org.oasis.ebxml.registry.bindings.rim.RegistryObjectType;
import org.oasis.ebxml.registry.bindings.rim.ServiceType;
import org.oasis.ebxml.registry.bindings.rs.RegistryRequestType;

/**
 * Concrete Content Management Service implementation.  For each
 * registry object in a <code>RequestContext</code>, the
 * <code>CMSManager</code> determines the applicable content
 * management service or services to use and invokes them.
 */
public class CMSManagerImpl extends AbstractCMSManager {
    private static final Log log = LogFactory.getLog(CMSManagerImpl.class);
    @SuppressWarnings("rawtypes")
    private HashMap objectTypeToServicesMap = new HashMap();
    @SuppressWarnings("rawtypes")
    private HashMap<String, Collection> objectTypeToServiceInvocationInfosMap = new HashMap<String, Collection>();
    @SuppressWarnings("rawtypes")
    protected Map<String, Constructor> constructors = new TreeMap<String, Constructor>();
    @SuppressWarnings("rawtypes")
    protected Comparator serviceInvocationInfoComparator = new ServiceInvocationInfoComparator();
    public static final String CLASS_MAPPER_PREFIX = "eric.server.cms.classMap.";
    CMSTypeManager filteringManager = new ContentFilteringServiceManager();
    CMSTypeManager catalogingManager = new ContentCatalogingServiceManager();
    CMSTypeManager validationManager = new ContentValidationServiceManager();

    public CMSManagerImpl() {
        // get mapped constructors. log.warn on error and ignore.
        RegistryProperties props = RegistryProperties.getInstance();
        Iterator<String> propsIter = props.getPropertyNamesStartingWith(CLASS_MAPPER_PREFIX);

        while (propsIter.hasNext()) {
            String prop = propsIter.next();
            addConstructor(prop.substring(CLASS_MAPPER_PREFIX.length()), props.getProperty(prop));
        }
    }

    /** Adds a constructor to map */
    private void addConstructor(String typeName, String className) {
        try {
            Class<?> clazz = Class.forName(className);
            Constructor<?> c1 = clazz.getConstructor(new Class[] {});
            constructors.put(typeName, c1);

            if (log.isDebugEnabled()) {
                log.debug(ServerResourceBundle.getInstance().getString(
                        "message.RegisteredConstructorForServiceTypeOrInstanceForType",
                        new Object[] { className, typeName }));
            }
        } catch (Exception e) {
            log.warn(ServerResourceBundle.getInstance().getString("message.extension.load.failure",
                    new Object[] { className, typeName }), e);
        }
    }

    /**
     * Gets the objects that the CM Service must process.
     * 
     * TODO: Not OO as current CMS frame does not allow 
     * any better because objectToProcess are needed before
     * we know which CMSTypeManager to use.
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected Set<Object> getObjectsToProcess(ServerRequestContext context) throws RegistryException {
        Set objectsToProcess = null;

        if (context.getRegistryRequestStack().size() == 0) {
            objectsToProcess = new HashSet<Object>();
        } else {
            RegistryRequestType request = context.getCurrentRegistryRequest();

            if (request instanceof AdhocQueryRequest) {
                objectsToProcess = (new HashSet((context).getQueryResults()));
            } else if ((request instanceof SubmitObjectsRequest) || (request instanceof UpdateObjectsRequest)) {
                objectsToProcess = (new HashSet((context).getTopLevelRegistryObjectTypeMap().values()));
            }
        }

        return objectsToProcess;
    }

    /**
     * Invokes any applicable content management services for the
     * registry objects in the <code>RequestContext</code>.
     *
     * @param context a <code>RequestContext</code> value
     * @param objectsToProcess the objects that need to be processed by CM Services
     */
    public void invokeServices(ServerRequestContext context) throws RegistryException {
        //try {
        // Iterate over objectsToProcess and process them via CMS services
        Iterator<Object> iter = getObjectsToProcess(context).iterator();

        while (iter.hasNext()) {
            Object obj = iter.next();

            //Careful: obj could be an ObjectRefType for queries.
            if (obj instanceof RegistryObjectType) {
                RegistryObjectType ebRegistryObjectType = (RegistryObjectType) obj;

                if (log.isDebugEnabled()) {
                    log.debug(
                            "RegistryObject: " + ebRegistryObjectType.getId() + " (" + ebRegistryObjectType + ")");
                }

                // Get information about any services that should be
                // invoked for objects of this type.
                String objectType = ebRegistryObjectType.getObjectType();
                if (objectType == null) {
                    try {
                        objectType = BindingUtility.getInstance().getObjectType(ebRegistryObjectType);
                    } catch (Exception e) {
                        //Unlikely to happen
                        log.error(e);
                    }
                }

                if (objectType != null) {
                    Collection<ServiceInvocationInfo> serviceInvocationInfos = getServiceInvocationInfos(context,
                            objectType);

                    // If there are any services to be invoked, invoke
                    // each in turn.
                    Iterator<ServiceInvocationInfo> invocationsIter = serviceInvocationInfos.iterator();

                    while (invocationsIter.hasNext()) {
                        ServiceInvocationInfo invocationInfo = invocationsIter.next();
                        RepositoryItem ri = (RepositoryItem) context.getRepositoryItemsMap()
                                .get(ebRegistryObjectType.getId());

                        //Note that ri will be null for ExternalLink ro.
                        System.err.println("Invoking CMS service: " + invocationInfo.getService().getId()
                                + "  for object: " + ebRegistryObjectType.getId());
                        invocationInfo.getManager().invokeServiceForObject(invocationInfo, ebRegistryObjectType, ri,
                                context);
                    }
                }
            }
        }
        //} catch (Exception e) {
        //    e.printStackTrace();
        //}
    }

    /**
     * Gets the constructor to use for handling a Content Management
     * Service.
     * <p>
     * Selection is based on one or more of:
     * <ul>
     * <li>
     * The service's UUID -- e.g., for the built-in Canonical XML
     * Content Cataloging Service.
     * </li>
     * <li>
     * The service's service binding's accessURI value
     * </li>
     * <li>
     * The service's classification as either a validation or content
     * cataloging service.
     * </li>
     * </ul>
     *
     * @param cms a <code>ServiceType</code> value
     * @return a <code>Constructor</code> value
     */
    private Constructor<?> getConstructor(ServiceType cms) {
        Object constructor = null;
        String reason = null;

        constructor = constructors.get(cms.getId());
        reason = "Service UUID";

        if (constructor == null) {
            List<ClassificationType> classifications = cms.getClassification();

            Iterator<ClassificationType> classificationsIter = classifications.iterator();

            while (classificationsIter.hasNext()) {
                ClassificationType classification = classificationsIter.next();

                constructor = constructors.get(classification.getClassificationNode());

                if (constructor != null) {
                    reason = "Service classification";

                    break;
                }
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("getConstructor:: cms: " + cms.getId() + "; constructor: " + constructor + "; reason: "
                    + reason);
        }

        return (Constructor<?>) constructor;
    }

    /**
     * Get information about any content management services for an
     * object type.
     *
     * @param objectType a <code>String</code> value
     * @return a <code>Collection</code> value
     * @exception InvalidConfigurationException if an error occurs
     */
    private Collection<ServiceInvocationInfo> getServiceInvocationInfos(ServerRequestContext context,
            String objectType) throws InvalidConfigurationException, RegistryException {
        // TODO: Remove or update map entry if new Service or
        // InvocationControlFile added for objectType.
        @SuppressWarnings("unchecked")
        Collection<ServiceInvocationInfo> serviceInvocationInfos = objectTypeToServiceInvocationInfosMap
                .get(objectType);

        //TODO: Don't do lookup if objectType has invalid configuration
        if ((serviceInvocationInfos == null)
                && (!(objectTypeToServiceInvocationInfosMap.containsKey(objectType)))) {
            //Lookup failed in map so look dynamically
            serviceInvocationInfos = new ArrayList<ServiceInvocationInfo>(getServiceInvocationInfos(context,
                    objectType, BindingUtility.CANONICAL_ASSOCIATION_TYPE_ID_ValidationControlFileFor,
                    BindingUtility.CANONICAL_CONTENT_MANAGEMENT_SERVICE_ID_ContentValidationService,
                    validationManager));

            serviceInvocationInfos.addAll(getServiceInvocationInfos(context, objectType,
                    BindingUtility.CANONICAL_ASSOCIATION_TYPE_ID_CatalogingControlFileFor,
                    BindingUtility.CANONICAL_CONTENT_MANAGEMENT_SERVICE_ID_ContentCatalogingService,
                    catalogingManager));

            serviceInvocationInfos.addAll(getServiceInvocationInfos(context, objectType,
                    BindingUtility.CANONICAL_ASSOCIATION_TYPE_ID_FilteringControlFileFor,
                    BindingUtility.CANONICAL_CONTENT_MANAGEMENT_SERVICE_ID_ContentFilteringService,
                    filteringManager));

            ((ArrayList<ServiceInvocationInfo>) serviceInvocationInfos).trimToSize();

            //Update the map to avoid dynamic resolution in future for this objectType
            objectTypeToServiceInvocationInfosMap.put(objectType, serviceInvocationInfos);
        }

        return serviceInvocationInfos;
    }

    /**
     * Get the <code>ServiceInvocationInfo</code>s of the specified
     * object type.
     *
     * @param objectType a <code>String</code> value
     * @param assocId a <code>String</code> value
     * @param cmsClassificationId a <code>String</code> value
     * @param manager a <code>CMSTypeManager</code> value
     * @return a <code>Collection</code> value
     */
    private Collection<ServiceInvocationInfo> getServiceInvocationInfos(ServerRequestContext context,
            String objectType, String assocId, String cmsClassificationId, CMSTypeManager cmsTypeManager)
            throws InvalidConfigurationException, RegistryException {
        if (log.isDebugEnabled()) {
            log.debug("getServiceInfos:: " + "objectType: " + objectType + "; assocId: " + assocId
                    + "; cmsClassificationId: " + cmsClassificationId + "; cmsTypeManager: " + cmsTypeManager);
        }

        // Order of invoking services is not defined by ebRS spec, but
        // this implementation should at least be consistent with
        // itself.
        @SuppressWarnings("unchecked")
        Collection<ServiceInvocationInfo> serviceInvocationInfos = new TreeSet<ServiceInvocationInfo>(
                serviceInvocationInfoComparator);

        Collection<InvocationController> invocationControllers = getInvocationControllers(context, objectType,
                assocId);

        if (invocationControllers.size() != 0) {
            ServiceType service = getServiceType(context, objectType, cmsClassificationId);

            if (service == null) {
                throw new InvalidConfigurationException(ServerResourceBundle.getInstance()
                        .getString("message.InvocationFileButNoService", new Object[] { objectType }));
            }

            Constructor<?> serviceConstructor = getConstructor(service);

            if (serviceConstructor == null) {
                throw new InvalidConfigurationException(ServerResourceBundle.getInstance()
                        .getString("message.NoManagerImplementedForObjectType", new Object[] { objectType }));
            }

            Iterator<InvocationController> controllersIter = invocationControllers.iterator();

            while (controllersIter.hasNext()) {
                serviceInvocationInfos.add(new ServiceInvocationInfo(service, serviceConstructor,
                        controllersIter.next(), cmsTypeManager));
            }
        }

        return serviceInvocationInfos;
    }

    /**
     * Gets the <code>InvocationContoller</code>s of a specified
     * association type for a specified registry object type.  The
     * association type should be a subtype of the
     * 'InvocationControlFileFor' association type.
     *
     * @param objectType a <code>String</code> value
     * @param controlFileForAssocId Id of an InvocationControlFileFor association subtype
     * @return a <code>Collection</code> of <code>InvocationController</code>.
     * @exception InvalidConfigurationException if an error occurs
     */
    private Collection<InvocationController> getInvocationControllers(ServerRequestContext context,
            String objectType, String controlFileForAssocId)
            throws InvalidConfigurationException, RegistryException {
        if (log.isTraceEnabled()) {
            log.trace("getInvocationControllers:: " + "objectType: " + objectType + "; controlFileForAssocId: "
                    + controlFileForAssocId);
        }

        Collection<InvocationController> invocationControllers = new ArrayList<InvocationController>();

        // Get associations of object type.
        Collection<?> associations = getAssociations(context, objectType, controlFileForAssocId);

        // Iterate over the associations
        Iterator<?> associationsIter = associations.iterator();

        while (associationsIter.hasNext()) {
            AssociationType1 assoc = (AssociationType1) associationsIter.next();

            RegistryObjectType invocationControlFileEO = null;

            if (assoc != null) {
                String src = assoc.getSourceObject();

                try {
                    invocationControlFileEO = pm.getRegistryObject(context, bu.getObjectId(src), "ExtrinsicObject");
                } catch (Exception e) {
                    throw new InvalidConfigurationException(ServerResourceBundle.getInstance().getString(
                            "message.SourceObjectNotFoundForExtrinsicObject", new Object[] { assoc.getId() }));
                }
            } else {
                throw new InvalidConfigurationException(
                        ServerResourceBundle.getInstance().getString("message.InvocationControlFileIsNull"));
            }

            // Check that source object is an ExtrinsicObject.
            if (!(invocationControlFileEO instanceof ExtrinsicObjectType)) {
                throw new InvalidConfigurationException(ServerResourceBundle.getInstance()
                        .getString("message.InvocationControlFileIsNotPresentAsAnExtrinsicObject"));
            }

            if (log.isDebugEnabled()) {
                log.debug("InvocationControlFile: " + invocationControlFileEO.getId());
            }

            InvocationController ic = new InvocationController(controlFileForAssocId,
                    invocationControlFileEO.getId());
            invocationControllers.add(ic);
        }

        return invocationControllers;
    }

    /**
     * Gets the single service of the specified classification for the
     * registry object type.
     *
     * @param objectType a <code>String</code> value
     * @param cmsClassificationId a <code>String</code> value
     * @return a <code>ServiceType</code> value
     * @exception InvalidConfigurationException if an error occurs
     * @exception RegistryException if an error occurs
     */
    private ServiceType getServiceType(ServerRequestContext context, String objectType, String cmsClassificationId)
            throws InvalidConfigurationException, RegistryException {
        ServiceType serviceType = null;

        // TODO: Remove or update map entry if new Service added for
        // (any) objectType.
        Collection<ServiceType> serviceTypes = getServiceTypes(context, objectType);

        Iterator<ServiceType> serviceTypesIter = serviceTypes.iterator();

        while (serviceTypesIter.hasNext()) {
            ServiceType candidateServiceType = serviceTypesIter.next();

            List<ClassificationType> candidateClassifications = candidateServiceType.getClassification();

            Iterator<ClassificationType> classificationsIter = candidateClassifications.iterator();

            while (classificationsIter.hasNext()) {
                ClassificationType classification = classificationsIter.next();

                if (log.isDebugEnabled()) {
                    log.debug("CandidateServiceType: " + candidateServiceType.getId() + "; classificationNode: "
                            + classification.getClassificationNode());
                }

                if (classification.getClassificationNode().equals(cmsClassificationId)) {
                    serviceType = candidateServiceType;

                    // FIXME: need to break out of two loops.
                    break;
                }
            }
        }

        return serviceType;
    }

    @SuppressWarnings("unchecked")
    private Collection<ServiceType> getServiceTypes(ServerRequestContext context, String objectType)
            throws InvalidConfigurationException, RegistryException {
        Collection<ServiceType> serviceTypes;

        // TODO: Remove or update map entry if new Service added for
        // (any) objectType.
        serviceTypes = (Collection<ServiceType>) objectTypeToServicesMap.get(objectType);

        //TODO: Don't do lookup if objectType has invalid configuration
        if ((serviceTypes == null) && (!(objectTypeToServicesMap.containsKey(objectType)))) {
            //Lookup failed in map so look dynamically
            serviceTypes = new ArrayList<ServiceType>();

            // Get "ContentManagementServiceFor" associations of
            // each object
            Collection<?> serviceAssocs = getAssociations(context, objectType,
                    BindingUtility.CANONICAL_ASSOCIATION_TYPE_ID_ContentManagementServiceFor);

            // Iterate over the services
            Iterator<?> serviceAssocsIter = serviceAssocs.iterator();

            while (serviceAssocsIter.hasNext()) {
                AssociationType1 assoc = (AssociationType1) serviceAssocsIter.next();

                if (assoc != null) {
                    Object src = assoc.getSourceObject();

                    ServiceType serviceType = null;

                    try {
                        serviceType = (ServiceType) pm.getRegistryObject(context, bu.getObjectId(src), "Service");
                    } catch (Exception e) {
                        throw new InvalidConfigurationException(
                                ServerResourceBundle.getInstance().getString(
                                        "message.SourceObjectNotFoundForService", new Object[] { assoc.getId() }),
                                e);
                    }

                    System.err.println("Service: " + serviceType.getId());

                    serviceTypes.add(serviceType);
                }
            }

            //Update the map to avoid dynamic resolution in future for this service type
            objectTypeToServicesMap.put(objectType, (Object) serviceTypes);
        }

        return serviceTypes;
    }

    /**
     *
     * Gets the Associations that have the specified assocType and have
     * as targetObject the ClassificationNode pointed to by objectType
     * or an ancestor of the ClassificationNode pointed to by
     * objectType.
     *
     */
    private List<IdentifiableType> getAssociations(ServerRequestContext context, String objectType,
            String assocType) throws RegistryException {
        List<IdentifiableType> associations = new ArrayList<IdentifiableType>();

        try {
            RegistryObjectType ro = pm.getRegistryObject(context, objectType, "ClassificationNode");
            ResponseOptionType ebResponseOptionType = bu.queryFac.createResponseOptionType();
            ebResponseOptionType.setReturnType(ReturnType.LEAF_CLASS);
            ebResponseOptionType.setReturnComposedObjects(false);

            ArrayList<Object> objectRefs = new ArrayList<Object>();

            if ((ro != null) && (ro instanceof ClassificationNodeType)) {
                ClassificationNodeType node = (ClassificationNodeType) ro;

                String query = "SELECT ass.* from Association ass " + "  WHERE ass.targetObject = '" + node.getId()
                        + "' AND ass.associationType = '" + assocType + "' ";

                objectRefs.clear();

                associations = pm.executeSQLQuery(context, query, ebResponseOptionType, "Association", objectRefs);

                //TODO: Fix following design bug...
                //If there is a CatalogingService defined at
                //sub-class of XML (e.g. CPP) level then it will
                //not look at XML level even for a different type
                //of service such as FilteringService.
                //Base class lookup should be done if no service
                //is found for a specific type of CMS service.
                if (associations.size() == 0) {
                    //Check if an Association exists for the parent node
                    Object parent = node.getParent();

                    if ((parent != null)) {
                        String parentId = bu.getObjectId(parent);
                        associations = getAssociations(context, parentId, assocType);
                    }
                }
            }
        } catch (JAXRException e) {
            throw new RegistryException(e);
        }

        return associations;
    }

    /**
     * Gets the <code>ExtrinsicObjectType</code> as a stream of XML markup.
     *
     * @param eo an <code>ExtrinsicObjectType</code> value
     * @return a <code>StreamSource</code> value
     * @exception RegistryException if an error occurs
     */
    protected static StreamSource getAsStreamSource(ExtrinsicObjectType eo) throws RegistryException {
        log.trace("getAsStreamSource(ExtrinsicObjectType ) entered");

        StreamSource src = null;

        try {
            StringWriter sw = new StringWriter();

            Marshaller marshaller = bu.getJAXBContext().createMarshaller();

            marshaller.marshal(eo, sw);

            StringReader reader = new StringReader(sw.toString());
            src = new StreamSource(reader);
        }
        // these Exceptions should already be caught by Binding
        catch (JAXBException e) {
            throw new RegistryException(e);
        }

        return src;
    }
}