org.structr.core.module.ModuleService.java Source code

Java tutorial

Introduction

Here is the source code for org.structr.core.module.ModuleService.java

Source

/*
 *  Copyright (C) 2010-2013 Axel Morgner, structr <structr@structr.org>
 *
 *  This file is part of structr <http://structr.org>.
 *
 *  structr 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.
 *
 *  structr 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
 *  along with structr.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.structr.core.module;

import org.structr.core.agent.Agent;
import org.structr.core.entity.AbstractNode;
import org.structr.core.entity.AbstractRelationship;
import org.structr.core.entity.GenericNode;

//~--- JDK imports ------------------------------------------------------------

import java.io.File;
import java.io.IOException;

import java.lang.reflect.Modifier;

import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.commons.lang.StringUtils;
import org.structr.core.*;

//~--- classes ----------------------------------------------------------------

/**
 * The module service main class.
 * 
 * @author Christian Morgner
 */
public class ModuleService implements SingletonService {

    private static final Logger logger = Logger.getLogger(ModuleService.class.getName());
    private static final Map<String, Class<? extends Agent>> agentClassCache = new ConcurrentHashMap<String, Class<? extends Agent>>(
            10, 0.9f, 8);
    private static final Set<String> nodeEntityPackages = new LinkedHashSet<String>();
    private static final Set<String> relationshipPackages = new LinkedHashSet<String>();
    private static final Map<String, Class> relationshipClassCache = new ConcurrentHashMap<String, Class>(10, 0.9f,
            8);
    private static final Map<String, Class> nodeEntityClassCache = new ConcurrentHashMap<String, Class>(100, 0.9f,
            8);
    private static final Map<String, Set<Class>> interfaceCache = new ConcurrentHashMap<String, Set<Class>>(10,
            0.9f, 8);
    private static final Set<String> agentPackages = new LinkedHashSet<String>();
    private static final String fileSep = System.getProperty("file.separator");
    private static final String fileSepEscaped = fileSep.replaceAll("\\\\", "\\\\\\\\"); // ....
    private static final String pathSep = System.getProperty("path.separator");
    private static final String testClassesDir = fileSep.concat("test-classes");
    private static final String classesDir = fileSep.concat("classes");

    //~--- methods --------------------------------------------------------

    /**
     * Loads and activates the given module.
     *
     * @param resourceName the file name of the module to activate
     * @param recreateIndex whether to recreate the index file
     */
    public void scanResource(String resourceName) {

        try {

            Module module = loadResource(resourceName);

            if (module != null) {

                importResource(module);
            } else {

                logger.log(Level.WARNING, "Module was null!");
            }

        } catch (IOException ioex) {

            logger.log(Level.WARNING, "Error loading module {0}: {1}", new Object[] { resourceName, ioex });
            ioex.printStackTrace();

        }

    }

    @Override
    public void injectArguments(Command command) {

        if (command != null) {

            command.setArgument("moduleService", this);
        }

    }

    @Override
    public void initialize(Map<String, String> context) {

        scanResources();

    }

    @Override
    public void shutdown() {

        nodeEntityClassCache.clear();
        relationshipClassCache.clear();
        agentClassCache.clear();

    }

    private void scanResources() {

        Set<String> resourcePaths = getResourcesToScan();

        for (String resourcePath : resourcePaths) {

            scanResource(resourcePath);
        }

        logger.log(Level.INFO, "{0} JARs scanned", resourcePaths.size());

    }

    /**
     * Processes the information from the given module and makes them available for the service layer.
     *
     * @param module the module to process
     *
     * @throws IOException
     */
    private void importResource(Module module) throws IOException {

        Set<String> classes = module.getClasses();

        for (final String name : classes) {

            String className = StringUtils.removeStart(name, ".");

            logger.log(Level.FINE, "Instantiating class {0} ", className);

            try {

                // instantiate class..
                Class clazz = Class.forName(className);

                logger.log(Level.FINE, "Class {0} instantiated: {1}", new Object[] { className, clazz });

                if (!Modifier.isAbstract(clazz.getModifiers())) {

                    // register node entity classes
                    if (AbstractNode.class.isAssignableFrom(clazz)) {

                        EntityContext.init(clazz);

                        // try to instantiate class
                        try {
                            EntityContext.scanEntity(clazz.newInstance());
                        } catch (Throwable t) {
                        }

                        String simpleName = clazz.getSimpleName();
                        String fullName = clazz.getName();

                        nodeEntityClassCache.put(simpleName, clazz);
                        nodeEntityPackages.add(fullName.substring(0, fullName.lastIndexOf(".")));

                        for (Class interfaceClass : clazz.getInterfaces()) {

                            String interfaceName = interfaceClass.getSimpleName();
                            Set<Class> classesForInterface = interfaceCache.get(interfaceName);

                            if (classesForInterface == null) {

                                classesForInterface = new LinkedHashSet<Class>();

                                interfaceCache.put(interfaceName, classesForInterface);

                            }

                            classesForInterface.add(clazz);

                        }

                    }

                    // register entity classes
                    if (AbstractRelationship.class.isAssignableFrom(clazz)) {

                        EntityContext.init(clazz);

                        // try to instantiate class
                        try {
                            EntityContext.scanEntity(clazz.newInstance());
                        } catch (Throwable t) {
                        }

                        String simpleName = clazz.getSimpleName();
                        String fullName = clazz.getName();

                        relationshipClassCache.put(simpleName, clazz);
                        relationshipPackages.add(fullName.substring(0, fullName.lastIndexOf(".")));

                        for (Class interfaceClass : clazz.getInterfaces()) {

                            String interfaceName = interfaceClass.getSimpleName();
                            Set<Class> classesForInterface = interfaceCache.get(interfaceName);

                            if (classesForInterface == null) {

                                classesForInterface = new LinkedHashSet<Class>();

                                interfaceCache.put(interfaceName, classesForInterface);

                            }

                            classesForInterface.add(clazz);

                        }
                    }

                    // register services
                    if (Service.class.isAssignableFrom(clazz)) {

                        Services.registerServiceClass(clazz);
                    }

                    // register agents
                    if (Agent.class.isAssignableFrom(clazz)) {

                        String simpleName = clazz.getSimpleName();
                        String fullName = clazz.getName();

                        agentClassCache.put(simpleName, clazz);
                        agentPackages.add(fullName.substring(0, fullName.lastIndexOf(".")));

                    }
                }

            } catch (Throwable t) {
            }

        }

    }

    private Module loadResource(String resource) throws IOException {

        // create module
        DefaultModule ret = new DefaultModule(resource);
        Set<String> classes = ret.getClasses();

        if (resource.endsWith(".jar") || resource.endsWith(".war")) {

            ZipFile zipFile = new ZipFile(new File(resource), ZipFile.OPEN_READ);

            // conventions that might be useful here:
            // ignore entries beginning with meta-inf/
            // handle entries beginning with images/ as IMAGE
            // handle entries beginning with pages/ as PAGES
            // handle entries ending with .jar as libraries, to be deployed to WEB-INF/lib
            // handle other entries as potential page and/or entity classes
            // .. to be extended
            // (entries that end with "/" are directories)
            for (Enumeration<? extends ZipEntry> entries = zipFile.entries(); entries.hasMoreElements();) {

                ZipEntry entry = entries.nextElement();
                String entryName = entry.getName();

                if (entryName.endsWith(".class")) {

                    String fileEntry = entry.getName().replaceAll("[/]+", ".");

                    // add class entry to Module
                    classes.add(fileEntry.substring(0, fileEntry.length() - 6));

                }

            }

            zipFile.close();

        } else if (resource.endsWith(classesDir)) {

            addClassesRecursively(new File(resource), classesDir, classes);

        } else if (resource.endsWith(testClassesDir)) {

            addClassesRecursively(new File(resource), testClassesDir, classes);
        }

        return ret;
    }

    private void addClassesRecursively(File dir, String prefix, Set<String> classes) {

        int prefixLen = prefix.length();

        for (File file : dir.listFiles()) {

            if (file.isDirectory()) {

                addClassesRecursively(file, prefix, classes);

            } else {

                try {

                    String fileEntry = file.getAbsolutePath();

                    fileEntry = fileEntry.substring(0, fileEntry.length() - 6);
                    fileEntry = fileEntry.substring(fileEntry.indexOf(prefix) + prefixLen);
                    fileEntry = fileEntry.replaceAll("[".concat(fileSepEscaped).concat("]+"), ".");

                    if (fileEntry.startsWith(".")) {
                        fileEntry = fileEntry.substring(1);
                    }

                    classes.add(fileEntry);

                } catch (Throwable t) {

                    // ignore
                    t.printStackTrace();
                }

            }

        }

    }

    //~--- get methods ----------------------------------------------------

    public Set<String> getNodeEntityPackages() {

        return nodeEntityPackages;

    }

    public Set<String> getAgentPackages() {

        return agentPackages;

    }

    public Set<String> getCachedNodeEntityTypes() {

        return nodeEntityClassCache.keySet();

    }

    public Set<String> getCachedAgentTypes() {

        return agentClassCache.keySet();

    }

    public Set<Class> getClassesForInterface(String simpleName) {

        return interfaceCache.get(simpleName);

    }

    public Map<String, Class> getCachedNodeEntities() {

        return nodeEntityClassCache;

    }

    public Map<String, Class<? extends Agent>> getCachedAgents() {

        return agentClassCache;

    }

    public Set<String> getRelationshipPackages() {

        return relationshipPackages;

    }

    public Set<String> getCachedRelationshipTypes() {

        return relationshipClassCache.keySet();

    }

    public Map<String, Class> getCachedRelationships() {

        return relationshipClassCache;

    }

    public Class getNodeEntityClass(final String name) {

        Class ret = GenericNode.class;

        if ((name != null) && (!name.isEmpty())) {

            ret = nodeEntityClassCache.get(name);

            if (ret == null) {

                for (String possiblePath : nodeEntityPackages) {

                    if (possiblePath != null) {

                        try {

                            Class nodeClass = Class.forName(possiblePath + "." + name);

                            if (!Modifier.isAbstract(nodeClass.getModifiers())) {

                                nodeEntityClassCache.put(name, nodeClass);

                                // first match wins
                                break;

                            }

                        } catch (ClassNotFoundException ex) {

                            // ignore
                        }

                    }

                }

            }

        }

        return (ret);

    }

    public Class getRelationshipClass(final String name) {

        Class ret = AbstractNode.class;

        if ((name != null) && (name.length() > 0)) {

            ret = relationshipClassCache.get(name);

            if (ret == null) {

                for (String possiblePath : relationshipPackages) {

                    if (possiblePath != null) {

                        try {

                            Class nodeClass = Class.forName(possiblePath + "." + name);

                            if (!Modifier.isAbstract(nodeClass.getModifiers())) {

                                relationshipClassCache.put(name, nodeClass);

                                // first match wins
                                break;

                            }

                        } catch (ClassNotFoundException ex) {

                            // ignore
                        }

                    }

                }

            }

        }

        return (ret);

    }

    public Class<? extends Agent> getAgentClass(final String name) {

        Class ret = null;

        if ((name != null) && (name.length() > 0)) {

            ret = agentClassCache.get(name);

            if (ret == null) {

                for (String possiblePath : agentPackages) {

                    if (possiblePath != null) {

                        try {

                            Class nodeClass = Class.forName(possiblePath + "." + name);

                            agentClassCache.put(name, nodeClass);

                            // first match wins
                            break;

                        } catch (ClassNotFoundException ex) {

                            // ignore
                        }

                    }

                }

            }

        }

        return (ret);

    }

    /**
     * Scans the class path and returns a Set containing all structr modules.
     *
     * @return a Set of active module names
     */
    public Set<String> getResourcesToScan() {

        String classPath = System.getProperty("java.class.path");
        Set<String> ret = new LinkedHashSet<String>();
        Pattern pattern = Pattern.compile(".*(structr).*(war|jar)");
        Matcher matcher = pattern.matcher("");

        for (String jarPath : classPath.split("[".concat(pathSep).concat("]+"))) {

            String lowerPath = jarPath.toLowerCase();

            if (lowerPath.endsWith(classesDir) || lowerPath.endsWith(testClassesDir)) {

                ret.add(jarPath);

            } else {

                String moduleName = lowerPath.substring(lowerPath.lastIndexOf(pathSep) + 1);

                matcher.reset(moduleName);

                if (matcher.matches()) {

                    ret.add(jarPath);
                }

            }

        }

        String resources = Services.getResources();

        if (resources != null) {

            for (String resource : resources.split("[".concat(pathSep).concat("]+"))) {

                String lowerResource = resource.toLowerCase();

                if (lowerResource.endsWith(".jar") || lowerResource.endsWith(".war")) {

                    ret.add(resource);
                }

            }

        }

        // logger.log(Level.INFO, "resources: {0}", ret);

        return (ret);

    }

    @Override
    public String getName() {

        return (ModuleService.class.getSimpleName());

    }

    @Override
    public boolean isRunning() {

        // we're always running :)
        return (true);
    }
}