it.cnr.icar.eric.server.query.ReferenceResolverImpl.java Source code

Java tutorial

Introduction

Here is the source code for it.cnr.icar.eric.server.query.ReferenceResolverImpl.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.query;

import it.cnr.icar.eric.client.common.CommonResourceBundle;
import it.cnr.icar.eric.common.BindingUtility;
import it.cnr.icar.eric.common.spi.QueryManagerFactory;
import it.cnr.icar.eric.server.common.RegistryProperties;
import it.cnr.icar.eric.server.common.ServerRequestContext;
import it.cnr.icar.eric.server.persistence.PersistenceManagerFactory;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.registry.JAXRException;
import javax.xml.registry.RegistryException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.oasis.ebxml.registry.bindings.query.ResponseOptionType;
import org.oasis.ebxml.registry.bindings.query.ResponseOptionType.ReturnType;
import org.oasis.ebxml.registry.bindings.rim.RegistryObjectType;

/**
 * The default implementation of the ReferenceResolver interface
 */
public class ReferenceResolverImpl implements ReferenceResolver {

    private static Log log = LogFactory.getLog(ReferenceResolverImpl.class);

    public static final String ASSOC_INCLUDE_FILTER_PROPERTY_PREFIX = "eric.server.referenceResolver.associations.includeFilterList";
    public static final String ASSOC_EXCLUDE_FILTER_PROPERTY_PREFIX = "eric.server.referenceResolver.associations.excludeFilterList";
    private Map<String, String[]> assocIncludeFiltersMap = null;
    private Map<String, String[]> assocExcludeFiltersMap = null;

    /**
     * This method prefetches referenced objects up to the default specified 
     * depth level: 0 implies only fetch matched objects.
     *
     * @param context 
     * The ServerRequestContext used in this request
     * @param ro
     * The RegistryObjecType for which reference resolution is requested
     */
    public Collection<RegistryObjectType> getReferencedObjects(ServerRequestContext context, RegistryObjectType ro)
            throws RegistryException {
        return this.getReferencedObjects(context, ro, 0);
    }

    /**
     * This method prefetches referenced objects up to specified depth level.
     * Depth = 0 (default) implies only fetch matched objects.
     * Depth = n implies, also fetch all objects referenced by matched
     * objects upto depth of n
     * Depth = -1 implies, also fetch all objects referenced by matched
     * objects upto any level.
     *
     * @param context
     * The ServerRequestContext used in this request
     * @param ro
     * The target RegistryObjectType for which referenced objects are requested
     * @param depth int
     * The depth of the target RegistryObjectType dependency resolution
     */
    public Collection<RegistryObjectType> getReferencedObjects(ServerRequestContext context, RegistryObjectType ro,
            int depth) throws RegistryException {

        log.trace("start: getReferencedObjects");
        ArrayList<RegistryObjectType> refObjs = new ArrayList<RegistryObjectType>();
        internalGetReferencedObjects(context, ro, depth, new HashMap<Object, Object>(), refObjs);
        log.trace("end: getReferencedObjects");
        return refObjs;
    }

    /*
     * Gets the Collection of ReferenceInfos for all object references within specified RegistryObject.
     * TODO: replace with reflections API when JAXB bindings use special class for ReferenceURI.
     *
     * Reference attributes based on scanning rim.xsd for anyURI.
     *
     * @param ro specifies the RegistryObject whose ObjectRefs are being sought.
     *
     * @param idMap The Map with old temporary id to new permanent id mapping.
     *
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private void internalGetReferencedObjects(ServerRequestContext serverContext, RegistryObjectType ro, int depth,
            Map<Object, Object> idMap, Collection<RegistryObjectType> refObjs) throws RegistryException {
        log.trace("start: internalGetReferencedObjects");
        try {
            if ((ro != null) && (!refObjs.contains(ro))) {
                if (log.isDebugEnabled()) {
                    log.debug("get references for this ro: " + ro.getId() + " " + ro.getObjectType());
                }
                refObjs.add(ro);
                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.RegistryObjectType",
                        idMap, "ObjectType", refObjs);

                processRefAttribute(serverContext, ro,
                        "org.oasis.ebxml.registry.bindings.rim.ClassificationNodeType", idMap, "Parent", refObjs);

                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.ClassificationType",
                        idMap, "ClassificationNode", refObjs);
                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.ClassificationType",
                        idMap, "ClassificationScheme", refObjs);
                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.ClassificationType",
                        idMap, "ClassifiedObject", refObjs);

                processRefAttribute(serverContext, ro,
                        "org.oasis.ebxml.registry.bindings.rim.ExternalIdentifierType", idMap,
                        "IdentificationScheme", refObjs);
                processRefAttribute(serverContext, ro,
                        "org.oasis.ebxml.registry.bindings.rim.ExternalIdentifierType", idMap, "RegistryObject",
                        refObjs);

                //FederationType fed = (FederationType)ro;
                //TODO: Fix so it adds only Strings not ObjectRefType
                //refInfos.addAll(fed.getMembers().getObjectRef());

                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.AssociationType1",
                        idMap, "AssociationType", refObjs);
                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.AssociationType1",
                        idMap, "SourceObject", refObjs);
                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.AssociationType1",
                        idMap, "TargetObject", refObjs);

                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.AuditableEventType",
                        idMap, "User", refObjs);
                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.AuditableEventType",
                        idMap, "RequestId", refObjs);

                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.OrganizationType",
                        idMap, "Parent", refObjs);

                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.RegistryType", idMap,
                        "Operator", refObjs);

                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.ServiceBindingType",
                        idMap, "Service", refObjs);
                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.ServiceBindingType",
                        idMap, "TargetBinding", refObjs);

                processRefAttribute(serverContext, ro,
                        "org.oasis.ebxml.registry.bindings.rim.SpecificationLinkType", idMap, "ServiceBinding",
                        refObjs);
                processRefAttribute(serverContext, ro,
                        "org.oasis.ebxml.registry.bindings.rim.SpecificationLinkType", idMap, "SpecificationObject",
                        refObjs);

                processRefAttribute(serverContext, ro, "org.oasis.ebxml.registry.bindings.rim.SubscriptionType",
                        idMap, "Selector", refObjs);

                if (depth != 0) {
                    depth--;
                    // Now process composed objects
                    Set<RegistryObjectType> composedObjects = BindingUtility.getInstance()
                            .getComposedRegistryObjects(ro, 1);
                    Collection<?> composedNoDups = checkForDuplicates(refObjs, composedObjects);
                    // Now process associated objects
                    Collection<?> associatedObjects = getAssociatedObjects(serverContext, ro, depth, idMap,
                            refObjs);
                    Collection assocNoDups = checkForDuplicates(refObjs, associatedObjects);
                    Collection relatedObjects = new ArrayList();
                    relatedObjects.addAll(composedNoDups);
                    relatedObjects.addAll(assocNoDups);
                    Iterator iter = relatedObjects.iterator();
                    while (iter.hasNext()) {
                        Object obj = iter.next();
                        if (obj instanceof RegistryObjectType) {
                            RegistryObjectType regObj = (RegistryObjectType) obj;
                            internalGetReferencedObjects(serverContext, regObj, depth, idMap, refObjs);
                        }
                    }
                }
            }
        } catch (RegistryException re) {
            throw re;
        } catch (Throwable t) {
            throw new RegistryException(t);
        }
        log.trace("end: internalGetReferencedObjects");
    }

    /*
     * This method gets all objects that are associated with the ro. These
     * objects are placed in the ref
     */
    private Collection<?> getAssociatedObjects(ServerRequestContext serverContext, RegistryObjectType ro, int depth,
            Map<?, ?> idMap, Collection<?> refObjs) throws RegistryException {
        log.trace("start: getAssociatedObjects");
        Collection<?> results = new ArrayList<Object>();
        try {
            String id = ro.getId();
            String sqlQuery = getSQLStringForGettingAssociatedObjects(ro);
            List<String> queryParams = new ArrayList<String>();
            queryParams.add(id.toUpperCase());
            ResponseOptionType ebResponseOptionType = BindingUtility.getInstance().queryFac
                    .createResponseOptionType();
            ebResponseOptionType.setReturnComposedObjects(true);
            ebResponseOptionType.setReturnType(ReturnType.LEAF_CLASS);
            ArrayList<Object> objectRefs = new ArrayList<Object>();
            List<?> queryResults = PersistenceManagerFactory.getInstance().getPersistenceManager().executeSQLQuery(
                    serverContext, sqlQuery, queryParams, ebResponseOptionType, "RegistryObject", objectRefs);
            if (queryResults != null) {
                results = queryResults;
            }
        } catch (Throwable t) {
            throw new RegistryException(t);
        }
        log.trace("end: getAssociatedObjects");
        return results;
    }

    /*
     * This method checks to see if any of the members of the results collection
     * are contained in the running total refObjs collection.  If they are
     * contained, identify them as a duplicate and do not include in new 
     * no dups collection.
     */
    @SuppressWarnings("rawtypes")
    private Collection checkForDuplicates(Collection refObjects, Collection results) {
        log.trace("start: checkForDuplicates");
        @SuppressWarnings("unchecked")
        Collection resultsWithNoDups = new ArrayList(results);
        if (refObjects != null && results != null) {
            Iterator resultsItr = results.iterator();
            while (resultsItr.hasNext()) {
                Object obj = resultsItr.next();
                RegistryObjectType ro = null;
                if (obj instanceof RegistryObjectType) {
                    ro = (RegistryObjectType) obj;
                    String roId = ro.getId();
                    Iterator refItr = refObjects.iterator();
                    while (refItr.hasNext()) {
                        RegistryObjectType refRO = (RegistryObjectType) refItr.next();
                        if (refRO.getId().equalsIgnoreCase(roId)) {
                            resultsWithNoDups.remove(ro);
                        }
                    }
                }
            }
        }
        log.trace("end: checkForDuplicates");
        return resultsWithNoDups;
    }

    /*
     * This method gets any configured association filters as a String[].
     * These filters are configured in eric.properties.
     */
    @SuppressWarnings("unused")
    private String[] getAssocFilterStrings(RegistryObjectType ro) {
        log.trace("start: getAssocFilterStrings");
        String type = ro.getObjectType();
        // Use object type to get any configured filters
        // Includes filter takes precedence over excludes
        String[] filters = getAssociationIncludeFiltersMap().get(type);
        if (filters == null || filters.length > 0) {
            // If no includes filters, look for excludes
            filters = getAssociationExcludeFiltersMap().get(type);
        }
        log.trace("end: getAssocFilterStrings");
        return filters;
    }

    /*
     * This method is used to get the SQL string used in resolving any 
     * assocations of the target RO
     */
    private String getSQLStringForGettingAssociatedObjects(RegistryObjectType targetRO) {
        log.trace("start: getSQLString");
        StringBuffer sb = new StringBuffer();
        String sqlQueryFragment = "SELECT * FROM registryobject WHERE id IN "
                + "(SELECT targetObject FROM association WHERE UPPER(sourceobject) = " + "UPPER(?)";
        sb.append(sqlQueryFragment);
        // Since include filters take precedence over exclude filter, look
        // for them first
        String[] filters = getAssociationIncludeFiltersMap().get(targetRO.getObjectType());
        if (filters == null || filters.length == 0) {
            // Since no include filters, look for exclude filters
            filters = getAssociationExcludeFiltersMap().get(targetRO.getObjectType());
            // Process exclude filters
            sb.append(getAssocationTypeSQLPredicate(filters, true));
        } else {
            // Process includes filters
            sb.append(getAssocationTypeSQLPredicate(filters, false));
        }
        sb.append(')');
        log.trace("end: getSQLString");
        return sb.toString();
    }

    /*
     * This method gets the correct assocationtype SQL predicate statement.
     * Association excludes filter adds a 'NOT' SQL keyword to the predicate.
     */
    private String getAssocationTypeSQLPredicate(String[] filters, boolean excludesFilter) {
        log.trace("start: getAssocationTypeSQLPredicate");
        StringBuffer sb = new StringBuffer();
        if (filters != null && filters.length > 0) {
            sb.append(" and associationtype");
            if (excludesFilter) {
                sb.append(" not");
            }
            sb.append(" in (");
            for (int i = 0; i < filters.length; i++) {
                if (i > 0) {
                    sb.append(", ");
                }
                sb.append("'").append(filters[i]).append("'");
            }
            sb.append(')');
        }
        log.trace("end: getAssocationTypeSQLPredicate");
        return sb.toString();
    }

    /**
     * Gets the Set of ReferenceInfo for specified reference attribute within RegistryObject.
     *
     * Reference attributes based on scanning rim.xsd for anyURI.
     *
     * @param ro specifies the RegistryObject whose reference attribute is being sought.
     *
     * @param idMap The HashMap with old temporary id to new permanent id mapping.
     *
     */
    @SuppressWarnings("unchecked")
    private void processRefAttribute(ServerRequestContext serverContext, RegistryObjectType ro, String className,
            Map<?, ?> idMap, String attribute, @SuppressWarnings("rawtypes") Collection refObjs)
            throws JAXRException {
        log.trace("start: processRefAttribute");
        try {
            //Use reflections API to get the attribute value, check if it needs to be mapped
            //and set it with mapped value if needed and add the targetObject in refObjs
            Class<?> clazz = Class.forName(className);
            if (!(clazz.isInstance(ro))) {
                return;
            }

            //Get the attribute value by calling get method
            String getMethodName = "get" + attribute;
            Method getMethod = clazz.getMethod(getMethodName, (java.lang.Class[]) null);

            //Invoke getMethod to get the reference target object's id
            String targetObjectId = (String) getMethod.invoke(ro, (java.lang.Object[]) null);

            if (targetObjectId != null) {
                //Check if id has been mapped to a new id
                if (idMap.containsKey(targetObjectId)) {
                    //Replace old id with new id
                    targetObjectId = (String) idMap.get(targetObjectId);

                    //Use set method to set new value on ro
                    Class<?>[] parameterTypes = new Class[1];
                    Object[] parameterValues = new Object[1];
                    parameterTypes[0] = Class.forName("java.lang.String");
                    parameterValues[0] = targetObjectId;
                    String setMethodName = "set" + attribute;
                    Method setMethod = clazz.getMethod(setMethodName, parameterTypes);
                    setMethod.invoke(ro, parameterValues);
                }
                log.debug("sourceObject: " + ro.getId() + " targetObject: " + targetObjectId + " attribute: "
                        + attribute);
                RegistryObjectType refObj = QueryManagerFactory.getInstance().getQueryManager()
                        .getRegistryObject(serverContext, targetObjectId);
                if (refObj != null && !refObjs.contains(refObj)) {
                    refObjs.add(refObj);
                }
            }

        } catch (Exception e) {
            //throw new OMARExeption("Class = " ro.getClass() + " attribute = " + attribute", e);
            log.error(CommonResourceBundle.getInstance().getString("message.ErrorClassAttribute",
                    new Object[] { ro.getClass(), attribute }));
            e.printStackTrace();
        }
        log.trace("end: processRefAttribute");
    }

    /*
     * This method is used to get a java.util.Map of association include filters
     * configured in eric.properties
     */
    private Map<String, String[]> getAssociationIncludeFiltersMap() {
        log.trace("start: getAssociationIncludeFiltersMap");
        if (assocIncludeFiltersMap == null) {
            synchronized (this) {
                assocIncludeFiltersMap = new HashMap<String, String[]>();
                RegistryProperties props = RegistryProperties.getInstance();
                Iterator<String> propsIter = props
                        .getPropertyNamesStartingWith(ASSOC_INCLUDE_FILTER_PROPERTY_PREFIX);

                while (propsIter.hasNext()) {
                    String prop = propsIter.next();
                    String objectTypeForFilter = prop.substring(ASSOC_INCLUDE_FILTER_PROPERTY_PREFIX.length() + 1);
                    String assocFilters = props.getProperty(prop);
                    String[] assocFilterArray = assocFilters.split("\\|");
                    assocIncludeFiltersMap.put(objectTypeForFilter, assocFilterArray);
                }
            }
        }
        log.trace("end: getAssociationIncludeFiltersMap");
        return assocIncludeFiltersMap;
    }

    /*
     * This method is used to get a java.util.Map of association exclude filters
     * configured in eric.properties
     */
    private Map<String, String[]> getAssociationExcludeFiltersMap() {
        log.trace("start: getAssociationExcludeFiltersMap");
        if (assocExcludeFiltersMap == null) {
            synchronized (this) {
                assocExcludeFiltersMap = new HashMap<String, String[]>();
                // The Include Filter Map has precedence over the excludes. If an
                // includes filter is set, ignore the excludes filters
                if (getAssociationIncludeFiltersMap().size() == 0) {
                    RegistryProperties props = RegistryProperties.getInstance();
                    Iterator<String> propsIter = props
                            .getPropertyNamesStartingWith(ASSOC_EXCLUDE_FILTER_PROPERTY_PREFIX);

                    while (propsIter.hasNext()) {
                        String prop = propsIter.next();
                        String objectTypeForFilter = prop
                                .substring(ASSOC_EXCLUDE_FILTER_PROPERTY_PREFIX.length() + 1);
                        String assocFilters = props.getProperty(prop);
                        String[] assocFilterArray = assocFilters.split("\\|");
                        assocExcludeFiltersMap.put(objectTypeForFilter, assocFilterArray);
                    }
                }
            }
        }
        log.trace("end: getAssociationExcludeFiltersMap");
        return assocExcludeFiltersMap;
    }

}