Package org.jboss.dna.repository

Source Code of org.jboss.dna.repository.RepositoryService

/*
* JBoss DNA (http://www.jboss.org/dna)
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.  Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
* See the AUTHORS.txt file in the distribution for a full listing of
* individual contributors.
*
* JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
* is licensed to you under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* JBoss DNA 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.dna.repository;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import net.jcip.annotations.ThreadSafe;
import org.jboss.dna.common.collection.Problems;
import org.jboss.dna.common.collection.SimpleProblems;
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.common.util.Logger;
import org.jboss.dna.common.util.Reflection;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.Graph;
import org.jboss.dna.graph.JcrLexicon;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.Node;
import org.jboss.dna.graph.Subgraph;
import org.jboss.dna.graph.connector.RepositorySource;
import org.jboss.dna.graph.observe.Changes;
import org.jboss.dna.graph.observe.NetChangeObserver;
import org.jboss.dna.graph.observe.Observer;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.PathFactory;
import org.jboss.dna.graph.property.PathNotFoundException;
import org.jboss.dna.graph.property.Property;
import org.jboss.dna.graph.property.PropertyType;
import org.jboss.dna.graph.property.ValueFactories;
import org.jboss.dna.graph.property.ValueFactory;
import org.jboss.dna.repository.service.AbstractServiceAdministrator;
import org.jboss.dna.repository.service.AdministeredService;
import org.jboss.dna.repository.service.ServiceAdministrator;

/**
* A service that manages the {@link RepositorySource}es defined within a configuration repository.
*/
@ThreadSafe
public class RepositoryService implements AdministeredService, Observer {

    /**
     * The administrative component for this service.
     *
     * @author Randall Hauch
     */
    protected class Administrator extends AbstractServiceAdministrator {

        protected Administrator() {
            super(RepositoryI18n.federationServiceName, State.PAUSED);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected boolean doCheckIsTerminated() {
            return true;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected void doStart( State fromState ) {
            super.doStart(fromState);
            startService();
        }

        /**
         * {@inheritDoc}
         *
         * @see org.jboss.dna.repository.service.AbstractServiceAdministrator#doShutdown(org.jboss.dna.repository.service.ServiceAdministrator.State)
         */
        @Override
        protected void doShutdown( State fromState ) {
            super.doShutdown(fromState);
            shutdownService();
        }

        /**
         * {@inheritDoc}
         *
         * @see org.jboss.dna.repository.service.ServiceAdministrator#awaitTermination(long, java.util.concurrent.TimeUnit)
         */
        public boolean awaitTermination( long timeout,
                                         TimeUnit unit ) {
            return true;
        }
    }

    private final ExecutionContext context;
    private final RepositoryLibrary sources;
    private final String configurationSourceName;
    private final String configurationWorkspaceName;
    private final Path pathToConfigurationRoot;
    private final ConfigurationChangeObserver configurationChangeObserver;
    private final Administrator administrator = new Administrator();
    private final AtomicBoolean started = new AtomicBoolean(false);
    /** The problem sink used when encountering problems while starting repositories */
    private final Problems problems;

    /**
     * Create a service instance, reading the configuration describing new {@link RepositorySource} instances from the supplied
     * configuration repository.
     *
     * @param configurationSource the {@link RepositorySource} that is the configuration repository
     * @param configurationWorkspaceName the name of the workspace in the {@link RepositorySource} that is the configuration
     *        repository, or null if the default workspace of the source should be used (if there is one)
     * @param pathToConfigurationRoot the path of the node in the configuration source repository that should be treated by this
     *        service as the root of the service's configuration; if null, then "/dna:system" is used
     * @param context the execution context in which this service should run
     * @param problems the {@link Problems} instance that this service should use to report problems starting repositories
     * @throws IllegalArgumentException if the bootstrap source is null or the execution context is null
     */
    public RepositoryService( RepositorySource configurationSource,
                              String configurationWorkspaceName,
                              Path pathToConfigurationRoot,
                              ExecutionContext context,
                              Problems problems ) {
        CheckArg.isNotNull(configurationSource, "configurationSource");
        CheckArg.isNotNull(context, "context");
        PathFactory pathFactory = context.getValueFactories().getPathFactory();
        if (pathToConfigurationRoot == null) pathToConfigurationRoot = pathFactory.create("/dna:system");
        if (problems == null) problems = new SimpleProblems();
        Path sourcesPath = pathFactory.create(pathToConfigurationRoot, DnaLexicon.SOURCES);

        this.sources = new RepositoryLibrary(configurationSource, configurationWorkspaceName, sourcesPath, context);
        this.sources.addSource(configurationSource);
        this.pathToConfigurationRoot = pathToConfigurationRoot;
        this.configurationSourceName = configurationSource.getName();
        this.configurationWorkspaceName = configurationWorkspaceName;
        this.context = context;
        this.problems = problems;
        this.configurationChangeObserver = new ConfigurationChangeObserver();
    }

    /**
     * {@inheritDoc}
     */
    public final ServiceAdministrator getAdministrator() {
        return this.administrator;
    }

    /**
     * @return configurationSourceName
     */
    public final String getConfigurationSourceName() {
        return configurationSourceName;
    }

    /**
     * @return configurationWorkspaceName
     */
    public final String getConfigurationWorkspaceName() {
        return configurationWorkspaceName;
    }

    /**
     * Get the library of {@link RepositorySource} instances used by this service.
     *
     * @return the RepositorySource library; never null
     */
    public final RepositoryLibrary getRepositoryLibrary() {
        return sources;
    }

    /**
     * @return pathToConfigurationRoot
     */
    protected final Path getPathToConfigurationRoot() {
        return pathToConfigurationRoot;
    }

    /**
     * @return env
     */
    public final ExecutionContext getExecutionEnvironment() {
        return context;
    }

    public String getJndiName() {
        // TODO
        return null;
    }

    protected synchronized void startService() {
        if (this.started.get() == false) {
            // ------------------------------------------------------------------------------------
            // Read the configuration ...
            // ------------------------------------------------------------------------------------

            // Read the configuration and repository source nodes (children under "/dna:sources") ...
            Graph graph = Graph.create(getConfigurationSourceName(), sources, context);
            Path pathToSourcesNode = context.getValueFactories().getPathFactory().create(pathToConfigurationRoot,
                                                                                         DnaLexicon.SOURCES);
            try {
                String workspaceName = getConfigurationWorkspaceName();
                if (workspaceName != null) graph.useWorkspace(workspaceName);

                Subgraph sourcesGraph = graph.getSubgraphOfDepth(3).at(pathToSourcesNode);

                // Iterate over each of the children, and create the RepositorySource ...
                for (Location location : sourcesGraph.getRoot().getChildren()) {
                    Node sourceNode = sourcesGraph.getNode(location);
                    sources.addSource(createRepositorySource(location.getPath(), sourceNode.getPropertiesByName(), problems));
                }
            } catch (PathNotFoundException e) {
                // No sources were found, and this is okay!
            } catch (Throwable err) {
                throw new DnaConfigurationException(RepositoryI18n.errorStartingRepositoryService.text(), err);
            }

            this.started.set(true);
        }
    }

    protected synchronized void shutdownService() {
        // Close the repository library ...
        this.sources.getAdministrator().shutdown();
    }

    /**
     * Instantiate the {@link RepositorySource} described by the supplied properties.
     *
     * @param path the path to the node where these properties were found; never null
     * @param properties the properties; never null
     * @param problems the problems container in which any problems should be reported; never null
     * @return the repository source instance, or null if it could not be created
     */
    protected RepositorySource createRepositorySource( Path path,
                                                       Map<Name, Property> properties,
                                                       Problems problems ) {
        ValueFactories valueFactories = context.getValueFactories();
        ValueFactory<String> stringFactory = valueFactories.getStringFactory();

        // Get the classname and classpath ...
        Property classnameProperty = properties.get(DnaLexicon.CLASSNAME);
        Property classpathProperty = properties.get(DnaLexicon.CLASSPATH);
        if (classnameProperty == null) {
            problems.addError(RepositoryI18n.requiredPropertyIsMissingFromNode, DnaLexicon.CLASSNAME, path);
        }
        // If the classpath property is null or empty, the default classpath will be used
        if (problems.hasErrors()) return null;

        // Create the instance ...
        assert classnameProperty != null;
        String classname = stringFactory.create(classnameProperty.getValues().next());
        String[] classpath = classpathProperty == null ? new String[] {} : stringFactory.create(classpathProperty.getValuesAsArray());
        ClassLoader classLoader = context.getClassLoader(classpath);
        RepositorySource source = null;
        try {
            Class<?> sourceClass = classLoader.loadClass(classname);
            source = (RepositorySource)sourceClass.newInstance();
        } catch (ClassNotFoundException err) {
            problems.addError(err, RepositoryI18n.unableToLoadClassUsingClasspath, classname, classpath);
        } catch (IllegalAccessException err) {
            problems.addError(err, RepositoryI18n.unableToAccessClassUsingClasspath, classname, classpath);
        } catch (Throwable err) {
            problems.addError(err, RepositoryI18n.unableToInstantiateClassUsingClasspath, classname, classpath);
        }
        if (source == null) return null;

        // We need to set the name using the local name of the node...
        Property nameProperty = context.getPropertyFactory().create(JcrLexicon.NAME,
                                                                    path.getLastSegment().getName().getLocalName());
        properties.put(JcrLexicon.NAME, nameProperty);

        // Attempt to set the configuration information as bean properties,
        // if they exist on the RepositorySource object and are not already set to some value ...
        setBeanPropertyIfExistsAndNotSet(source, "configurationSourceName", getConfigurationSourceName());
        setBeanPropertyIfExistsAndNotSet(source, "configurationWorkspaceName", getConfigurationWorkspaceName());
        setBeanPropertyIfExistsAndNotSet(source, "configurationPath", stringFactory.create(path));

        // Now set all the properties that we can, ignoring any property that doesn't fit the pattern ...
        Reflection reflection = new Reflection(source.getClass());
        for (Map.Entry<Name, Property> entry : properties.entrySet()) {
            Name propertyName = entry.getKey();
            Property property = entry.getValue();
            String javaPropertyName = propertyName.getLocalName();
            if (property.isEmpty()) continue;

            Object value = null;
            Method setter = null;
            try {
                setter = reflection.findFirstMethod("set" + javaPropertyName, false);
                if (setter == null) continue;
                // Determine the type of the one parameter ...
                Class<?>[] parameterTypes = setter.getParameterTypes();
                if (parameterTypes.length != 1) continue; // not a valid JavaBean property
                Class<?> paramType = parameterTypes[0];
                PropertyType allowedType = PropertyType.discoverType(paramType);
                if (allowedType == null) continue; // assume not a JavaBean property with usable type
                ValueFactory<?> factory = context.getValueFactories().getValueFactory(allowedType);
                if (paramType.isArray()) {
                    if (paramType.getComponentType().isArray()) continue; // array of array, which we don't do
                    Object[] values = factory.create(property.getValuesAsArray());
                    // Convert to an array of primitives if that's what the signature requires ...
                    Class<?> componentType = paramType.getComponentType();
                    if (Integer.TYPE.equals(componentType)) {
                        int[] primitiveValues = new int[values.length];
                        for (int i = 0; i != values.length; ++i) {
                            primitiveValues[i] = ((Long)values[i]).intValue();
                        }
                        value = primitiveValues;
                    } else if (Short.TYPE.equals(componentType)) {
                        short[] primitiveValues = new short[values.length];
                        for (int i = 0; i != values.length; ++i) {
                            primitiveValues[i] = ((Long)values[i]).shortValue();
                        }
                        value = primitiveValues;
                    } else if (Long.TYPE.equals(componentType)) {
                        long[] primitiveValues = new long[values.length];
                        for (int i = 0; i != values.length; ++i) {
                            primitiveValues[i] = ((Long)values[i]).longValue();
                        }
                        value = primitiveValues;
                    } else if (Double.TYPE.equals(componentType)) {
                        double[] primitiveValues = new double[values.length];
                        for (int i = 0; i != values.length; ++i) {
                            primitiveValues[i] = ((Double)values[i]).doubleValue();
                        }
                        value = primitiveValues;
                    } else if (Float.TYPE.equals(componentType)) {
                        float[] primitiveValues = new float[values.length];
                        for (int i = 0; i != values.length; ++i) {
                            primitiveValues[i] = ((Double)values[i]).floatValue();
                        }
                        value = primitiveValues;
                    } else if (Boolean.TYPE.equals(componentType)) {
                        boolean[] primitiveValues = new boolean[values.length];
                        for (int i = 0; i != values.length; ++i) {
                            primitiveValues[i] = ((Boolean)values[i]).booleanValue();
                        }
                        value = primitiveValues;
                    } else {
                        value = values;
                    }
                } else {
                    value = factory.create(property.getFirstValue());
                    // Convert to the correct primitive, if needed ...
                    if (Integer.TYPE.equals(paramType)) {
                        value = new Integer(((Long)value).intValue());
                    } else if (Short.TYPE.equals(paramType)) {
                        value = new Short(((Long)value).shortValue());
                    } else if (Float.TYPE.equals(paramType)) {
                        value = new Float(((Double)value).floatValue());
                    }
                }
                // Invoke the method ...
                String msg = "Setting property {0} to {1} on source at {2} in configuration repository {3} in workspace {4}";
                Logger.getLogger(getClass()).trace(msg,
                                                   javaPropertyName,
                                                   value,
                                                   path,
                                                   configurationSourceName,
                                                   configurationWorkspaceName);
                setter.invoke(source, value);
            } catch (SecurityException err) {
                Logger.getLogger(getClass()).debug(err, "Error invoking {0}.{1}", source.getClass(), setter);
            } catch (IllegalArgumentException err) {
                // Do nothing ... assume not a JavaBean property (but log)
                String msg = "Invalid argument invoking {0} with parameter {1} on source at {2} in configuration repository {3} in workspace {4}";
                Logger.getLogger(getClass()).debug(err,
                                                   msg,
                                                   setter,
                                                   value,
                                                   path,
                                                   configurationSourceName,
                                                   configurationWorkspaceName);
            } catch (IllegalAccessException err) {
                Logger.getLogger(getClass()).debug(err, "Error invoking {0}.{1}", source.getClass(), setter);
            } catch (InvocationTargetException err) {
                // Do nothing ... assume not a JavaBean property (but log)
                String msg = "Error invoking {0} with parameter {1} on source at {2} in configuration repository {3} in workspace {4}";
                Logger.getLogger(getClass()).debug(err.getTargetException(),
                                                   msg,
                                                   setter,
                                                   value,
                                                   path,
                                                   configurationSourceName,
                                                   configurationWorkspaceName);
            }
        }
        return source;
    }

    protected boolean setBeanPropertyIfExistsAndNotSet( Object target,
                                                        String propertyName,
                                                        Object value ) {
        Reflection reflection = new Reflection(target.getClass());
        try {
            if (reflection.invokeGetterMethodOnTarget(propertyName, target) == null) {
                reflection.invokeSetterMethodOnTarget(propertyName, target, value);
                return true;
            }
            return false;
        } catch (Exception e) {
            // Log that the property was not found ...
            Logger.getLogger(getClass())
                  .debug("Unknown property '{0}' on '{1}' class", propertyName, target.getClass().getName());
            return false;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals( Object obj ) {
        if (obj == this) return true;
        return false;
    }

    /**
     * {@inheritDoc}
     *
     * @see org.jboss.dna.graph.observe.Observer#notify(org.jboss.dna.graph.observe.Changes)
     */
    public void notify( Changes changes ) {
        // Forward the changes to the net change observer ...
        this.configurationChangeObserver.notify(changes);
    }

    protected class ConfigurationChangeObserver extends NetChangeObserver {
        /**
         * {@inheritDoc}
         *
         * @see org.jboss.dna.graph.observe.NetChangeObserver#notify(org.jboss.dna.graph.observe.NetChangeObserver.NetChange)
         */
        @Override
        protected void notify( NetChange change ) {
            if (!getConfigurationSourceName().equals(change.getRepositorySourceName())) return;
            if (!getConfigurationWorkspaceName().equals(change.getRepositoryWorkspaceName())) return;
            Path changedPath = change.getPath();
            Path configPath = getPathToConfigurationRoot();
            if (!changedPath.isAtOrBelow(getPathToConfigurationRoot())) return;
            boolean changedNodeIsPotentiallySource = configPath.size() + 1 == changedPath.size();

            // At this point, we know that something inside the configuration changed, so figure out what happened ...
            if (changedNodeIsPotentiallySource && change.includes(ChangeType.NODE_REMOVED)) {
                // Then potentially a source with the supplied name has been removed ...
                String sourceName = changedPath.getLastSegment().getName().getLocalName();
                getRepositoryLibrary().removeSource(sourceName);
            } else {
                // The add/change/remove is either at or below a source, so try to create a new source for it ...
                Path sourcePath = changedNodeIsPotentiallySource ? changedPath : changedPath.subpath(0, configPath.size() + 1);
                Problems problems = new SimpleProblems();
                // Now read the node and create the source ...
                Graph graph = Graph.create(getConfigurationSourceName(), getRepositoryLibrary(), getExecutionEnvironment());
                try {
                    String workspaceName = getConfigurationWorkspaceName();
                    if (workspaceName != null) graph.useWorkspace(workspaceName);
                    Map<Name, Property> properties = graph.getPropertiesByName().on(sourcePath);
                    RepositorySource source = createRepositorySource(sourcePath, properties, problems);
                    if (source != null) {
                        // It was the config for a source, so try to add or replace an existing source ...
                        getRepositoryLibrary().addSource(source, true);
                    }
                } catch (PathNotFoundException e) {
                    // No source was found, and this is okay (since it may just been deleted)...
                    String sourceName = changedPath.getLastSegment().getName().getLocalName();
                    getRepositoryLibrary().removeSource(sourceName);
                }
            }
        }
    }
}
TOP

Related Classes of org.jboss.dna.repository.RepositoryService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.