Java tutorial
/* * Copyright (c) 2000-2004 Netspective Communications LLC. All rights reserved. * * Netspective Communications LLC ("Netspective") permits redistribution, modification and use of this file in source * and binary form ("The Software") under the Netspective Source License ("NSL" or "The License"). The following * conditions are provided as a summary of the NSL but the NSL remains the canonical license and must be accepted * before using The Software. Any use of The Software indicates agreement with the NSL. * * 1. Each copy or derived work of The Software must preserve the copyright notice and this notice unmodified. * * 2. Redistribution of The Software is allowed in object code form only (as Java .class files or a .jar file * containing the .class files) and only as part of an application that uses The Software as part of its primary * functionality. No distribution of the package is allowed as part of a software development kit, other library, * or development tool without written consent of Netspective. Any modified form of The Software is bound by these * same restrictions. * * 3. Redistributions of The Software in any form must include an unmodified copy of The License, normally in a plain * ASCII text file unless otherwise agreed to, in writing, by Netspective. * * 4. The names "Netspective", "Axiom", "Commons", "Junxion", and "Sparx" are trademarks of Netspective and may not be * used to endorse or appear in products derived from The Software without written consent of Netspective. * * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED REPRESENTATIONS AND * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, * ARE HEREBY DISCLAIMED. * * NETSPECTIVE AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE OR ANY THIRD PARTY AS A * RESULT OF USING OR DISTRIBUTING THE SOFTWARE. IN NO EVENT WILL NETSPECTIVE OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, EVEN * IF IT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. */ package com.netspective.commons.xdm; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.apache.commons.discovery.tools.DiscoverClass; import com.netspective.commons.io.FileFind; import com.netspective.commons.io.Resource; import com.netspective.commons.text.TextUtils; import com.netspective.commons.xdm.exception.DataModelException; public class XdmComponentFactory { public static final int XDMCOMPFLAG_ALLOWRELOAD = 1; public static final int XDMCOMPFLAG_ALLOWRELOAD_IF_FILE = XDMCOMPFLAG_ALLOWRELOAD; // synonym, value should be same public static final int XDMCOMPFLAG_CACHE_WHEN_NO_ERRORS = XDMCOMPFLAG_ALLOWRELOAD * 2; public static final int XDMCOMPFLAG_CACHE_ALWAYS = XDMCOMPFLAG_CACHE_WHEN_NO_ERRORS * 2; public static final int XDMCOMPFLAG_INSIDE_ANT = XDMCOMPFLAG_CACHE_ALWAYS * 2; public static final int XDMCOMPFLAGS_DEFAULT = XDMCOMPFLAG_ALLOWRELOAD | XDMCOMPFLAG_CACHE_WHEN_NO_ERRORS; private static DiscoverClass discoverClass = new DiscoverClass(); private static Map componentsBySystemId = Collections.synchronizedMap(new HashMap()); public static Map getComponentsBySystemId() { return componentsBySystemId; } public static XdmComponent getCachedComponent(String systemId, int flags) { XdmComponent component = (XdmComponent) componentsBySystemId.get(systemId); if (component != null) { // If we have a component and we don't want to allow re-loads then we use what we have if ((flags & XDMCOMPFLAG_ALLOWRELOAD) == 0) return component; // If we have a component and we do allow reloads but the source has not changed, then use what we have if (!component.getInputSource().sourceChanged()) return component; // If we get to this point, we have an existing component and we are allowing reloads but the source seems // to have changed; we need to read the entire component again so remove the instance from the map and set // it to null to give the GC a hint to get rid of the instance as soon as possible. component.removedFromCache(componentsBySystemId, systemId, flags); componentsBySystemId.remove(systemId); component = null; // call the garbage-collector at the earliest convenience because all the old components should now be freed System.gc(); } return component; } public static void cacheComponent(String key, XdmComponent component, int flags) { componentsBySystemId.put(key, component); component.addedToCache(componentsBySystemId, key, flags); } public static XdmComponent get(Class componentClass, File file, int flags) throws DataModelException, InvocationTargetException, InstantiationException, IllegalAccessException, FileNotFoundException, NoSuchMethodException { return get(componentClass, file, flags, false); } /** * Factory method for obtaining a particular component from a file. This method will load the appropriate * component file and cache it for future use. If, after caching, the file's input source has changed the file * will automatically be reloaded assuming reload is set to true. * * @param file The file to obtain the content from * @param flags Whether or not to allow reloading if the input source has changed and other flags */ public static XdmComponent get(Class componentClass, File file, int flags, boolean forceReload) throws DataModelException, InvocationTargetException, InstantiationException, IllegalAccessException, FileNotFoundException, NoSuchMethodException { XdmComponent component = forceReload ? null : getCachedComponent(file.getAbsolutePath(), flags); if (component != null) return component; // if we we get to this point, we're parsing an XML file into a given component class XdmParseContext pc = null; List errors = null; List warnings = null; // create a new class instance and hang onto the error list for use later component = (XdmComponent) discoverClass.newInstance(componentClass, componentClass.getName()); errors = component.getErrors(); warnings = component.getWarnings(); // if the class's attributes and model is not known, get it now XmlDataModelSchema.getSchema(componentClass); // parse the XML file long startTime = System.currentTimeMillis(); pc = XdmParseContext.parse(component, file); component.setLoadDuration(startTime, System.currentTimeMillis()); // if we had some syntax errors, make sure the component records them for later use if (pc != null && pc.getErrors().size() != 0) errors.addAll(pc.getErrors()); if (pc != null && pc.getWarnings().size() != 0) warnings.addAll(pc.getWarnings()); component.loadedFromXml(flags); // if there are no errors, cache this component so if the file is needed again, it's available immediately if ((flags & XDMCOMPFLAG_CACHE_ALWAYS) != 0 || (((flags & XDMCOMPFLAG_CACHE_WHEN_NO_ERRORS) != 0) && errors.size() == 0)) cacheComponent(file.getAbsolutePath(), component, flags); return component; } /** * Factory method for obtaining a particular (already-instantiated) component from a file. This method will load * the appropriate component file but not cache it for future use. * * @param file The file to obtain the content from */ public static void load(XdmComponent component, File file) throws DataModelException, FileNotFoundException { // if the class's attributes and model is not known, get it now XmlDataModelSchema.getSchema(component.getClass()); // parse the XML file long startTime = System.currentTimeMillis(); XdmParseContext pc = XdmParseContext.parse(component, file); component.setLoadDuration(startTime, System.currentTimeMillis()); // if we had some syntax errors, make sure the component records them for later use if (pc != null && pc.getErrors().size() != 0) component.getErrors().addAll(pc.getErrors()); if (pc != null && pc.getWarnings().size() != 0) component.getWarnings().addAll(pc.getWarnings()); } public static XdmComponent get(Class componentClass, Resource resource, int flags) throws DataModelException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, NoSuchMethodException { return get(componentClass, resource, flags); } /** * Factory method for obtaining a particular component from a resource. The ClassLoader of the given componentClass * is used to locate the resource. If the resource is actually a file, then this method locates the resource, creates * a File object and calls get(componentClass, File, true). */ public static XdmComponent get(Class componentClass, Resource resource, int flags, boolean forceReload) throws DataModelException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, NoSuchMethodException { // if the resource resolves to an actual file, just treat it as a normal file File resourceFile = resource.getFile(); if (resourceFile != null) return get(componentClass, resourceFile, flags, forceReload); // if we get to here, the resource is being loaded remotely or through a JAR or other source so it's not a physical file XdmParseContext pc = null; XdmComponent component = null; List errors = null; List warnings = null; // create a new class instance and hang onto the error list for use later component = (XdmComponent) discoverClass.newInstance(componentClass, componentClass.getName()); errors = component.getErrors(); warnings = component.getWarnings(); // if the class's attributes and model is not known, get it now XmlDataModelSchema.getSchema(componentClass); // parse the XML file long startTime = System.currentTimeMillis(); pc = XdmParseContext.parse(component, resource); component.setLoadDuration(startTime, System.currentTimeMillis()); // if we had some syntax errors, make sure the component records them for later use if (pc != null && pc.getErrors().size() != 0) errors.addAll(pc.getErrors()); if (pc != null && pc.getWarnings().size() != 0) warnings.addAll(pc.getWarnings()); component.loadedFromXml(flags); return component; } /** * Factory method for obtaining a particular (pre-instantiated) component from a resource. This method will load * the appropriate component file but not cache it for future use. */ public static void load(XdmComponent component, Resource resource) throws IOException, DataModelException, FileNotFoundException { // if the class's attributes and model is not known, get it now XmlDataModelSchema.getSchema(component.getClass()); // parse the XML file long startTime = System.currentTimeMillis(); XdmParseContext pc = XdmParseContext.parse(component, resource); component.setLoadDuration(startTime, System.currentTimeMillis()); // if we had some syntax errors, make sure the component records them for later use if (pc != null && pc.getErrors().size() != 0) component.getErrors().addAll(pc.getErrors()); if (pc != null && pc.getWarnings().size() != 0) component.getWarnings().addAll(pc.getWarnings()); } /** * Factory method for obtaining a particular component from a file that can be found in the classpath (including * JARs and ZIPs on the classpath). */ public static XdmComponent get(Class componentClass, String fileName, int flags, boolean forceReload) throws DataModelException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, NoSuchMethodException, FileNotFoundException { FileFind.FileFindResults ffResults = FileFind.findInClasspath(fileName, FileFind.FINDINPATHFLAG_SEARCH_INSIDE_ARCHIVES_LAST); if (ffResults.isFileFound()) { if (ffResults.isFoundFileInJar()) { ZipFile zipFile = new ZipFile(ffResults.getFoundFile()); ZipEntry zipEntry = zipFile.getEntry(ffResults.getSearchFileName()); String systemId = ffResults.getFoundFile().getAbsolutePath() + "!" + ffResults.getSearchFileName(); XdmComponent component = forceReload ? null : getCachedComponent(systemId, flags); if (component != null) return component; // if we we get to this point, we're parsing an XML file into a given component class XdmParseContext pc = null; List errors = null; List warnings = null; // create a new class instance and hang onto the error list for use later component = (XdmComponent) discoverClass.newInstance(componentClass, componentClass.getName()); errors = component.getErrors(); warnings = component.getWarnings(); // if the class's attributes and model is not known, get it now XmlDataModelSchema.getSchema(componentClass); // parse the XML file long startTime = System.currentTimeMillis(); pc = XdmParseContext.parse(component, ffResults.getFoundFile(), zipEntry); component.setLoadDuration(startTime, System.currentTimeMillis()); // if we had some syntax errors, make sure the component records them for later use if (pc != null && pc.getErrors().size() != 0) errors.addAll(pc.getErrors()); if (pc != null && pc.getWarnings().size() != 0) warnings.addAll(pc.getWarnings()); component.loadedFromXml(flags); // if there are no errors, cache this component so if the file is needed again, it's available immediately if ((flags & XDMCOMPFLAG_CACHE_ALWAYS) != 0 || (((flags & XDMCOMPFLAG_CACHE_WHEN_NO_ERRORS) != 0) && errors.size() == 0)) cacheComponent(systemId, component, flags); return component; } else return get(componentClass, ffResults.getFoundFile(), flags, forceReload); } else throw new FileNotFoundException("File '" + fileName + "' not found in classpath. Searched: " + TextUtils.getInstance().join(ffResults.getSearchPaths(), ", ")); } }