com.tangosol.io.pof.generator.PofConfigGenerator Maven / Gradle / Ivy
Show all versions of coherence Show documentation
/*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
package com.tangosol.io.pof.generator;
import com.tangosol.dev.introspect.ClassAnnotationSeeker;
import com.tangosol.dev.introspect.ClassPathResourceDiscoverer.InformedResourceDiscoverer;
import com.tangosol.io.pof.annotation.Portable;
import com.tangosol.run.xml.XmlElement;
import com.tangosol.run.xml.XmlHelper;
import com.tangosol.util.Base;
import com.tangosol.util.FilterEnumerator;
import com.tangosol.util.extractor.IdentityExtractor;
import com.tangosol.util.extractor.KeyExtractor;
import com.tangosol.util.filter.InFilter;
import com.tangosol.util.filter.NotFilter;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import static com.tangosol.io.pof.ConfigurablePofContext.DEFAULT_RESOURCE;
import static com.tangosol.io.pof.ConfigurablePofContext.mergeIncludes;
import static com.tangosol.util.Base.azzert;
/**
* PofConfigGenerator is a utility class allowing the generation of a POF
* configuration file based on the provided {@link Dependencies}. The
* generation is influenced by various configuration items present in the
* Dependencies object. The high level objective of this class is to
* generate the POF configuration based upon all known / discoverable classes
* annotated with the {@link Portable} annotation. The generation is capable
* of determining unallocated user type ids and allocating appropriately.
* This class is envisaged to be used against various generations thus
* supports backwards compatibility by specifying a previously defined POF
* configuration file ensuring all previously allocated type ids are
* respected.
*
* This class is a can be executed as follows:
*
*
* new PofConfigGenerator(new Dependencies()).generate()
*
*
* There are various configuration items that affect the generation and are
* described within the {@link Dependencies} object.
*
* @author hr 2012.07.04
*
* @since Coherence 12.1.2
*
* @see PofConfigGenerator.Dependencies
* @see GarPofConfigGenerator
* @see Executor
*/
public class PofConfigGenerator
{
// ----- constructors ---------------------------------------------------
/**
* Construct a PofConfigGenerator with the provided dependencies.
*
* @param deps the configuration items used by this generator
*/
public PofConfigGenerator(Dependencies deps)
{
azzert(deps != null, "PofConfigGenerator requires dependencies");
deps.validate();
m_deps = deps.clone();
}
// ----- PofConfigGenerator methods -------------------------------------
/**
* Executes the various phases of generating a POF Configuration file.
* The following phases describe the lifecycle of this class:
*
* - ensureClassLoader - based on the
* {@link Dependencies#getPathRoot() PathRoot} determine the
* appropriate {@link ClassLoader} and / or populate the list of
* class path entries via {@link #setListClassPath(List)}.
* - ensureReservedUserTypes - determine the reserved
* user types returning a map of class info by type id.
* - scan - given the reserved user types scan the
* appropriate resources for classes annotated with {@link Portable}.
*
- generateXml - based on the reserved and discovered
* user types build an XML document.
*
- persist - persist the generated XML.
*
*/
public void generate()
{
ClassLoader loader = ensureClassLoader();
Map mapReserved = ensureReservedUserTypes(loader);
Map mapDiscovered = discoverUserTypes(mapReserved, loader);
// inject reserved types that should be carried over
for (Map.Entry entry : mapReserved.entrySet())
{
TypeInfo type = entry.getValue();
if (type.m_fExport)
{
mapDiscovered.put(entry.getKey(), type);
}
}
persist(generateXml(mapDiscovered));
}
// ----- accessors ------------------------------------------------------
/**
* Returns the path the XML file was written to.
*
* @return the path the XML file was written to
*/
public String getWrittenPath()
{
return m_sWrittenPath;
}
/**
* The {@link Dependencies} this class is configured with.
*
* @return The {@link Dependencies} this class is configured with
*/
public Dependencies getDependencies()
{
return m_deps;
}
/**
* Specify the {@link Dependencies} this class should use.
*
* @param deps the {@link Dependencies} this class should use
*/
public void setDependencies(Dependencies deps)
{
m_deps = deps;
}
/**
* Sets all the {@link URL}s used to scope the scanning of annotations.
*
* @param listClassPath the {@link URL}s used to scope the scanning of
* annotations
*/
public void setListClassPath(List listClassPath)
{
m_listClassPath = listClassPath;
}
// ----- lifecycle methods ----------------------------------------------
/**
* Based on the {@link Dependencies#getPathRoot() PathRoot} configuration
* item return a class loader encompassing the contents within
* {@literal PathRoot}. There are four states {@literal PathRoot} may
* hold which results in different outcomes. These states are as
* follows:
*
* - empty - returns that {@link ClassLoader} this class is
* executing under.
* - directory - iff the PathRoot is a directory gather all
* jar files within the directory, including the directory itself,
* creating and returning an appropriate {@link ClassLoader}.
* - jar file - iff PathRoot refers to a jar file use that
* jar file as the class loader.
* - delimited list of the above - a file system path
* separator delimited list of the above is accepted and each entry
* is added to the class path and class loader using the rules
* mentioned above.
*
* In the last three states the resolved directory / jars are added to the
* list of class path entries via {@link #setListClassPath(List)}.
*
* The location of Coherence classes is also added to the class loader.
*
* @return a derived {@link ClassLoader}
*/
protected ClassLoader ensureClassLoader()
{
List listRoots = m_deps.getPathRoot();
if (listRoots.isEmpty())
{
return Base.getContextClassLoader();
}
List listClassPath = extractClassPath(listRoots);
// add coherence.jar to the class loader
ProtectionDomain domain = getClass().getProtectionDomain();
CodeSource codeSrc = domain == null ? null : domain.getCodeSource();
URL url = codeSrc == null ? null : codeSrc.getLocation();
if (url != null)
{
try
{
url = toJarUrl(url);
}
catch (MalformedURLException e) {}
listClassPath.add(url);
}
setListClassPath(listClassPath);
return new URLClassLoader(listClassPath.toArray(new URL[0]));
}
/**
* Determine predefined POF type ids using the provided
* {@link ClassLoader} and the specified
* {@link Dependencies#getPofConfig() PofConfig}, returning a map of
* {@link TypeInfo} objects by type id.
*
* @param loader the ClassLoader used to load the pof config
*
* @return a map of TypeInfo objects by user type id
*/
protected Map ensureReservedUserTypes(ClassLoader loader)
{
Dependencies deps = m_deps;
String sPofConfig = deps.getPofConfig();
boolean fInclude = deps.isInclude();
sPofConfig = sPofConfig == null ? DEFAULT_RESOURCE : sPofConfig;
XmlElement xmlConfig = XmlHelper.loadFileOrResource(sPofConfig, "POF configuration", loader);
XmlElement[] axmlUserTypes = new XmlElement[2];
XmlElement xmlConfigGen = (XmlElement) xmlConfig.clone();
axmlUserTypes[0] = xmlConfigGen.getSafeElement("user-type-list");
// merge the referenced includes in a single user-type-list
mergeIncludes(sPofConfig, xmlConfig, loader);
Map mapReserved = new HashMap();
axmlUserTypes[1] = xmlConfig.getSafeElement("user-type-list");
StringBuilder sbErrors = new StringBuilder();
for (int i = fInclude ? 1 : 0; i < axmlUserTypes.length; ++i)
{
XmlElement xmlUserTypes = axmlUserTypes[i];
Set setTypeIds = new HashSet();
for (Iterator iterUserTypes = xmlUserTypes.getElements("user-type"); iterUserTypes.hasNext(); )
{
XmlElement xmlUserType = iterUserTypes.next();
XmlElement xmlTypeId = xmlUserType.getSafeElement("type-id");
XmlElement xmlClassName = xmlUserType.getSafeElement("class-name");
int nTypeId = xmlTypeId.getInt(-1);
String sClassName = xmlClassName.getString();
if (nTypeId < 0)
{
sbErrors.append("\t contains a"
+ " that has a missing or invalid type"
+ " ID value: " + xmlTypeId.getString(null));
}
if (sClassName.isEmpty())
{
sbErrors.append("\tMissing class name for type-id: " + nTypeId);
}
Integer ITypeId = nTypeId;
if (setTypeIds.contains(ITypeId))
{
sbErrors.append("\tDuplicate user type id: " + nTypeId);
}
else if (!mapReserved.containsKey(ITypeId))
{
// we only consider the first round of user-types (the virtual
// user-types) as those that should contribute to the generated
// pof configuration
setTypeIds.add(ITypeId);
mapReserved.put(ITypeId, new TypeInfo(sClassName, i == 0, xmlUserType));
}
}
}
if (sbErrors.length() > 0)
{
throw new IllegalStateException("Encountered the following errors when parsing POF Configuration "
+ sPofConfig + ":\n" + sbErrors.toString());
}
// remove existing user-types (and includes iff generating a delta)
XmlElement xmlUserTypes = xmlConfigGen.getSafeElement("user-type-list");
for (Iterator iter = xmlUserTypes.getElementList().iterator(); iter.hasNext(); )
{
XmlElement xmlChild = iter.next();
if (!fInclude && xmlChild.getName().equals("include"))
{
continue;
}
iter.remove();
}
m_xmlConfig = xmlConfigGen;
return mapReserved;
}
/**
* Use an annotation scanner to scan for all classes annotated with the
* {@link Portable} annotation. Use the {@literal mapReservedTypes} to
* distinguish predefined user types that can be discarded by the
* annotation scanner. This class is also responsible for allocating each
* discovered class a unique user type id whilst preserving allocated
* user type ids.
*
* @param mapReservedTypes all discovered classes annotated with
* {@link Portable}
* @param loader the {@link ClassLoader}
*
* @return a map of discovered user types
*/
protected Map discoverUserTypes(Map mapReservedTypes, ClassLoader loader)
{
Dependencies deps = m_deps;
List listClassPath = m_listClassPath;
Iterator iterTypeId = new TypeIdIterator(mapReservedTypes.keySet(), deps.getStartTypeId());
ClassAnnotationSeeker.Dependencies seekerDeps = new ClassAnnotationSeeker.Dependencies()
.setFilter(new NotFilter(new InFilter(new KeyExtractor(), extractClassNames(mapReservedTypes))))
.setPackages(deps.getPackages());
if (listClassPath != null && !listClassPath.isEmpty())
{
seekerDeps.setDiscoverer(new InformedResourceDiscoverer(listClassPath.toArray(new URL[listClassPath.size()])));
}
seekerDeps.setContextClassLoader(loader);
List listClassNames = new ArrayList(
new ClassAnnotationSeeker(seekerDeps).findClassNames(Portable.class));
Collections.sort(listClassNames);
Map mapDiscoveredTypes = new TreeMap();
for (String sClassName : listClassNames)
{
mapDiscoveredTypes.put(iterTypeId.next(), new TypeInfo(sClassName, true));
}
return mapDiscoveredTypes;
}
/**
* Based on the provided user types generate an XML document
* incorporating all include references.
*
* @param mapAllUserTypes map of all user types that should be persisted
*
* @return the generated XML document
*/
protected XmlElement generateXml(Map mapAllUserTypes)
{
Dependencies deps = m_deps;
XmlElement xmlConfig = m_xmlConfig;
XmlElement xmlUserTypes = xmlConfig.ensureElement("user-type-list");
// when generating a delta configuration based on an original pof
// configuration, i.e. complimenting opposed to overwriting, we must
// reference what we compliment
if (deps.isInclude())
{
String sPofConfig = deps.getPofConfig();
File filePofConfig = new File(sPofConfig);
sPofConfig = filePofConfig.exists() ? filePofConfig.getName() : sPofConfig;
xmlUserTypes.ensureElement("include").setString(sPofConfig);
}
for (Map.Entry entry : mapAllUserTypes.entrySet())
{
TypeInfo type = entry.getValue();
XmlElement xmlUserType = type.m_xmlUserType;
if (xmlUserType == null)
{
// this type was discovered
xmlUserType = xmlUserTypes.addElement("user-type");
XmlElement xmlTypeId = xmlUserType.ensureElement("type-id");
XmlElement xmlClassName = xmlUserType.ensureElement("class-name");
xmlTypeId.setInt(entry.getKey());
xmlClassName.setString(type.m_sClassName);
}
else
{
// this type was pre-existing
xmlUserTypes.getElementList().add(xmlUserType);
}
}
return xmlConfig;
}
/**
* Persist the passed Xml document to an appropriate file. The file used
* is specified by {@link Dependencies#getOutputPath() OutputPath}
* which may have the following representations:
*
* - file - the filename to persist the generated POF
* configuration to. The file or the directory containing the file
* must be writable. In the former case the file is overwritten.
* - directory - the directory to persist a new file
* with the following format: {@literal (pof-config.xml |
* pof-config-{n}.xml)} where n is in the range of 1 -255 used based
* on the presence of a preexisting file.
*
*
* @param xmlConfig the generated XML POF configuration to persist
*/
protected void persist(XmlElement xmlConfig)
{
String sOutputPath = m_deps.getOutputPath();
File fileOut = sOutputPath == null || sOutputPath.isEmpty()
? new File(".") : new File(sOutputPath);
try
{
if (fileOut.isDirectory())
{
fileOut = ensureUniqueFile(fileOut, getDefaultPofConfigFileName());
}
else
{
fileOut.createNewFile();
}
if (!fileOut.canWrite())
{
throw new IllegalStateException("Insufficient permissions to write to file: " + fileOut);
}
xmlConfig.writeXml(new PrintWriter(fileOut), true);
m_sWrittenPath = fileOut.getCanonicalPath();
}
catch (IOException e)
{
throw new IllegalStateException("Error in writing file: " + fileOut, e);
}
}
// ----- helpers --------------------------------------------------------
/**
* Interrogate the path root reference provided, adding acceptable files
* to the {@link List} of {@link URL}s.
*
* @param listRoots a list of locations to contribute to convert to URLs
*
* @return the given list of roots converted to a list of URLs
*/
protected List extractClassPath(List listRoots)
{
List listClassPath = new ArrayList(listRoots.size());
for (String sFile : listRoots)
{
File file = new File(sFile);
if (!file.exists())
{
throw new IllegalArgumentException("Path in root is invalid: " + sFile);
}
else if (file.isFile())
{
String sFileName = file.getName();
int iPosExt = sFileName.lastIndexOf('.');
String sExtension = iPosExt < 0 ? "" : sFileName.substring(iPosExt);
if (!sExtension.endsWith("jar"))
{
throw new IllegalArgumentException("Root location refers to a file without a jar extension: " + sFileName);
}
try
{
listClassPath.add(toJarUrl(file));
}
catch (MalformedURLException e)
{
throw new IllegalArgumentException("Root location can not be referred to: " + sFileName);
}
}
else if (file.isDirectory())
{
String sErrorHead = "The following file descriptors could not be referred to:\n";
StringBuilder sbldrErrors = new StringBuilder(sErrorHead);
try
{
listClassPath.add(file.toURI().toURL());
}
catch (MalformedURLException e)
{
sbldrErrors.append("\t" + file);
}
File[] afileJars = file.listFiles(new FilenameFilter()
{
public boolean accept(File dir, String name)
{
return name.endsWith(".jar");
}
});
for (File fileJar : afileJars)
{
try
{
listClassPath.add(toJarUrl(fileJar));
}
catch (MalformedURLException e)
{
sbldrErrors.append("\t" + fileJar);
}
}
if (sbldrErrors.length() > sErrorHead.length())
{
throw new IllegalArgumentException(sbldrErrors.toString());
}
}
}
return listClassPath;
}
/**
* Given a directory and a file name ensure a unique file is created and
* returned.
*
* @param fileDir the base directory
* @param sName the name of the file
*
* @return a unique file
*
* @throws IOException iff a file could not be created
*/
protected File ensureUniqueFile(File fileDir, String sName)
throws IOException
{
File fileAttempt;
File fileOut = null;
String sFileName = sName;
int iInject = sName.lastIndexOf('.');
iInject = iInject == -1 ? sName.length() : iInject;
String sPrefix = sName.substring(0, iInject) + "-";
String sSuffix = sName.substring(iInject, sName.length());
for (int i = 0; i < 255;)
{
fileAttempt = new File(fileDir, sFileName);
if (!fileAttempt.exists())
{
fileOut = fileAttempt;
break;
}
sFileName = sPrefix + ++i + sSuffix;
}
if (fileOut == null)
{
// fall back to random number injection into the filename
fileOut = File.createTempFile(sPrefix, sSuffix, fileDir);
}
else
{
fileOut.createNewFile();
}
return fileOut;
}
/**
* Given the map of {@link TypeInfo} objects by user type id return a
* set of class names.
*
* @param mapTypes map of user types used to extract class names from
*
* @return a distinct collection of class names
*/
protected Set extractClassNames(Map mapTypes)
{
Set setClassNames = new HashSet(mapTypes.size());
for (Map.Entry entry : mapTypes.entrySet())
{
setClassNames.add(entry.getValue().m_sClassName);
}
return setClassNames;
}
/**
* Based on a {@link File} create a JAR {@link URL}.
*
* @param file the JAR file
*
* @return a {@link URL} using a jar protocol
*
* @throws MalformedURLException
*/
protected URL toJarUrl(File file)
throws MalformedURLException
{
return toJarUrl(file.toURI().toURL());
}
/**
* Based on a {@link URL} create a JAR URL.
*
* @param url the original url
*
* @return a URL using the jar protocol
*
* @throws MalformedURLException
*/
protected URL toJarUrl(URL url)
throws MalformedURLException
{
String sExternal = url.toExternalForm();
return url.getProtocol().equals("jar") || !sExternal.endsWith(".jar")
? url : new URL("jar:" + sExternal + "!/");
}
/**
* Returns the default POF configuration file name to use when one is
* not specified or a directory is specified.
*
* @return the default POF configuration file name
*/
protected String getDefaultPofConfigFileName()
{
return DEFAULT_POF_CONFIG_FILE_NAME;
}
// ----- inner class: TypeInfo ------------------------------------------
/**
* A TypeInfo represents a user type based on it's class name, whether it
* should be present in any generated XML files and an original XML
* description of the user type.
*/
protected class TypeInfo
{
// ----- constructors -----------------------------------------------
/**
* Construct a TypeInfo with a class name and whether it should be
* present in any generated XML files.
*
* @param sClassName the name of the class
* @param fExport whether the user type should be present in any
* generated xml files
*/
protected TypeInfo(String sClassName, boolean fExport)
{
this(sClassName, fExport, null);
}
/**
* Construct a TypeInfo with a class name and whether it should be
* present in any generated xml files.
*
* @param sClassName the name of the class
* @param fExport whether the user type should be present in any
* generated XML files
*/
protected TypeInfo(String sClassName, boolean fExport, XmlElement xmlUserType)
{
m_sClassName = sClassName;
m_fExport = fExport;
m_xmlUserType = fExport ? xmlUserType : null;
}
// ----- data members -----------------------------------------------
/**
* The class name of the represented type.
*/
protected String m_sClassName;
/**
* Whether to export this type into the generated POF Configuration
* file.
*/
protected boolean m_fExport;
/**
* The original XML definition of a user type.
*/
protected XmlElement m_xmlUserType;
}
// ----- inner class: TypeIdIterator ------------------------------------
/**
* An {@link Iterator} implementation that understands allocated user
* type ids thus able to navigate around these allocated blocks.
*/
protected class TypeIdIterator
extends FilterEnumerator
{
// ----- constructors -----------------------------------------------
/**
* Construct a TypeIdIterator with the provided reservations and a
* starting type id of {@literal 1000}.
*
* @param setReservedTypeIds pre-allocated type ids
*/
protected TypeIdIterator(Set setReservedTypeIds)
{
this(setReservedTypeIds, 1000);
}
/**
* Construct a TypeIdIterator with the provided reservations.
*
* @param setReservedTypeIds pre-allocated type ids
* @param nStartTypeId the user type id to start allocating type
* ids from
*/
protected TypeIdIterator(Set setReservedTypeIds, final int nStartTypeId)
{
super(new Iterator()
{
@Override
public boolean hasNext()
{
int nCurrentTypeId = m_nCurrentTypeId;
return nCurrentTypeId >= 0 && nCurrentTypeId < Integer.MAX_VALUE;
}
@Override
public Object next()
{
return m_nCurrentTypeId++;
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
protected int m_nCurrentTypeId = nStartTypeId;
}, new NotFilter(new InFilter(IdentityExtractor.INSTANCE, setReservedTypeIds)));
}
}
// ----- inner class: Dependencies --------------------------------------
/**
* All dependencies of this class. None of the attributes are mandatory.
* Below is a description of the properties of this object which affect
* the POF configuration generation:
*
* Configuration Item Influence Default
* {@link Dependencies#getOutputPath() OutputPath}
* The destination of the generated file. Iff this is a file
* reference will the file be overwritten otherwise a file is
* placed in the referenced directory with the format
* {@literal (pof-config.xml | pof-config-{n}.xml)} where n is in
* the range of 1 -255 used based on the presence of a
* preexisting file.
* Current working directory.
* {@link Dependencies#getPathRoot() PathRoot}
* A list of locations used to scan for annotated classes. Each
* location may be a jar file, a directory containing jar files
* or a root directory of classes.
* The class path this class is executing under.
* {@link Dependencies#getPofConfig() PofConfig}
* A previous POF configuration file. This file is used to
* determine reserved type ids and associated classes. The user
* types defined in this file are propagated to the generated
* POF configuration.
*
* Iff present JVM argument {@literal tangosol.pof.confg} or
* {@literal pof-config.xml}.
* {@link Dependencies#getPackages() Packages}
* A set of packages to refine the search for annotated classes.
*
* All packages.
* {@link Dependencies#isInclude() Include}
* Iff set to true the generator will generate a delta including
* all POF user types not present in the provided
* {@link Dependencies#getPofConfig() PofConfig} file but
* discovered as a part of the annotation scanning process. The
* provided {@literal PofConfig} configuration file will be
* referenced as an include in the generated configuration. Iff
* this is an absolute path, thus can be referenced as a
* {@link File}, only the file name will be used with the
* assumption that the file will exist at the root of the class
* path.
* false.
* {@link Dependencies#getStartTypeId()} StartTypeId}
* The user type id to start allocations from.
* 1000.
*
*/
public static class Dependencies
{
// ----- constructors -----------------------------------------------
/**
* Default constructor initializing a Dependencies object with
* default values.
*/
public Dependencies()
{
m_fInclude = false;
}
/**
* Copy constructor cloning the provided Dependencies into this
* instance.
*
* @param deps the dependencies to copy from
*/
public Dependencies(Dependencies deps)
{
m_sPofConfig = deps.getPofConfig();
m_listRoots = deps.getPathRoot();
m_sOutputPath = deps.getOutputPath();
m_fInclude = deps.isInclude();
m_setPackages = new HashSet(deps.getPackages());
m_nStartTypeId = deps.getStartTypeId();
}
// ----- accessors --------------------------------------------------
/**
* Returns the location of the POF Configuration file to use as the
* previous generation. This file is used to
* determine reserved type ids and associated classes. The user
* types defined in this file are propagated to the generated
* POF configuration if {@link #isInclude()}{@code == false}.
*
* Default: Iff present JVM argument
* {@literal tangosol.pof.confg} or {@literal pof-config.xml}.
*
* @return the path to the POF configuration of the previous
* generation
*/
public String getPofConfig()
{
return m_sPofConfig;
}
/**
* Sets the path to POF configuration of a previous generation or of
* base types to include.
*
* @param sPofConfig path to POF configuration of a previous
* generation
*
* @return a reference to this Dependencies object
*/
public Dependencies setPofConfig(String sPofConfig)
{
m_sPofConfig = sPofConfig;
return this;
}
/**
* Returns a list of locations used to scan for annotated classes.
* Each location may be a jar file, a directory containing jar files
* or a root directory of classes.
*
* Default: The class path this class is executing under.
*
* @return a reference to this Dependencies object
*/
public List getPathRoot()
{
return m_listRoots;
}
/**
* Sets a list of locations used to scan for annotated classes.
*
* @param listRoots a list of locations used to scan for annotated
* classes
*
* @return a reference to this Dependencies object
*/
public Dependencies setPathRoot(List listRoots)
{
azzert(listRoots != null, "PofConfigGenerator.Dependencies can not have a null path root");
m_listRoots = listRoots;
return this;
}
/**
* The destination of the generated file. Iff this is a file
* reference will the file be overwritten otherwise a file is
* placed in the referenced directory with the format
* {@literal (pof-config.xml | pof-config-{n}.xml)} where n is in
* the range of 1 - 255 used based on the presence of a
* preexisting file.
*
* Default: Current working directory.
*
* @return destination of the generated file
*/
public String getOutputPath()
{
return m_sOutputPath;
}
/**
* Sets the destination of the generated file.
*
* @param sOutputPath destination of the generated file
*
* @return a reference to this Dependencies object
*/
public Dependencies setOutputPath(String sOutputPath)
{
m_sOutputPath = sOutputPath;
return this;
}
/**
* A set of packages to refine the search for annotated classes.
*
* @return set of packages to refine the search for annotated classes
*/
public Set getPackages()
{
return m_setPackages;
}
/**
* Sets the set of packages to refine the search for annotated
* classes.
*
* @param setPackages set of packages to refine the search for
* annotated classes
*
* @return a reference to this Dependencies object
*/
public Dependencies setPackages(Set setPackages)
{
azzert(setPackages != null, "PofConfigGenerator.Dependencies can not have a null set of packages");
m_setPackages = setPackages;
return this;
}
/**
* Adds a package to a set of packages to refine the search for
* annotated classes.
*
* @param sPackage add the passed package to the set of packages to
* scan
*
* @return a reference to this Dependencies object
*/
public Dependencies addPackage(String sPackage)
{
m_setPackages.add(sPackage);
return this;
}
/**
* Returns whether to generate a delta with an include referring to
* the previous configuration.
*
* Iff set to true the generator will generate a delta including
* all POF user types not present in the provided
* {@link Dependencies#getPofConfig() PofConfig} file but
* discovered as a part of the annotation scanning process. The
* provided {@literal PofConfig} configuration file will be
* referenced as an include in the generated configuration. Iff
* this is an absolute path, thus can be referenced as a
* {@link File}, only the file name will be used with the
* assumption that the file will exist at the root of the class
* path.
*
* Default: false.
*
* @return whether to generate a delta with an include
*/
public boolean isInclude()
{
return m_fInclude;
}
/**
* Sets whether to generate a delta with an include referring to
* the previous configuration.
*
* @param fInclude whether to generate a delta with an include
*
* @return a reference to this Dependencies object
*/
public Dependencies setInclude(boolean fInclude)
{
m_fInclude = fInclude;
return this;
}
/**
* Returns a user type id to start allocations from.
*
* Default: 1000.
*
* @return a user type id to start allocations from
*/
public int getStartTypeId()
{
return m_nStartTypeId;
}
/**
* Sets a user type id to start allocations from.
*
* @param nStartTypeId a user type id to start allocations from
*
* @return a reference to this Dependencies object
*/
public Dependencies setStartTypeId(Integer nStartTypeId)
{
m_nStartTypeId = nStartTypeId == null ? m_nStartTypeId : nStartTypeId;
return this;
}
// ----- helpers ----------------------------------------------------
/**
* Validates this object ensuring any defaults are populated and
* mandatory attributes are specified.
*/
public void validate()
{
String sPofConfig = m_sPofConfig;
if (sPofConfig == null || sPofConfig.isEmpty())
{
sPofConfig = m_sPofConfig = DEFAULT_RESOURCE;
}
}
// ----- object methods ---------------------------------------------
/**
* {@inheritDoc}
*/
@Override
protected Dependencies clone()
{
return new Dependencies(this);
}
/**
* {@inheritDoc}
*/
@Override
public String toString()
{
return "PofConfigGenerator.Dependencies{" +
"pathRoot = '" + m_listRoots + '\'' +
", outputPath = '" + m_sOutputPath + '\'' +
", pofConfig = '" + m_sPofConfig + '\'' +
", include = " + m_fInclude +
", packages = " + m_setPackages +
", startTypeId = " + m_nStartTypeId +
'}';
}
/**
* Original POF Configuration file. This file is read only and must
* be respected in any type id allocations.
*/
protected String m_sPofConfig;
/**
* The root location to search for annotated classes.
*/
protected List m_listRoots = new ArrayList();
/**
* The output path for the generated XML.
*/
protected String m_sOutputPath;
/**
* The packages to include in the class discovery stage.
*/
protected Set m_setPackages = new HashSet();
/**
* An indication to the generator to generate a delta POF
* configuration, complimenting the original POF configuration with
* an include element referring to the original.
*/
protected boolean m_fInclude;
/**
* A type id to start the allocations from.
*/
protected int m_nStartTypeId = 1000;
}
// ----- constants ------------------------------------------------------
/**
* The default filename used when generating the POF configuration and
* a file is not specified.
*/
protected static final String DEFAULT_POF_CONFIG_FILE_NAME = "pof-config.xml";
// ----- data members ---------------------------------------------------
/**
* The required dependencies for the execution of this class.
*/
protected Dependencies m_deps;
/**
* The generated XML.
*/
protected XmlElement m_xmlConfig;
/**
* The location the generated file was written to.
*/
protected String m_sWrittenPath;
/**
* A list of URLs used to build a {@link ClassLoader}. This is only
* populated when a root location referencing a directory or a jar is
* specified. The list of URLs allows an isolated resource discovery
* phase which provides better scoping and performance.
*/
protected List m_listClassPath;
}