FileDocCategorySizeDatePackage
ModifiedSelector.javaAPI DocApache Ant 1.7031218Wed Dec 13 06:16:20 GMT 2006org.apache.tools.ant.types.selectors.modifiedselector

ModifiedSelector

public class ModifiedSelector extends org.apache.tools.ant.types.selectors.BaseExtendSelector implements org.apache.tools.ant.BuildListener, org.apache.tools.ant.types.resources.selectors.ResourceSelector

Selector class that uses Algorithm, Cache and Comparator for its work. The Algorithm is used for computing a hashvalue for a file. The Comparator decides whether to select or not. The Cache stores the other value for comparison by the Comparator in a persistent manner.

The ModifiedSelector is implemented as a CoreSelector and uses default values for all its attributes therefore the simpliest example is

<copy todir="dest">
<filelist dir="src">
<modified/>
</filelist>
</copy>

The same example rewritten as CoreSelector with setting the all values (same as defaults are) would be

<copy todir="dest">
<filelist dir="src">
<modified update="true"
cache="propertyfile"
algorithm="digest"
comparator="equal">
<param name="cache.cachefile" value="cache.properties"/>
<param name="algorithm.algorithm" value="MD5"/>
</modified>
</filelist>
</copy>

And the same rewritten as CustomSelector would be

<copy todir="dest">
<filelist dir="src">
<custom class="org.apache.tools.ant.type.selectors.ModifiedSelector">
<param name="update" value="true"/>
<param name="cache" value="propertyfile"/>
<param name="algorithm" value="digest"/>
<param name="comparator" value="equal"/>
<param name="cache.cachefile" value="cache.properties"/>
<param name="algorithm.algorithm" value="MD5"/>
</custom>
</filelist>
</copy>

If you want to provide your own interface implementation you can do that via the *classname attributes. If the classes are not on Ant's core classpath, you will have to provide the path via nested <classpath> element, so that the selector can find the classes.

<modified cacheclassname="com.mycompany.MyCache">
<classpath>
<pathelement location="lib/mycompony-antutil.jar"/>
</classpath>
</modified>

All these three examples copy the files from src to dest using the ModifiedSelector. The ModifiedSelector uses the PropertyfileCache , the DigestAlgorithm and the EqualComparator for its work. The PropertyfileCache stores key-value-pairs in a simple java properties file. The filename is cache.properties. The update flag lets the selector update the values in the cache (and on first call creates the cache). The DigestAlgorithm computes a hashvalue using the java.security.MessageDigest class with its MD5-Algorithm and its standard provider. The new computed hashvalue and the stored one are compared by the EqualComparator which returns 'true' (more correct a value not equals zero (1)) if the values are not the same using simple String comparison.

A useful scenario for this selector is inside a build environment for homepage generation (e.g. with Apache Forrest).

<target name="generate-and-upload-site">
<echo> generate the site using forrest </echo>
<antcall target="site"/>

<echo> upload the changed files </echo>
<ftp server="${ftp.server}" userid="${ftp.user}" password="${ftp.pwd}">
<fileset dir="htdocs/manual">
<modified/>
</fileset>
</ftp>
</target>
Here all changed files are uploaded to the server. The ModifiedSelector saves therefore much upload time.

This selector uses reflection for setting the values of its three interfaces (using org.apache.tools.ant.IntrospectionHelper) therefore no special 'configuration interfaces' has to be implemented by new caches, algorithms or comparators. All present setXX methods can be used. E.g. the DigestAlgorithm can use a specified provider for computing its value. For selecting this there is a setProvider(String providername) method. So you can use a nested <param name="algorithm.provider" value="MyProvider"/>.

since
Ant 1.6

Fields Summary
private CacheName
cacheName
Cache name for later instantiation.
private String
cacheClass
User specified classname for Cache.
private AlgorithmName
algoName
Algorithm name for later instantiation.
private String
algorithmClass
User specified classname for Algorithm.
private ComparatorName
compName
Comparator name for later instantiation.
private String
comparatorClass
User specified classname for Comparator.
private boolean
update
Should the cache be updated?
private boolean
selectDirectories
Are directories selected?
private boolean
selectResourcesWithoutInputStream
Should Resources whithout an InputStream, and therefore without checking, be selected?
private boolean
delayUpdate
Delay the writing of the cache file
private Comparator
comparator
How should the cached value and the new one compared?
private Algorithm
algorithm
Algorithm for computing new values and updating the cache.
private Cache
cache
The Cache containing the old values.
private int
modified
Count of modified properties
private boolean
isConfigured
Flag whether this object is configured. Configuration is only done once.
private Vector
configParameter
Parameter vector with parameters for later initialization.
private Vector
specialParameter
Parameter vector with special parameters for later initialization. The names have the pattern '*.*', e.g. 'cache.cachefile'. These parameters are used after the parameters with the pattern '*'.
private ClassLoader
myClassLoader
The classloader of this class.
private org.apache.tools.ant.types.Path
classpath
provided classpath for the classloader
Constructors Summary
public ModifiedSelector()
Bean-Constructor.



    // -----  constructors  -----


      
      
    
Methods Summary
public voidaddClasspath(org.apache.tools.ant.types.Path path)
Add the classpath.

param
path the classpath

        if (classpath != null) {
            throw new BuildException("<classpath> can be set only once.");
        }
        classpath = path;
    
public voidaddParam(java.lang.String key, java.lang.Object value)
Support for nested <param> tags.

param
key the key of the parameter
param
value the value of the parameter

        Parameter par = new Parameter();
        par.setName(key);
        par.setValue(String.valueOf(value));
        configParameter.add(par);
    
public voidaddParam(org.apache.tools.ant.types.Parameter parameter)
Support for nested <param> tags.

param
parameter the parameter object

        configParameter.add(parameter);
    
public voidbuildFinished(org.apache.tools.ant.BuildEvent event)
Signals that the last target has finished.

param
event recieved BuildEvent

        if (getDelayUpdate()) {
            saveCache();
        }
    
public voidbuildStarted(org.apache.tools.ant.BuildEvent event)
Signals that a build has started.

param
event recieved BuildEvent

        // no-op
    
public voidconfigure()
Configures this Selector. Does this work only once per Selector object.

Because some problems while configuring from Selector the configuration is done in the following order:

  1. collect the configuration data
  2. wait for the first isSelected() call
  3. set the default values
  4. set values for name pattern '*': update, cache, algorithm, comparator
  5. set values for name pattern '*.*: cache.cachefile, ...

This configuration algorithm is needed because you don't know the order of arriving config-data. E.g. if you first set the cache.cachefilename and after that the cache itself, the default value for cachefilename is used, because setting the cache implies creating a new Cache instance - with its defaults.

        //
        // -----  The "Singleton"  -----
        //
        if (isConfigured) {
            return;
        }
        isConfigured = true;

        //
        // -----  Set default values  -----
        //
        Project p = getProject();
        String filename = "cache.properties";
        File cachefile = null;
        if (p != null) {
            // normal use inside Ant
            cachefile = new File(p.getBaseDir(), filename);

            // set self as a BuildListener to delay cachefile saves
            getProject().addBuildListener(this);
        } else {
            // no reference to project - e.g. during normal JUnit tests
            cachefile = new File(filename);
            setDelayUpdate(false);
        }
        Cache      defaultCache      = new PropertiesfileCache(cachefile);
        Algorithm  defaultAlgorithm  = new DigestAlgorithm();
        Comparator defaultComparator = new EqualComparator();
        update = true;
        selectDirectories = true;

        //
        // -----  Set the main attributes, pattern '*'  -----
        //
        for (Iterator itConfig = configParameter.iterator(); itConfig.hasNext();) {
            Parameter par = (Parameter) itConfig.next();
            if (par.getName().indexOf(".") > 0) {
                // this is a *.* parameter for later use
                specialParameter.add(par);
            } else {
                useParameter(par);
            }
        }
        configParameter = new Vector();

        // specify the algorithm classname
        if (algoName != null) {
            // use Algorithm defined via name
            if ("hashvalue".equals(algoName.getValue())) {
                algorithm = new HashvalueAlgorithm();
            } else if ("digest".equals(algoName.getValue())) {
                algorithm = new DigestAlgorithm();
            } else if ("checksum".equals(algoName.getValue())) {
                algorithm = new ChecksumAlgorithm();
            }
        } else {
            if (algorithmClass != null) {
                // use Algorithm specified by classname
                algorithm = (Algorithm) loadClass(
                    algorithmClass,
                    "is not an Algorithm.",
                    Algorithm.class);
            } else {
                // nothing specified - use default
                algorithm = defaultAlgorithm;
            }
        }

        // specify the cache classname
        if (cacheName != null) {
            // use Cache defined via name
            if ("propertyfile".equals(cacheName.getValue())) {
                cache = new PropertiesfileCache();
            }
        } else {
            if (cacheClass != null) {
                // use Cache specified by classname
                cache = (Cache) loadClass(cacheClass, "is not a Cache.", Cache.class);
            } else {
                // nothing specified - use default
                cache = defaultCache;
            }
        }

        // specify the comparator classname
        if (compName != null) {
            // use Algorithm defined via name
            if ("equal".equals(compName.getValue())) {
                comparator = new EqualComparator();
             } else if ("rule".equals(compName.getValue())) {
                // TODO there is a problem with the constructor for the RBC.
                // you have to provide the rules in the constructors - no setters
                // available.
                throw new BuildException("RuleBasedCollator not yet supported.");
                // Have to think about lazy initialization here...  JHM
                // comparator = new java.text.RuleBasedCollator();
            }
        } else {
            if (comparatorClass != null) {
                // use Algorithm specified by classname
                comparator = (Comparator) loadClass(
                    comparatorClass,
                    "is not a Comparator.",
                    Comparator.class);
            } else {
                // nothing specified - use default
                comparator = defaultComparator;
            }
        }

        //
        // -----  Set the special attributes, pattern '*.*'  -----
        //
        for (Iterator itSpecial = specialParameter.iterator(); itSpecial.hasNext();) {
            Parameter par = (Parameter) itSpecial.next();
            useParameter(par);
        }
        specialParameter = new Vector();
    
public AlgorithmgetAlgorithm()
Get the algorithm type to use.

return
the enumerated algorithm type

 return algorithm; 
public CachegetCache()
Get the cache type to use.

return
the enumerated cache type

 return cache; 
public java.lang.ClassLoadergetClassLoader()
Returns and initializes the classloader for this class.

return
the classloader

        if (myClassLoader == null) {
            myClassLoader = (classpath == null)
                // the usual classloader
                ? getClass().getClassLoader()
                // additional use the provided classpath
                : getProject().createClassLoader(classpath);
        }
        return myClassLoader;
    
public java.util.ComparatorgetComparator()
Get the comparator type to use.

return
the enumerated comparator type

 return comparator; 
public booleangetDelayUpdate()
Getter for the delay update

return
true if we should delay for performance

        return delayUpdate;
    
public intgetModified()
Getter for the modified count

return
modified count

        return modified;
    
public booleanisSelected(org.apache.tools.ant.types.Resource resource)
Implementation of ResourceSelector.isSelected().

param
resource The resource to check
return
whether the resource is selected
see
ResourceSelector#isSelected(Resource)

        if (resource.isFilesystemOnly()) {
            // We have a 'resourced' file, so reconvert it and use
            // the 'old' implementation.
            FileResource fileResource = (FileResource) resource;
            File file = fileResource.getFile();
            String filename = fileResource.getName();
            File basedir = fileResource.getBaseDir();
            return isSelected(basedir, filename, file);
        } else {
            try {
                // How to handle non-file-Resources? I copy temporarily the
                // resource to a file and use the file-implementation.
                FileUtils fu = FileUtils.getFileUtils();
                File tmpFile = fu.createTempFile("modified-", ".tmp", null);
                Resource tmpResource = new FileResource(tmpFile);
                ResourceUtils.copyResource(resource, tmpResource);
                boolean isSelected = isSelected(tmpFile.getParentFile(),
                                                tmpFile.getName(),
                                                resource.toLongString());
                tmpFile.delete();
                return isSelected;
            } catch (UnsupportedOperationException uoe) {
                log("The resource '"
                  + resource.getName()
                  + "' does not provide an InputStream, so it is not checked. "
                  + "Akkording to 'selres' attribute value it is "
                  + ((selectResourcesWithoutInputStream) ? "" : " not")
                  + "selected.", Project.MSG_INFO);
                return selectResourcesWithoutInputStream;
            } catch (Exception e) {
                throw new BuildException(e);
            }
        }
    
public booleanisSelected(java.io.File basedir, java.lang.String filename, java.io.File file)
Implementation of BaseExtendSelector.isSelected().

param
basedir as described in BaseExtendSelector
param
filename as described in BaseExtendSelector
param
file as described in BaseExtendSelector
return
as described in BaseExtendSelector

        return isSelected(basedir, filename, file.getAbsolutePath());
    
private booleanisSelected(java.io.File basedir, java.lang.String filename, java.lang.String cacheKey)
The business logic of this selector for use as ResourceSelector of FileSelector.

param
basedir as described in BaseExtendSelector
param
filename as described in BaseExtendSelector
param
cacheKey the name for the key for storing the hashvalue
return

        validate();
        File f = new File(basedir, filename);

        // You can not compute a value for a directory
        if (f.isDirectory()) {
            return selectDirectories;
        }

        // Get the values and do the comparison
        String cachedValue = String.valueOf(cache.get(f.getAbsolutePath()));
        String newValue = algorithm.getValue(f);

        boolean rv = (comparator.compare(cachedValue, newValue) != 0);

        // Maybe update the cache
        if (update && rv) {
            cache.put(f.getAbsolutePath(), newValue);
            setModified(getModified() + 1);
            if (!getDelayUpdate()) {
                saveCache();
            }
        }

        return rv;
    
protected java.lang.ObjectloadClass(java.lang.String classname, java.lang.String msg, java.lang.Class type)
Loads the specified class and initializes an object of that class. Throws a BuildException using the given message if an error occurs during loading/instantiation or if the object is not from the given type.

param
classname the classname
param
msg the message-part for the BuildException
param
type the type to check against
return
a castable object

        try {
            // load the specified class
            ClassLoader cl = getClassLoader();
            Class clazz = null;
            if (cl != null) {
                clazz = cl.loadClass(classname);
            } else {
                clazz = Class.forName(classname);
            }

            Object rv = clazz.newInstance();

            if (!type.isInstance(rv)) {
                throw new BuildException("Specified class (" + classname + ") " + msg);
            }
            return rv;
        } catch (ClassNotFoundException e) {
            throw new BuildException("Specified class (" + classname + ") not found.");
        } catch (Exception e) {
            throw new BuildException(e);
        }
    
public voidmessageLogged(org.apache.tools.ant.BuildEvent event)
Signals a message logging event.

param
event recieved BuildEvent

        // no-op
    
protected voidsaveCache()
save the cache file

        if (getModified() > 1) {
            cache.save();
            setModified(0);
        }
    
public voidsetAlgorithm(org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector$AlgorithmName name)
Set the algorithm type to use.

param
name an enumerated algorithm type.

        algoName = name;
    
public voidsetAlgorithmClass(java.lang.String classname)
Setter for algorithmClass.

param
classname new value

        algorithmClass = classname;
    
public voidsetCache(org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector$CacheName name)
Set the cache type to use.

param
name an enumerated cache type.

        cacheName = name;
    
public voidsetCacheClass(java.lang.String classname)
Setter for cacheClass.

param
classname new value

        cacheClass = classname;
    
public voidsetClassLoader(java.lang.ClassLoader loader)
Set the used ClassLoader. If you invoke this selector by API (e.g. inside some testcases) the selector will use a different classloader for loading the interface implementations than the caller. Therefore you will get a ClassCastException if you get the implementations from the selector and cast them.

param
loader the ClassLoader to use

        myClassLoader = loader;
    
public voidsetComparator(org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector$ComparatorName name)
Set the comparator type to use.

param
name an enumerated comparator type.

        compName = name;
    
public voidsetComparatorClass(java.lang.String classname)
Setter for comparatorClass.

param
classname new value

        comparatorClass = classname;
    
public voidsetDelayUpdate(boolean delayUpdate)
Setter for the delay update

param
delayUpdate true if we should delay for performance

        this.delayUpdate = delayUpdate;
    
public voidsetModified(int modified)
Setter for the modified count

param
modified count

        this.modified = modified;
    
public voidsetParameters(org.apache.tools.ant.types.Parameter[] parameters)
Defined in org.apache.tools.ant.types.Parameterizable. Overwrite implementation in superclass because only special parameters are valid.

see
#addParam(String,Object).
param
parameters the parameters to set.

        if (parameters != null) {
            for (int i = 0; i < parameters.length; i++) {
                configParameter.add(parameters[i]);
            }
        }
    
public voidsetSeldirs(boolean seldirs)
Support for seldirs attribute.

param
seldirs new value

        selectDirectories = seldirs;
    
public voidsetSelres(boolean newValue)
Support for selres attribute.

param
newValue the new value

        this.selectResourcesWithoutInputStream = newValue;
    
public voidsetUpdate(boolean update)
Support for update attribute.

param
update new value

        this.update = update;
    
public voidtargetFinished(org.apache.tools.ant.BuildEvent event)
Signals that a target has finished.

param
event recieved BuildEvent

        if (getDelayUpdate()) {
            saveCache();
        }
    
public voidtargetStarted(org.apache.tools.ant.BuildEvent event)
Signals that a target is starting.

param
event received BuildEvent

        // no-op
    
public voidtaskFinished(org.apache.tools.ant.BuildEvent event)
Signals that a task has finished.

param
event recieved BuildEvent

        if (getDelayUpdate()) {
            saveCache();
        }
    
public voidtaskStarted(org.apache.tools.ant.BuildEvent event)
Signals that a task is starting.

param
event recieved BuildEvent

        // no-op
    
public java.lang.StringtoString()
Override Object.toString().

return
information about this selector

        StringBuffer buf = new StringBuffer("{modifiedselector");
        buf.append(" update=").append(update);
        buf.append(" seldirs=").append(selectDirectories);
        buf.append(" cache=").append(cache);
        buf.append(" algorithm=").append(algorithm);
        buf.append(" comparator=").append(comparator);
        buf.append("}");
        return buf.toString();
    
protected voidtryToSetAParameter(java.lang.Object obj, java.lang.String name, java.lang.String value)
Try to set a value on an object using reflection. Helper method for easier access to IntrospectionHelper.setAttribute().

param
obj the object on which the attribute should be set
param
name the attributename
param
value the new value

        Project prj = (getProject() != null) ? getProject() : new Project();
        IntrospectionHelper iHelper
            = IntrospectionHelper.getHelper(prj, obj.getClass());
        try {
            iHelper.setAttribute(prj, obj, name, value);
        } catch (org.apache.tools.ant.BuildException e) {
            // no-op
        }
    
public voiduseParameter(org.apache.tools.ant.types.Parameter parameter)
Support for nested tags. Parameter named cache, algorithm, comparator or update are mapped to the respective set-Method. Parameter which names starts with cache. or algorithm. or comparator. are tried to set on the appropriate object via its set-methods. Other parameters are invalid and an BuildException will be thrown.

param
parameter Key and value as parameter object

        String key = parameter.getName();
        String value = parameter.getValue();
        if ("cache".equals(key)) {
            CacheName cn = new CacheName();
            cn.setValue(value);
            setCache(cn);
        } else if ("algorithm".equals(key)) {
            AlgorithmName an = new AlgorithmName();
            an.setValue(value);
            setAlgorithm(an);
        } else if ("comparator".equals(key)) {
            ComparatorName cn = new ComparatorName();
            cn.setValue(value);
            setComparator(cn);
        } else if ("update".equals(key)) {
            boolean updateValue =
                ("true".equalsIgnoreCase(value))
                ? true
                : false;
            setUpdate(updateValue);
        } else if ("delayupdate".equals(key)) {
            boolean updateValue =
                ("true".equalsIgnoreCase(value))
                ? true
                : false;
            setDelayUpdate(updateValue);
        } else if ("seldirs".equals(key)) {
            boolean sdValue =
                ("true".equalsIgnoreCase(value))
                ? true
                : false;
            setSeldirs(sdValue);
        } else if (key.startsWith("cache.")) {
            String name = key.substring(6);
            tryToSetAParameter(cache, name, value);
        } else if (key.startsWith("algorithm.")) {
            String name = key.substring(10);
            tryToSetAParameter(algorithm, name, value);
        } else if (key.startsWith("comparator.")) {
            String name = key.substring(11);
            tryToSetAParameter(comparator, name, value);
        } else {
            setError("Invalid parameter " + key);
        }
    
public voidverifySettings()
Overrides BaseSelector.verifySettings().

        configure();
        if (cache == null) {
            setError("Cache must be set.");
        } else if (algorithm == null) {
            setError("Algorithm must be set.");
        } else if (!cache.isValid()) {
            setError("Cache must be proper configured.");
        } else if (!algorithm.isValid()) {
            setError("Algorithm must be proper configured.");
        }