org.xchain.framework.scanner.ScannerLifecycle.java Source code

Java tutorial

Introduction

Here is the source code for org.xchain.framework.scanner.ScannerLifecycle.java

Source

/**
 *    Copyright 2011 meltmedia
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.xchain.framework.scanner;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.net.URL;

import org.xchain.framework.lifecycle.LifecycleClass;
import org.xchain.framework.lifecycle.LifecycleAccessor;
import org.apache.commons.collections.map.LRUMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The lifecycle scanner provides utilities for scanning the class loader of an xcahins application.
 *
 * @author Christian Trimble
 * @author Josh Kennedy
 * @author John Trimble
 */
@LifecycleClass(uri = "http://www.xchain.org/scanner")
public class ScannerLifecycle {
    /*
     * Flag to turn on/off memoization of ScannerLifecycle.scanNode().
     */
    private static final boolean MEMOIZE_SCAN_NODE = true;
    private static Logger log = LoggerFactory.getLogger(ScannerLifecycle.class);

    private static ScannerLifecycle instance = new ScannerLifecycle();
    private Map<ClassLoader, Map<RootUrlLocator, ScanNode>> cache;

    @LifecycleAccessor
    public static ScannerLifecycle getInstance() {
        return instance;
    }

    private Map<String, ProtocolScanner> protocolMap = new HashMap<String, ProtocolScanner>();

    public ScannerLifecycle() {
        protocolMap.put("file", new FileProtocolScanner());
        protocolMap.put("jar", new JarProtocolScanner());

        // define the vfszip, vfsfile, and vfsmemory protocols if the jboss virtual file system classes are on the classpath.
        if (isVfsDefined()) {
            protocolMap.put("vfszip", new VfsProtocolScanner());
            protocolMap.put("vfsfile", new VfsProtocolScanner());
            protocolMap.put("vfsmemory", new VfsProtocolScanner());
        }
        if (isZipDefined()) {
            protocolMap.put("zip", new ZipProtocolScanner());
        }
        if (isBundleDefined()) {
            protocolMap.put("bundle", new BundleProtocolScanner());
            protocolMap.put("bundleresource", new BundleProtocolScanner());
        }

        if (MEMOIZE_SCAN_NODE) {
            cache = Collections.synchronizedMap(new WeakHashMap<ClassLoader, Map<RootUrlLocator, ScanNode>>());
        }
    }

    /**
     * Returns the root scan node.  Using this node, all of the nodes on the
     * classpath can be visited.
     */
    public ScanNode scanNode() throws Exception {
        return scanNode(Thread.currentThread().getContextClassLoader(),
                new MarkerResourceLocator("META-INF/xchain.xml"));
    }

    /**
     * <p>Scans the root urls found by the locator and returns the root scan node for those roots.</p>
     */
    public ScanNode scanNode(RootUrlLocator locator) throws Exception {
        return scanNode(Thread.currentThread().getContextClassLoader(), locator);
    }

    public ScanNode scanNode(ClassLoader classLoader, RootUrlLocator locator) throws Exception {
        if (MEMOIZE_SCAN_NODE) {
            ScanNode cachedScanNode = getCached(classLoader, locator);
            if (cachedScanNode != null) {
                log.debug("Using cached ScanNode instance '{}' for ClassLoader '{}' and RootUrlLocator '{}'.",
                        new Object[] { cachedScanNode, classLoader, locator });
                return cachedScanNode;
            }
        }

        ScanNode rootScanNode = new ScanNode();
        Set<URL> roots = locator.findRoots(Thread.currentThread().getContextClassLoader());

        for (URL root : roots) {
            // find the protocol scanner for this root url.
            String protocol = root.getProtocol();
            ProtocolScanner scanner = protocolMap.get(protocol);
            if (scanner == null) {
                if (log.isDebugEnabled()) {
                    log.debug("Could not scan protocol " + protocol + " of url " + root
                            + ", because a scanner for this protocol is not defined.");
                }
                continue;
            }
            scanner.scan(rootScanNode, root);
        }

        if (MEMOIZE_SCAN_NODE) {
            putCached(classLoader, locator, rootScanNode);
        }
        return rootScanNode;
    }

    /**
     * Clears internal cache of ScanNode instances.
     */
    public void clearCache() {
        if (MEMOIZE_SCAN_NODE)
            this.cache.clear();
    }

    /*
     * Returns the cached ScanNode instance mapped by the (classLoader, locator) tuple.
     */
    private ScanNode getCached(ClassLoader classLoader, RootUrlLocator locator) {
        Map<RootUrlLocator, ScanNode> locatorScanNodeMap = cache.get(classLoader);
        if (locatorScanNodeMap != null)
            return locatorScanNodeMap.get(locator);
        return null;
    }

    /*
     * Maps a (ClassLoader,RootUrlLocator) tuple to a ScanNode instance to cache.
     */
    private void putCached(ClassLoader classLoader, RootUrlLocator locator, ScanNode scanNode) {
        Map<RootUrlLocator, ScanNode> locatorScanNodeMap = this.cache.get(classLoader);
        if (locatorScanNodeMap == null) {
            locatorScanNodeMap = Collections.synchronizedMap(new LRUMap(20));
            this.cache.put(classLoader, locatorScanNodeMap);
        }
        locatorScanNodeMap.put(locator, scanNode);
    }

    private boolean isVfsDefined() {
        try {
            Thread.currentThread().getContextClassLoader().loadClass("org.jboss.virtual.VirtualFile");
            // ASSERT: the virtual file class loaded, so we are on jboss 5.
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private boolean isZipDefined() {
        try {
            Thread.currentThread().getContextClassLoader().loadClass("weblogic.utils.zip.ZipURLConnection");
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private boolean isBundleDefined() {
        try {
            Thread.currentThread().getContextClassLoader().loadClass("org.osgi.framework.Bundle");
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}