at.spardat.xma.boot.antext.GenPreloadFile Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* s IT Solutions AT Spardat GmbH - initial API and implementation
*******************************************************************************/
/*
* @(#) $Id: GenPreloadFile.java 2318 2008-02-04 10:04:04Z s2877 $
*/
package at.spardat.xma.boot.antext;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import at.spardat.xma.boot.cache.VersionNumber;
import at.spardat.xma.boot.comp.data.XMAApp;
import at.spardat.xma.boot.comp.data.XMAAppParser;
import at.spardat.xma.boot.comp.data.XMAResource;
import at.spardat.xma.boot.logger.LogLevel;
import at.spardat.xma.boot.logger.Logger;
/**
* Ant task to generate a html document containing a list of links to all downloadable resources of an XMA application.
* This html document can be used for preloading these resources.
* This task has to be called after the task AppDescriptor!
* @author s2877
*/
public class GenPreloadFile extends Task {
private boolean verbose;
private File xmaappFile;
private File pluginFile;
private File preloadFile;
private boolean gendeltas;
private List deltaInfo = new ArrayList();
final static Pattern versionedfilename = Pattern.compile("(.*)_(\\d+)\\.(\\d+)\\.(\\d+)(.*)");
final static Pattern deltafilename = Pattern.compile("(.*)_(\\d+)\\.(\\d+)\\.(\\d+)-(\\d+)\\.(\\d+)\\.(\\d+)\\.xdelta(.*)");
/**
* Set the xma-app.xml file to read the resources
*/
public void setXmaapp(File xmaappFile) {
this.xmaappFile = xmaappFile;
}
/**
* Set the plugins.xml file to read the plugin resources
*/
public void setPlugin(File pluginFile) {
this.pluginFile = pluginFile;
}
/**
* Set the output file to write the preload list
*/
public void setPreload(File preloadFile) {
this.preloadFile = preloadFile;
}
/**
* Turn on verbose output to the ant console
*/
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
/**
* Set if the URLs for delta download files should be included in the preload file.
* If set to true, the URLs of all delta download files of all versioned files without
* a -tag are written. For all versioned files with -tag, the
* -tag determines which delta download files are written.
*/
public void setGendeltas(boolean gen) {
gendeltas=gen;
}
/**
* Adds nested tag containing customization which delta file URLs
* should be included in the preload file.
*/
public void addConfiguredDeltaInfo(DeltaInfo info) {
deltaInfo.add(info);
}
/**
* Called by ant to do the work of the task.
* Reads the content of the two input files using classes from XMA-bootrt
* and generates the output file containing the list of the resources including the hashvalues in the urls.
*/
public void execute() throws BuildException {
InputStream inapp = null;
InputStream inplug = null;
PrintWriter out = null;
try {
Logger logger = Logger.getLogger("build");
logger.setLevel(LogLevel.SEVERE);
if(preloadFile==null) throw new BuildException("missing parameter 'preload'");
if(verbose == true) log("generating preload links into " + preloadFile);
out = new PrintWriter(new FileWriter(preloadFile));
out.println("");
out.println("");
out.println("Preload ");
out.println("");
out.println("");
if(xmaappFile==null) throw new BuildException("missing parameter 'xmaapp'");
if(verbose == true) log("reading resources from " + xmaappFile);
inapp = new FileInputStream(xmaappFile);
XMAAppParser parser = new XMAAppParser(logger, true );
XMAApp app = parser.parse(inapp);
for(Iterator it=app.getAllResources().values().iterator();it.hasNext();) {
handleResource((XMAResource)it.next(),out);
}
if(pluginFile!=null) {
if(verbose == true) log("reading resources from " + pluginFile);
inplug = new FileInputStream(pluginFile);
parser = new XMAAppParser(logger, true );
app = parser.parse(inplug);
for(Iterator it=app.getAllResources().values().iterator();it.hasNext();) {
handleResource((XMAResource)it.next(),out);
}
}
out.println("");
out.println("");
} catch (BuildException be) {
throw be;
} catch (Exception e) {
throw new BuildException(e);
} finally {
try { if(inapp != null) inapp.close(); } catch (IOException e2) { }
try { if(inplug != null) inplug.close(); } catch (IOException e2) { }
if(out != null) out.close();
}
}
/**
* Writes the URL of the given resource as HTML-link to out.
* If the given resource is a versioned file (containing the verion number
* in the file name), the URLs of its delta download files are written, too,
* if gendelta="true" was given.
* If a -tag was given for this resource, the -tag overrules
* the behaviour. see {@link DeltaInfo}.
*/
private void handleResource(XMAResource res,PrintWriter out) {
String href = res.getHref_();
String hash = res.getVersion_().getVersion();
DeltaInfo delta = findDeltaInfo(href);
if(gendeltas||delta!=null) {
String[] files = findDeltas(href,delta);
for(int i=0;iPreload");
if(verbose) log("generate link to "+file);
}
}
if(delta==null||delta.isPreloadfull()) {
String file = VersionNumber.insertHash(href,hash);
out.println("Preload");
if(verbose) log("generate link to "+file);
}
}
/**
* Finds the DeltaInfo to the given resource name.
* name is the relative url to a versioned jar file.
* [/]*_...jar
* e.g. "clientrt/xmartclient_2.0.0.jar"
* The name of the matching DeltaInfo must be the same or may
* omit the version number and extention.
* e.g. "clientrt/xmartclient"
*/
private DeltaInfo findDeltaInfo(String name) {
for(Iterator it=deltaInfo.iterator();it.hasNext();) {
DeltaInfo info = (DeltaInfo)it.next();
if(name.startsWith(info.getName())) {
if(name.equals(info.getName())) return info;
VersionNumber version = VersionNumber.parse(name);
if(version!=null) {
if(name.equals(info.getName()+"_"+version.toString()+".jar")) {
return info;
}
}
}
}
return null;
}
/**
* Finds the delta download files for the resource specified by the given
* href and returns there relative urls. If delta is null all corresponding
* download files are taken. Otherwise only the delta files accepted by
* delta are taken.
* The delta download files are searched in the same directory as the versioned resource.
*/
private String[] findDeltas(String href,final DeltaInfo delta) {
File baseDir = preloadFile.getParentFile();
File versionedFile = new File(baseDir,href);
File searchDir = versionedFile.getParentFile();
Matcher matcher = versionedfilename.matcher(href);
if(matcher.find()) {
String pathprefix = null;
String string = matcher.group(1);
int sep = string.lastIndexOf('/');
if(sep>0) {
pathprefix = string.substring(0,sep);
string = string.substring(sep+1);
}
final String prefix = string;
final String major = matcher.group(2);
final String minor = matcher.group(3);
final String bugfix = matcher.group(4);
final String postfix = matcher.group(5);
String[] found = searchDir.list(new FilenameFilter(){
public boolean accept(File dir, String name) {
Matcher matcher = deltafilename.matcher(name);
if(!matcher.find()) return false;
String string = matcher.group(1);
if(!prefix.equals(string)) return false;
String oldMajor = matcher.group(2);
String oldMinor = matcher.group(3);
String oldBugfix = matcher.group(4);
string = matcher.group(5);
if(!major.equals(string)) return false;
string = matcher.group(6);
if(!minor.equals(string)) return false;
string = matcher.group(7);
if(!bugfix.equals(string)) return false;
string = matcher.group(8);
if(!postfix.equals(string)) return false;
if(delta!=null) {
return delta.accept(oldMajor,oldMinor,oldBugfix);
}
return true;
}});
if(pathprefix!=null) {
for(int i=0;i.
* Each deltainfo corresponds to one versioned jar file for which delta download files are contained in
* the web application. It is used for fine grained selection of the delta download files choosed for
* preload. It supports minimal version, an include list of versions and and exclude list of versions.
* Further it can exclude the full version file from the preload.
*
* The objects are created by ant for any tag found inside this task and delivered by calling
* {@link GenPreloadFile#addConfiguredDeltaInfo(at.spardat.xma.boot.antext.GenPreloadFile.DeltaInfo)}. *
*/
public static class DeltaInfo {
String name;
Version minversion;
List includes = new ArrayList();
List excludes = new ArrayList();
boolean preloadfull=true;
public DeltaInfo() {}
/**
* Get the name of the affected resource.
*/
public String getName() {
return name;
}
/**
* Set the name of the affected resource. This name is the href of the concerned resource
* without the version number and extention. E.g. for "clientrt/xmartclient_2.0.0.jar"
* "clientrt/xmartclient" is used as name.
*/
public void setName(String name) {
this.name = name;
}
/**
* Returns if the full version of the resource should be included in the preload.
*/
public boolean isPreloadfull() {
return preloadfull;
}
/**
* Set if the full version of the resource should be included in the preload.
*/
public void setPreloadfull(boolean preloadfull) {
this.preloadfull = preloadfull;
}
/**
* Set the minimum version for delta download files to include in the preload.
* Delta files to older versions are omitted.
* For the format of the version number see the constructor of Version.
* Example: minversion="1.8.2"
* Default is empty which means no restriction.
*/
public void setMinversion(String minversion) {
this.minversion = new Version(minversion,true);
}
/**
* Sets a pattern for delta download files to exclude from the preload.
* The pattern is a comma seperated list of version numbers and version ranges
* |[,|]*
* Version numbers may contain wildcards.
* For the format of version numbers see the constructor of Version.
* For the format of version ranges see VersionRange.parse().
* Example: excludes="1.7.*,1.8.0-1.8.1"
* Default is empty meaning no exclusions.
*/
public void setExcludes(String excludes) {
for(StringTokenizer tok=new StringTokenizer(excludes,", \t");tok.hasMoreTokens();) {
String token = tok.nextToken();
VersionRange range = VersionRange.parse(token);
if(range!=null) this.excludes.add(range);
else this.excludes.add(new Version(token,false));
}
}
/**
* Sets a pattern for delta download files to include into the preload.
* The pattern is a comma seperated list of version numbers and version ranges
* |[,|]*
* Version numbers may contain wildcards.
* For the format of version numbers see the constructor of Version.
* For the format of version ranges see VersionRange.parse().
* Example: includes="1.8.2-1.8.5,2.*.*"
* Default is empty meaning all versions. (same as *.*.*)
*/
public void setIncludes(String includes) {
for(StringTokenizer tok=new StringTokenizer(includes,", \t");tok.hasMoreTokens();) {
String token = tok.nextToken();
VersionRange range = VersionRange.parse(token);
if(range!=null) this.includes.add(range);
else this.includes.add(new Version(token,false));
}
}
/**
* Determines if the version number given in the parameters is accepted according
* to the settings of minversion, includes and excludes.
* If includes have been set, includes must contain the given number to accept it.
* If excludes have been set, excludes must not contain the given number to accept it.
* If the given number is contained in includes and excludes, it is not accepted.
* If minversion have been set, the given number must always be greater or equal to
* minversion to be accepted.
*/
public boolean accept(String major,String minor,String bugfix) {
Version toTest = new Version(major,minor,bugfix);
if(minversion!=null && minversion.compareTo(toTest)>0) return false;
for(Iterator it=excludes.iterator();it.hasNext();) {
Object vers = it.next();
if(vers instanceof Version) {
if(toTest.compareTo(vers)==0) return false;
} else if(vers instanceof VersionRange) {
if(((VersionRange)vers).isInRange(toTest)) return false;
}
}
if(includes.size()<=0) return true;
for(Iterator it=includes.iterator();it.hasNext();) {
Object vers = it.next();
if(vers instanceof Version) {
if(toTest.compareTo(vers)==0) return true;
} else if(vers instanceof VersionRange) {
if(((VersionRange)vers).isInRange(toTest)) return true;
}
}
return false;
}
}
/**
* Represents a version number. In contrast to at.spardat.xma.boot.cache.VersionNumber it
* supports wildcards (e.g. 1.8.* or 1.*.*). Therfore it has its on compareTo-method which
* treats '*' as equal to any digit.
*/
public static class Version {
final static Pattern strict = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)");
final static Pattern fuzzy = Pattern.compile("(\\d+|\\*)\\.(\\d+|\\*)\\.(\\d+|\\*)");
int major,minor,bugfix;
boolean majorDefined,minorDefined,bugfixDefined;
/**
* Constructs a Version from major,minor,bugfix. All three strings must contain
* a valid integer. Wildcards are not supported with this constructor.
* @throws NumberFormatException if any parameter does not contain a parsable integer.
*/
public Version(String major,String minor,String bugfix) {
this.major=Integer.parseInt(major);
this.minor=Integer.parseInt(minor);
this.bugfix=Integer.parseInt(bugfix);
majorDefined=minorDefined=bugfixDefined=true;
}
/**
* Constructs a Version by parsing it form a String. The string has to be of the format
* .. e.g. "1.8.4".
* @param version the String to parse.
* @param strict if true no wildcards are allowed. If false '*' can be used as wildcard
* for any of the three digits.
* @throws IllegalArgumentException if the string cannot be parsed sucessfully.
*/
public Version(String version,boolean strict) {
Matcher matcher;
if(strict) matcher = Version.strict.matcher(version);
else matcher = fuzzy.matcher(version);
if(matcher.matches()) {
String major = matcher.group(1);
String minor = matcher.group(2);
String bugfix = matcher.group(3);
init(major,minor,bugfix);
} else {
throw new IllegalArgumentException("'"+version+"' is not a valid version number");
}
}
/**
* Initializes major,minor and bugfix which may contain wildcards here.
*/
private void init(String major,String minor,String bugfix) {
if(!"*".equals(major)) {
this.major=Integer.parseInt(major);
majorDefined=true;
}
if(!"*".equals(minor)) {
this.minor=Integer.parseInt(minor);
minorDefined=true;
}
if(!"*".equals(bugfix)) {
this.bugfix=Integer.parseInt(bugfix);
bugfixDefined=true;
}
}
/**
* Compares this Version to an other Version. Both Versions may contain
* wildcards. A Wildcard is considered equal to any corresponding digit.
* E.g 1.8.5 and 1.8.* are considered equal.
* @return -1 if this is less than other
* 0 if this is equal to other
* 1 if this is greater than other
*/
public int compareTo(Object other) {
if(other==null) throw new NullPointerException();
if(!(other instanceof Version)) throw new ClassCastException();
Version version = (Version) other;
if(majorDefined&&version.majorDefined) {
if(majorversion.major) return 1;
}
if(minorDefined&&version.minorDefined) {
if(minorversion.minor) return 1;
}
if(bugfixDefined&&version.bugfixDefined) {
if(bugfixversion.bugfix) return 1;
}
return 0;
}
/**
* Returns the string representation of this Version.
*/
public String toString() {
StringBuffer buffer = new StringBuffer();
if(majorDefined) buffer.append(major);
else buffer.append('*');
buffer.append('.');
if(minorDefined) buffer.append(minor);
else buffer.append('*');
buffer.append('.');
if(bugfixDefined) buffer.append(bugfix);
else buffer.append('*');
return buffer.toString();
}
}
/**
* Represents a range of version numbers defined by a minimum and a maximum version number.
* Used in build.xml in the propertes "includes" and "excludes" of -tag of this ant task.
* String format: ..-.. e.g. includes="1.7.5-1.8.4"
*/
public static class VersionRange {
final static Pattern pattern = Pattern.compile("((\\d+)\\.(\\d+)\\.(\\d+))-((\\d+)\\.(\\d+)\\.(\\d+))");
Version min,max;
/**
* Parses a version range from a String.
* String format: ..-.. e.g. includes="1.7.5-1.8.4"
* No withespaces are supported in this string.
* @return the parsed range or null if the string does not contain a valid version range.
*/
public static VersionRange parse(String string) {
Matcher matcher = pattern.matcher(string);
if(matcher.matches()) {
VersionRange result = new VersionRange();
String first = matcher.group(1);
result.min=new Version(first,true);
String second = matcher.group(5);
result.max=new Version(second,true);
return result;
} else {
return null;
}
}
/**
* Dedermines if the given version is contained in the range.
* The borders of the range are inclusive meaning this method returns true
* if min <= version or version <= max.
*/
public boolean isInRange(Version version) {
return min.compareTo(version)<=0 && max.compareTo(version)>=0;
}
/**
* Returns the string representation of the range.
*/
public String toString() {
return min.toString() + "-" + max.toString();
}
}
}