Merged in repository_patch branch. Plus these changes:

* Renamed helma.util.SourceProperties to helma.util.ResourceProperties
* Removed timing from helma.framework.core.RequestEvaluator
This commit is contained in:
hns 2005-03-10 16:54:04 +00:00
parent 343fad7638
commit 9f842e8c5d
37 changed files with 2163 additions and 1768 deletions

View file

@ -17,10 +17,13 @@
package helma.doc; package helma.doc;
import helma.framework.IPathElement; import helma.framework.IPathElement;
import helma.framework.repository.FileResource;
import helma.framework.repository.FileResource;
import helma.main.Server; import helma.main.Server;
import helma.util.SystemProperties; import helma.util.ResourceProperties;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
/** /**
* *
*/ */
@ -94,12 +97,13 @@ public class DocApplication extends DocDirElement {
*/ */
private void readProps() { private void readProps() {
File propsFile = new File(location, "app.properties"); File propsFile = new File(location, "app.properties");
SystemProperties serverProps = null; ResourceProperties serverProps = null;
if (Server.getServer()!=null) { if (Server.getServer()!=null) {
serverProps = Server.getServer().getProperties(); serverProps = Server.getServer().getProperties();
} }
SystemProperties appProps = new SystemProperties(propsFile.getAbsolutePath(), ResourceProperties appProps = new ResourceProperties();
serverProps); appProps.setDefaultProperties(serverProps);
appProps.addResource(new FileResource(propsFile));
excluded = new HashSet(); excluded = new HashSet();
addExclude("cvs"); addExclude("cvs");

View file

@ -17,6 +17,7 @@
package helma.framework; package helma.framework;
import helma.framework.core.Skin; import helma.framework.core.Skin;
import helma.framework.core.Application;
import helma.util.*; import helma.util.*;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -124,15 +125,24 @@ public final class ResponseTrans implements Serializable {
// the message digest used to generate composed digests for ETag headers // the message digest used to generate composed digests for ETag headers
private transient MessageDigest digest; private transient MessageDigest digest;
// the appliciation checksum to make ETag headers sensitive to app changes // the application
long applicationChecksum; Application app;
/**
* Creates a new ResponseTrans object.
*/
public ResponseTrans(Application app) {
this.app = app;
}
/** /**
* Creates a new ResponseTrans object. * Creates a new ResponseTrans object.
* *
* @param req the RequestTrans for this response * @param req the RequestTrans for this response
*/ */
public ResponseTrans(RequestTrans req) { public ResponseTrans(Application app, RequestTrans req) {
this.app = app;
reqtrans = req; reqtrans = req;
} }
@ -681,27 +691,13 @@ public final class ResponseTrans implements Serializable {
return; return;
} }
byte[] b = digest.digest(MD5Encoder.toBytes(applicationChecksum)); // add the application checksum as dependency to make ETag
// generation sensitive to changes in the app
byte[] b = digest.digest(MD5Encoder.toBytes(app.getChecksum()));
/* StringBuffer buf = new StringBuffer(b.length*2);
for ( int i=0; i<b.length; i++ ) {
int j = (b[i]<0) ? 256+b[i] : b[i];
if ( j<16 ) buf.append("0");
buf.append(Integer.toHexString(j));
}
setETag (buf.toString ()); */
setETag(new String(Base64.encode(b))); setETag(new String(Base64.encode(b)));
} }
/**
*
*
* @param n ...
*/
public void setApplicationChecksum(long n) {
applicationChecksum = n;
}
/** /**
* *
* *

View file

@ -19,18 +19,21 @@ package helma.framework.core;
import helma.extensions.ConfigurationException; import helma.extensions.ConfigurationException;
import helma.extensions.HelmaExtension; import helma.extensions.HelmaExtension;
import helma.framework.*; import helma.framework.*;
import helma.framework.repository.ResourceComparator;
import helma.framework.repository.Repository;
import helma.framework.repository.FileResource;
import helma.framework.repository.FileRepository;
import helma.main.Server; import helma.main.Server;
import helma.objectmodel.*; import helma.objectmodel.*;
import helma.objectmodel.db.*; import helma.objectmodel.db.*;
import helma.scripting.*;
import helma.util.*; import helma.util.*;
import java.io.*; import java.io.*;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.net.MalformedURLException;
import java.rmi.*; import java.rmi.*;
import java.util.*; import java.util.*;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import java.util.ArrayList;
/** /**
@ -44,16 +47,16 @@ public final class Application implements IPathElement, Runnable {
private String name; private String name;
// properties and db-properties // properties and db-properties
SystemProperties props; ResourceProperties props;
// properties and db-properties // properties and db-properties
SystemProperties dbProps; ResourceProperties dbProps;
// Helma server home directory // Helma server home directory
File home; File home;
// application directory // application sources
File appDir; ArrayList repositories;
// embedded db directory // embedded db directory
File dbDir; File dbDir;
@ -129,10 +132,10 @@ public final class Application implements IPathElement, Runnable {
String charset; String charset;
// password file to use for authenticate() function // password file to use for authenticate() function
private CryptFile pwfile; private CryptResource pwfile;
// Map of java class names to object prototypes // Map of java class names to object prototypes
SystemProperties classMapping; ResourceProperties classMapping;
// Map of extensions allowed for public skins // Map of extensions allowed for public skins
Properties skinExtensions; Properties skinExtensions;
@ -151,6 +154,8 @@ public final class Application implements IPathElement, Runnable {
// the list of custom cron jobs // the list of custom cron jobs
Hashtable customCronJobs = null; Hashtable customCronJobs = null;
private ResourceComparator resourceComparator;
/** /**
* Simple constructor for dead application instances. * Simple constructor for dead application instances.
*/ */
@ -159,12 +164,12 @@ public final class Application implements IPathElement, Runnable {
} }
/** /**
* Build an application with the given name in the app directory. No Server-wide * Build an application with the given name with the given sources. No
* properties are created or used. * Server-wide properties are created or used.
*/ */
public Application(String name, File appDir, File dbDir) public Application(String name, Repository[] repositories, File dbDir)
throws RemoteException, IllegalArgumentException { throws RemoteException, IllegalArgumentException {
this(name, null, appDir, dbDir); this(name, null, repositories, dbDir);
} }
/** /**
@ -173,28 +178,34 @@ public final class Application implements IPathElement, Runnable {
*/ */
public Application(String name, Server server) public Application(String name, Server server)
throws RemoteException, IllegalArgumentException { throws RemoteException, IllegalArgumentException {
this(name, server, null, null); this(name, server, new Repository[0], null);
} }
/** /**
* Build an application with the given name, server instance, app and * Build an application with the given name, server instance, sources and
* db directories. * db directory.
*/ */
public Application(String name, Server server, File customAppDir, File customDbDir) public Application(String name, Server server, Repository[] repositories, File customDbDir)
throws RemoteException, IllegalArgumentException { throws RemoteException, IllegalArgumentException {
if ((name == null) || (name.trim().length() == 0)) { if ((name == null) || (name.trim().length() == 0)) {
throw new IllegalArgumentException("Invalid application name: " + name); throw new IllegalArgumentException("Invalid application name: " + name);
} }
this.name = name; this.name = name;
appDir = customAppDir; if (repositories.length > 0) {
this.repositories = new ArrayList();
this.repositories.addAll(Arrays.asList(repositories));
resourceComparator = new ResourceComparator(this);
} else {
throw new java.lang.IllegalArgumentException("No sources defined for application: " + name);
}
dbDir = customDbDir; dbDir = customDbDir;
// system-wide properties, default to null // system-wide properties, default to null
SystemProperties sysProps; ResourceProperties sysProps;
// system-wide properties, default to null // system-wide properties, default to null
SystemProperties sysDbProps; ResourceProperties sysDbProps;
sysProps = sysDbProps = null; sysProps = sysDbProps = null;
home = null; home = null;
@ -202,12 +213,6 @@ public final class Application implements IPathElement, Runnable {
if (server != null) { if (server != null) {
home = server.getHopHome(); home = server.getHopHome();
// if appDir and dbDir weren't explicitely passed, use the
// standard subdirectories of the Hop home directory
if (appDir == null) {
appDir = new File(server.getAppsHome(), name);
}
if (dbDir == null) { if (dbDir == null) {
dbDir = new File(server.getDbHome(), name); dbDir = new File(server.getDbHome(), name);
} }
@ -217,11 +222,6 @@ public final class Application implements IPathElement, Runnable {
sysDbProps = server.getDbProperties(); sysDbProps = server.getDbProperties();
} }
// create the directories if they do not exist already
if (!appDir.exists()) {
appDir.mkdirs();
}
if (!dbDir.exists()) { if (!dbDir.exists()) {
dbDir.mkdirs(); dbDir.mkdirs();
} }
@ -230,32 +230,26 @@ public final class Application implements IPathElement, Runnable {
threadgroup = new ThreadGroup("TX-" + name); threadgroup = new ThreadGroup("TX-" + name);
// create app-level properties // create app-level properties
File propfile = new File(appDir, "app.properties"); props = new ResourceProperties(this, "app.properties", sysProps);
props = new SystemProperties(propfile.getAbsolutePath(), sysProps);
// get log names // get log names
accessLogName = props.getProperty("accessLog", "helma."+name+".access"); accessLogName = props.getProperty("accessLog", "helma."+name+".access");
eventLogName = props.getProperty("eventLog", "helma."+name+".event"); eventLogName = props.getProperty("eventLog", "helma."+name+".event");
// create app-level db sources // create app-level db sources
File dbpropfile = new File(appDir, "db.properties"); dbProps = new ResourceProperties(this, "db.properties", sysDbProps);
dbProps = new SystemProperties(dbpropfile.getAbsolutePath(), sysDbProps);
// the passwd file, to be used with the authenticate() function // the passwd file, to be used with the authenticate() function
CryptFile parentpwfile = null; CryptResource parentpwfile = null;
if (home != null) { if (home != null) {
parentpwfile = new CryptFile(new File(home, "passwd"), null); parentpwfile = new CryptResource(new FileResource(new File(home, "passwd")), null);
} }
pwfile = new CryptFile(new File(appDir, "passwd"), parentpwfile); pwfile = new CryptResource(repositories[0].getResource("passwd"), parentpwfile);
// the properties that map java class names to prototype names // the properties that map java class names to prototype names
File classMappingFile = new File(appDir, "class.properties"); classMapping = new ResourceProperties(this, "class.properties");
classMapping = new SystemProperties(classMappingFile.getAbsolutePath());
classMapping.setIgnoreCase(false); classMapping.setIgnoreCase(false);
// get class name of root object if defined. Otherwise native Helma objectmodel will be used. // get class name of root object if defined. Otherwise native Helma objectmodel will be used.
@ -274,9 +268,8 @@ public final class Application implements IPathElement, Runnable {
* Get the application ready to run, initializing the evaluators and type manager. * Get the application ready to run, initializing the evaluators and type manager.
*/ */
public synchronized void init() public synchronized void init()
throws DatabaseException, MalformedURLException, throws DatabaseException, IllegalAccessException,
IllegalAccessException, InstantiationException, InstantiationException, ClassNotFoundException {
ClassNotFoundException {
// create and init type mananger // create and init type mananger
typemgr = new TypeManager(this); typemgr = new TypeManager(this);
@ -346,8 +339,8 @@ public final class Application implements IPathElement, Runnable {
// The whole user/userroot handling is basically old // The whole user/userroot handling is basically old
// ugly obsolete crap. Don't bother. // ugly obsolete crap. Don't bother.
SystemProperties p = new SystemProperties(); ResourceProperties p = new ResourceProperties();
String usernameField = userMapping.getNameField(); String usernameField = (userMapping != null) ? userMapping.getNameField() : null;
if (usernameField == null) { if (usernameField == null) {
usernameField = "name"; usernameField = "name";
@ -438,6 +431,18 @@ public final class Application implements IPathElement, Runnable {
return running; return running;
} }
public File getAppDir() {
try {
return new File(((FileRepository) getRepositories().next()).getName());
} catch (ClassCastException ex) {
return null;
}
}
public ResourceComparator getResourceComparator() {
return resourceComparator;
}
/** /**
* Returns a free evaluator to handle a request. * Returns a free evaluator to handle a request.
*/ */
@ -554,6 +559,8 @@ public final class Application implements IPathElement, Runnable {
* Execute a request coming in from a web client. * Execute a request coming in from a web client.
*/ */
public ResponseTrans execute(RequestTrans req) { public ResponseTrans execute(RequestTrans req) {
System.setProperty("request.start", String.valueOf(System.currentTimeMillis()));
requestCount += 1; requestCount += 1;
// get user for this request's session // get user for this request's session
@ -594,7 +601,7 @@ public final class Application implements IPathElement, Runnable {
throw stopped; throw stopped;
} catch (Exception x) { } catch (Exception x) {
errorCount += 1; errorCount += 1;
res = new ResponseTrans(req); res = new ResponseTrans(this, req);
res.writeErrorReport(name, x.getMessage()); res.writeErrorReport(name, x.getMessage());
} finally { } finally {
if (primaryRequest) { if (primaryRequest) {
@ -808,7 +815,7 @@ public final class Application implements IPathElement, Runnable {
* Return a skin for a given object. The skin is found by determining the prototype * Return a skin for a given object. The skin is found by determining the prototype
* to use for the object, then looking up the skin for the prototype. * to use for the object, then looking up the skin for the prototype.
*/ */
public Skin getSkin(String protoname, String skinname, Object[] skinpath) { public Skin getSkin(String protoname, String skinname, Object[] skinpath) throws IOException {
Prototype proto = getPrototypeByName(protoname); Prototype proto = getPrototypeByName(protoname);
if (proto == null) { if (proto == null) {
@ -1616,10 +1623,23 @@ public final class Application implements IPathElement, Runnable {
} }
/** /**
* Return the directory of this application * Searches for the index of the given repository for this app.
* The arguement must be a root argument, or -1 will be returned.
*
* @param rep one of this app's root repositories.
* @return the index of the first occurrence of the argument in this
* list; returns <tt>-1</tt> if the object is not found.
*/ */
public File getAppDir() { public int getRepositoryIndex(Repository rep) {
return appDir; return repositories.indexOf(rep);
}
/**
* Returns the repositories of this application
* @return iterator through application repositories
*/
public Iterator getRepositories() {
return repositories.iterator();
} }
/** /**
@ -1721,7 +1741,7 @@ public final class Application implements IPathElement, Runnable {
* change, too. * change, too.
*/ */
public long getChecksum() { public long getChecksum() {
return starttime + typemgr.getChecksum() + props.getChecksum(); return starttime + typemgr.lastCodeUpdate() + props.getChecksum();
} }
/** /**
@ -1743,7 +1763,7 @@ public final class Application implements IPathElement, Runnable {
* *
* @return ... * @return ...
*/ */
public SystemProperties getProperties() { public ResourceProperties getProperties() {
return props; return props;
} }

View file

@ -490,7 +490,7 @@ public class ApplicationBean implements Serializable {
File f = app.getServerDir(); File f = app.getServerDir();
if (f == null) { if (f == null) {
f = app.getAppDir(); return app.getAppDir().getAbsolutePath();
} }
return f.getAbsolutePath(); return f.getAbsolutePath();

View file

@ -17,9 +17,12 @@
package helma.framework.core; package helma.framework.core;
import helma.objectmodel.db.DbMapping; import helma.objectmodel.db.DbMapping;
import helma.scripting.*;
import helma.util.SystemMap; import helma.util.SystemMap;
import helma.util.SystemProperties; import helma.util.ResourceProperties;
import helma.framework.repository.Resource;
import helma.framework.repository.Repository;
import helma.framework.repository.ResourceTracker;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
@ -30,81 +33,77 @@ import java.util.*;
* relational database table. * relational database table.
*/ */
public final class Prototype { public final class Prototype {
Application app;
String name; String name;
String lowerCaseName; String lowerCaseName;
Application app;
File directory; Resource[] resources;
File[] files; long lastResourceListing;
long lastDirectoryListing;
long checksum; // lastCheck is the time the prototype's files were last checked
HashMap code; long lastChecksum;
HashMap zippedCode; long newChecksum;
HashMap skins; // lastUpdate is the time at which any of the prototype's files were found updated the last time
HashMap zippedSkins; long lastCodeUpdate;
HashMap updatables;
TreeSet code;
TreeSet skins;
HashMap trackers;
HashSet repositories;
// a map of this prototype's skins as raw strings // a map of this prototype's skins as raw strings
// used for exposing skins to application (script) code (via app.skinfiles). // used for exposing skins to application (script) code (via app.skinfiles).
SkinMap skinMap; HashMap skinMap;
DbMapping dbmap; DbMapping dbmap;
// lastCheck is the time the prototype's files were last checked
private long lastChecksum;
// lastUpdate is the time at which any of the prototype's files were
// found updated the last time
private long lastUpdate;
private Prototype parent; private Prototype parent;
// Tells us whether this prototype is used to script a generic Java object, // Tells us whether this prototype is used to script a generic Java object,
// as opposed to a Helma objectmodel node object. // as opposed to a Helma objectmodel node object.
boolean isJavaPrototype; boolean isJavaPrototype;
ResourceProperties props;
/** /**
* Creates a new Prototype object. * Creates a new Prototype object.
* *
* @param name the prototype's name * @param name the prototype's name
* @param dir the prototype directory, if known * @param repository the first prototype's repository
* @param app the application this prototype is a part of * @param app the application this prototype is a part of
*/ */
public Prototype(String name, File dir, Application app) { public Prototype(String name, Repository repository, Application app) {
// app.logEvent ("Constructing Prototype "+app.getName()+"/"+name); // app.logEvent ("Constructing Prototype "+app.getName()+"/"+name);
this.app = app; this.app = app;
this.name = name; this.name = name;
repositories = new HashSet();
if (repository != null) {
repositories.add(repository);
}
lowerCaseName = name.toLowerCase(); lowerCaseName = name.toLowerCase();
if (dir != null) {
directory = dir;
} else {
directory = new File(app.appDir, name);
// a little bit of overkill to maintain backwards compatibility
// with lower case type names...
if (!directory.isDirectory()) {
File lowerDir = new File(app.appDir, lowerCaseName);
if (lowerDir.isDirectory()) {
directory = lowerDir;
}
}
}
// Create and register type properties file // Create and register type properties file
File propfile = new File(directory, "type.properties"); props = new ResourceProperties();
SystemProperties props = new SystemProperties(propfile.getAbsolutePath()); if (repository != null) {
props.addResource(repository.getResource("type.properties"));
}
dbmap = new DbMapping(app, name, props); dbmap = new DbMapping(app, name, props);
// we don't need to put the DbMapping into proto.updatables, because // we don't need to put the DbMapping into proto.updatables, because
// dbmappings are checked separately in TypeManager.checkFiles() for // dbmappings are checked separately in TypeManager.checkFiles() for
// each request // each request
code = new HashMap(); code = new TreeSet(app.getResourceComparator());
zippedCode = new HashMap(); skins = new TreeSet(app.getResourceComparator());
skins = new HashMap();
zippedSkins = new HashMap();
updatables = new HashMap();
skinMap = new SkinMap(); trackers = new HashMap();
skinMap = new HashMap();
isJavaPrototype = app.isJavaPrototype(name); isJavaPrototype = app.isJavaPrototype(name);
lastUpdate = lastChecksum = 0; lastCodeUpdate = lastChecksum = 0;
} }
/** /**
@ -115,42 +114,58 @@ public final class Prototype {
} }
/** /**
* Return this prototype's directory. * Adds an repository to the list of repositories
* @param repository repository to add
*/ */
public File getDirectory() { public void addRepository(Repository repository) {
return directory; if (!repositories.contains(repository)) {
repositories.add(repository);
props.addResource(repository.getResource("type.properties"));
}
return;
} }
/** /**
* Get the list of files in this prototype's directory * Returns the list of resources in this prototype's repositories
*/ */
public File[] getFiles() { public Resource[] getResources() {
if ((files == null) || (directory.lastModified() != lastDirectoryListing)) { long resourceListing = getChecksum();
lastDirectoryListing = directory.lastModified();
files = directory.listFiles();
if (files == null) { if (resources == null || resourceListing != lastResourceListing) {
files = new File[0]; lastResourceListing = resourceListing;
ArrayList list = new ArrayList();
Iterator iterator = repositories.iterator();
while (iterator.hasNext()) {
try {
list.addAll(((Repository) iterator.next()).getAllResources());
} catch (IOException iox) {
iox.printStackTrace();
}
}
resources = (Resource[]) list.toArray(new Resource[list.size()]);
}
return resources;
}
/**
* Get a checksum over this prototype's sources
*/
public long getChecksum() {
long checksum = 0;
Iterator iterator = repositories.iterator();
while (iterator.hasNext()) {
try {
checksum += ((Repository) iterator.next()).getChecksum();
} catch (IOException iox) {
iox.printStackTrace();
} }
} }
return files; newChecksum = checksum;
}
/**
* Get a checksum over the files in this prototype's directory
*/
public long getChecksum() {
// long start = System.currentTimeMillis();
File[] f = getFiles();
long c = directory.lastModified();
for (int i = 0; i < f.length; i++)
c += f[i].lastModified();
checksum = c;
// System.err.println ("CHECKSUM "+name+": "+(System.currentTimeMillis()-start));
return checksum; return checksum;
} }
@ -160,7 +175,8 @@ public final class Prototype {
* @return ... * @return ...
*/ */
public boolean isUpToDate() { public boolean isUpToDate() {
return checksum == lastChecksum; return (newChecksum == 0 && lastChecksum == 0) ?
false : newChecksum == lastChecksum;
} }
/** /**
@ -169,7 +185,7 @@ public final class Prototype {
*/ */
public void setParentPrototype(Prototype parent) { public void setParentPrototype(Prototype parent) {
// this is not allowed for the hopobject and global prototypes // this is not allowed for the hopobject and global prototypes
if ("HopObject".equals(name) || "global".equals(name)) { if ("hopobject".equals(lowerCaseName) || "global".equals(lowerCaseName)) {
return; return;
} }
@ -188,11 +204,11 @@ public final class Prototype {
* Check if the given prototype is within this prototype's parent chain. * Check if the given prototype is within this prototype's parent chain.
*/ */
public final boolean isInstanceOf(String pname) { public final boolean isInstanceOf(String pname) {
if (name.equals(pname) || lowerCaseName.equals(pname)) { if (name.equalsIgnoreCase(pname)) {
return true; return true;
} }
if ((parent != null) && !"HopObject".equals(parent.getName())) { if (parent != null) {
return parent.isInstanceOf(pname); return parent.isInstanceOf(pname);
} }
@ -248,14 +264,8 @@ public final class Prototype {
* residing in the prototype directory, not for skin files in * residing in the prototype directory, not for skin files in
* other locations or database stored skins. * other locations or database stored skins.
*/ */
public SkinFile getSkinFile(String sfname) { public Resource getSkinResource(String sname) {
SkinFile sf = (SkinFile) skins.get(sfname); return (Resource) skinMap.get(sname);
if (sf == null) {
sf = (SkinFile) zippedSkins.get(sfname);
}
return sf;
} }
/** /**
@ -263,11 +273,11 @@ public final class Prototype {
* residing in the prototype directory, not for skins files in * residing in the prototype directory, not for skins files in
* other locations or database stored skins. * other locations or database stored skins.
*/ */
public Skin getSkin(String sfname) { public Skin getSkin(String sname) throws IOException {
SkinFile sf = getSkinFile(sfname); Resource res = getSkinResource(sname);
if (sf != null) { if (res != null) {
return sf.getSkin(); return Skin.getSkin(res, app);
} else { } else {
return null; return null;
} }
@ -294,8 +304,8 @@ public final class Prototype {
/** /**
* Get the last time any script has been re-read for this prototype. * Get the last time any script has been re-read for this prototype.
*/ */
public long getLastUpdate() { public long lastCodeUpdate() {
return lastUpdate; return lastCodeUpdate;
} }
/** /**
@ -304,7 +314,7 @@ public final class Prototype {
* the evaluators. * the evaluators.
*/ */
public void markUpdated() { public void markUpdated() {
lastUpdate = System.currentTimeMillis(); lastCodeUpdate = System.currentTimeMillis();
} }
/** /**
@ -313,7 +323,7 @@ public final class Prototype {
*/ */
public void markChecked() { public void markChecked() {
// lastCheck = System.currentTimeMillis (); // lastCheck = System.currentTimeMillis ();
lastChecksum = checksum; lastChecksum = newChecksum;
} }
/** /**
@ -321,145 +331,30 @@ public final class Prototype {
* to not return a map in a transient state where it is just being * to not return a map in a transient state where it is just being
* updated by the type manager. * updated by the type manager.
*/ */
public synchronized Map getCode() { public synchronized Iterator getCodeResources() {
return (Map) code.clone(); return code.iterator();
} }
/** /**
* Return a clone of this prototype's functions container. Synchronized * Add a code resource to this prototype
* to not return a map in a transient state where it is just being *
* updated by the type manager. * @param res a code resource
*/ */
public synchronized Map getZippedCode() { public synchronized void addCodeResource(Resource res) {
return (Map) zippedCode.clone(); code.add(res);
trackers.put(res.getName(), new ResourceTracker(res));
} }
/**
*
*
* @param action ...
*/
public synchronized void addActionFile(ActionFile action) {
File f = action.getFile();
if (f != null) {
code.put(action.getSourceName(), action);
updatables.put(f.getName(), action);
} else {
zippedCode.put(action.getSourceName(), action);
}
}
/** /**
* Add a skin resource to this prototype
* *
* * @param res a skin resource
* @param template ...
*/ */
public synchronized void addTemplate(Template template) { public synchronized void addSkinResource(Resource res) {
File f = template.getFile(); skins.add(res);
skinMap.put(res.getShortName(), res);
if (f != null) { trackers.put(res.getName(), new ResourceTracker(res));
code.put(template.getSourceName(), template);
updatables.put(f.getName(), template);
} else {
zippedCode.put(template.getSourceName(), template);
}
}
/**
*
*
* @param funcfile ...
*/
public synchronized void addFunctionFile(FunctionFile funcfile) {
File f = funcfile.getFile();
if (f != null) {
code.put(funcfile.getSourceName(), funcfile);
updatables.put(f.getName(), funcfile);
} else {
zippedCode.put(funcfile.getSourceName(), funcfile);
}
}
/**
*
*
* @param skinfile ...
*/
public synchronized void addSkinFile(SkinFile skinfile) {
File f = skinfile.getFile();
if (f != null) {
skins.put(skinfile.getName(), skinfile);
updatables.put(f.getName(), skinfile);
} else {
zippedSkins.put(skinfile.getName(), skinfile);
}
}
/**
*
*
* @param action ...
*/
public synchronized void removeActionFile(ActionFile action) {
File f = action.getFile();
if (f != null) {
code.remove(action.getSourceName());
updatables.remove(f.getName());
} else {
zippedCode.remove(action.getSourceName());
}
}
/**
*
*
* @param funcfile ...
*/
public synchronized void removeFunctionFile(FunctionFile funcfile) {
File f = funcfile.getFile();
if (f != null) {
code.remove(funcfile.getSourceName());
updatables.remove(f.getName());
} else {
zippedCode.remove(funcfile.getSourceName());
}
}
/**
*
*
* @param template ...
*/
public synchronized void removeTemplate(Template template) {
File f = template.getFile();
if (f != null) {
code.remove(template.getSourceName());
updatables.remove(f.getName());
} else {
zippedCode.remove(template.getSourceName());
}
}
/**
*
*
* @param skinfile ...
*/
public synchronized void removeSkinFile(SkinFile skinfile) {
File f = skinfile.getFile();
if (f != null) {
skins.remove(skinfile.getName());
updatables.remove(f.getName());
} else {
zippedSkins.remove(skinfile.getName());
}
} }
/** /**
@ -475,7 +370,7 @@ public final class Prototype {
* @return ... * @return ...
*/ */
public SkinMap getSkinMap() { public SkinMap getSkinMap() {
return skinMap; return new SkinMap();
} }
// not yet implemented // not yet implemented
@ -529,13 +424,17 @@ public final class Prototype {
checkForUpdates(); checkForUpdates();
SkinFile sf = (SkinFile) super.get(key); Resource res = (Resource) super.get(key);
if (sf == null) { if (res == null) {
return null; return null;
} }
return sf.getSkin().getSource(); try {
return res.getContent();
} catch (IOException iox) {
return null;
}
} }
public int hashCode() { public int hashCode() {
@ -589,26 +488,20 @@ public final class Prototype {
app.typemgr.updatePrototype(Prototype.this); app.typemgr.updatePrototype(Prototype.this);
} }
if (lastUpdate > lastSkinmapLoad) { if (lastCodeUpdate > lastSkinmapLoad) {
load(); load();
} }
} }
private synchronized void load() { private synchronized void load() {
if (lastUpdate == lastSkinmapLoad) { if (lastCodeUpdate == lastSkinmapLoad) {
return; return;
} }
super.clear(); super.clear();
// load Skins from zip files first, then from directories // load Skins
for (Iterator i = zippedSkins.entrySet().iterator(); i.hasNext();) { for (Iterator i = skins.iterator(); i.hasNext();) {
Map.Entry e = (Map.Entry) i.next();
super.put(e.getKey(), e.getValue());
}
for (Iterator i = skins.entrySet().iterator(); i.hasNext();) {
Map.Entry e = (Map.Entry) i.next(); Map.Entry e = (Map.Entry) i.next();
super.put(e.getKey(), e.getValue()); super.put(e.getKey(), e.getValue());
@ -628,7 +521,7 @@ public final class Prototype {
} }
} }
lastSkinmapLoad = lastUpdate; lastSkinmapLoad = lastCodeUpdate;
} }
public String toString() { public String toString() {

View file

@ -305,10 +305,6 @@ public final class RequestEvaluator implements Runnable {
// set the req.action property, cutting off the _action suffix // set the req.action property, cutting off the _action suffix
req.action = action.substring(0, action.length() - 7); req.action = action.substring(0, action.length() - 7);
// set the application checksum in response to make ETag
// generation sensitive to changes in the app
res.setApplicationChecksum(app.getChecksum());
// reset skin recursion detection counter // reset skin recursion detection counter
skinDepth = 0; skinDepth = 0;
@ -856,7 +852,7 @@ public final class RequestEvaluator implements Runnable {
this.req = req; this.req = req;
this.reqtype = HTTP; this.reqtype = HTTP;
this.session = session; this.session = session;
res = new ResponseTrans(req); res = new ResponseTrans(app, req);
result = null; result = null;
exception = null; exception = null;
} }
@ -875,7 +871,7 @@ public final class RequestEvaluator implements Runnable {
req = new RequestTrans(reqtypeName); req = new RequestTrans(reqtypeName);
req.path = functionName; req.path = functionName;
session = new Session(functionName, app); session = new Session(functionName, app);
res = new ResponseTrans(req); res = new ResponseTrans(app, req);
result = null; result = null;
exception = null; exception = null;
} }

View file

@ -17,6 +17,7 @@
package helma.framework.core; package helma.framework.core;
import helma.framework.*; import helma.framework.*;
import helma.framework.repository.Resource;
import helma.objectmodel.ConcurrencyException; import helma.objectmodel.ConcurrencyException;
import helma.util.HtmlEncoder; import helma.util.HtmlEncoder;
import helma.util.SystemMap; import helma.util.SystemMap;
@ -24,6 +25,9 @@ import helma.util.WrappedMap;
import helma.util.UrlEncoded; import helma.util.UrlEncoded;
import java.util.*; import java.util.*;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.io.Reader;
import java.io.InputStreamReader;
import java.io.IOException;
/** /**
* This represents a Helma skin, i.e. a template created from containing Macro tags * This represents a Helma skin, i.e. a template created from containing Macro tags
@ -80,6 +84,22 @@ public final class Skin {
parse(); parse();
} }
public static Skin getSkin(Resource res, Application app) throws IOException {
String encoding = app.getProperty("skinCharset");
Reader reader;
if (encoding == null) {
reader = new InputStreamReader(res.getInputStream());
} else {
reader = new InputStreamReader(res.getInputStream(), encoding);
}
char[] characterBuffer = new char[(int) res.getLength()];
int length = reader.read(characterBuffer);
reader.close();
return new Skin(characterBuffer, length, app);
}
/** /**
* Parse a skin object from source text * Parse a skin object from source text
*/ */

View file

@ -1,191 +0,0 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.framework.core;
import helma.util.Updatable;
import java.io.*;
/**
* This represents a File containing a Hop skin
*/
public final class SkinFile implements Updatable {
String name;
Prototype prototype;
Application app;
File file;
Skin skin;
long lastmod = 0;
/**
* Creates a new SkinFile object.
*
* @param file ...
* @param name ...
* @param proto ...
*/
public SkinFile(File file, String name, Prototype proto) {
this.prototype = proto;
this.file = file;
this.name = name;
this.app = proto.app;
skin = null;
}
/**
* Create a skinfile without a file, passing the skin body directly. This is used for
* Skins contained in zipped applications. The whole update mechanism is bypassed
* by immediately setting the skin member.
*/
public SkinFile(String body, String name, Prototype proto) {
this.prototype = proto;
this.app = proto.app;
this.name = name;
this.file = null;
skin = new Skin(body, app);
}
/**
* Create a skinfile that doesn't belong to a prototype, or at
* least it doesn't know about its prototype and isn't managed by the prototype.
*/
public SkinFile(File file, String name, Application app) {
this.app = app;
this.file = file;
this.name = name;
this.prototype = null;
skin = null;
}
/**
* Tell the type manager whether we need an update. this is the case when
* the file has been modified or deleted.
*/
public boolean needsUpdate() {
// if skin object is null we only need to call update if the file doesn't
// exist anymore, while if the skin is initialized, we'll catch both
// cases (file deleted and file changed) by just calling lastModified().
return (skin != null) ? (lastmod != file.lastModified()) : (!file.exists());
}
/**
*
*/
public void update() {
if (!file.exists()) {
// remove skin from prototype
remove();
} else {
// we only need to update if the skin has already been initialized
if (skin != null) {
read();
}
}
}
private void read() {
String encoding = app.getProperty("skinCharset");
try {
Reader reader;
if (encoding == null) {
reader = new FileReader(file);
} else {
FileInputStream in = new FileInputStream(file);
reader = new InputStreamReader(in, encoding);
}
char[] c = new char[(int) file.length()];
int length = reader.read(c);
reader.close();
skin = new Skin(c, length, app);
} catch (IOException x) {
app.logEvent("Error reading Skin " + file + ": " + x);
}
lastmod = file.lastModified();
}
/**
*
*/
public void remove() {
if (prototype != null) {
prototype.removeSkinFile(this);
}
}
/**
*
*
* @return ...
*/
public File getFile() {
return file;
}
/**
*
*
* @return ...
*/
public Skin getSkin() {
if (skin == null) {
read();
}
return skin;
}
/**
*
*
* @return ...
*/
public String getName() {
return name;
}
/**
*
*
* @return ...
*/
public String toString() {
return new StringBuffer("[SkinFile ").append(prototype.getName())
.append("/").append(name).append("]").toString();
}
/**
* Override to produce hash code depending on file name
*
* @return a hash code value for this object.
*/
public int hashCode() {
return toString().hashCode();
}
/**
* Override to equal other SkinFile with the same source name
*
* @param obj the object to compare to
* @return true if obj is a SkinFile with the same source name
*/
public boolean equals(Object obj) {
return (obj instanceof SkinFile) &&
toString().equals(obj.toString());
}
}

View file

@ -17,6 +17,8 @@
package helma.framework.core; package helma.framework.core;
import helma.objectmodel.INode; import helma.objectmodel.INode;
import helma.framework.repository.FileResource;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
@ -41,7 +43,7 @@ public final class SkinManager implements FilenameFilter {
skinExtension = ".skin"; skinExtension = ".skin";
} }
protected Skin getSkin(Prototype proto, String skinname, Object[] skinpath) { protected Skin getSkin(Prototype proto, String skinname, Object[] skinpath) throws IOException {
if (proto == null) { if (proto == null) {
return null; return null;
} }
@ -77,7 +79,7 @@ public final class SkinManager implements FilenameFilter {
return null; return null;
} }
protected Skin getSkinInternal(Object skinset, String prototype, String skinname) { protected Skin getSkinInternal(Object skinset, String prototype, String skinname) throws IOException {
if ((prototype == null) || (skinset == null)) { if ((prototype == null) || (skinset == null)) {
return null; return null;
} }
@ -101,19 +103,16 @@ public final class SkinManager implements FilenameFilter {
} else { } else {
// Skinset is interpreted as directory name from which to // Skinset is interpreted as directory name from which to
// retrieve the skin // retrieve the skin
File f = new File(skinset.toString(), prototype); StringBuffer b = new StringBuffer(skinset.toString());
b.append(File.separatorChar).append(prototype).append(File.separatorChar)
.append(skinname).append(skinExtension);
// if directory does not exist use lower case property name // TODO: check for lower case prototype name for backwards compat
if (!f.isDirectory()) {
f = new File(skinset.toString(), prototype.toLowerCase());
}
f = new File(f, skinname + skinExtension); File f = new File(b.toString());
if (f.exists() && f.canRead()) { if (f.exists() && f.canRead()) {
SkinFile sf = new SkinFile(f, skinname, app); return Skin.getSkin(new FileResource(f), app);
return sf.getSkin();
} }
} }
@ -144,7 +143,7 @@ public final class SkinManager implements FilenameFilter {
String name = skinNames[i].substring(0, skinNames[i].length() - 5); String name = skinNames[i].substring(0, skinNames[i].length() - 5);
File file = new File(dir, skinNames[i]); File file = new File(dir, skinNames[i]);
map.put(name, new SkinFile(file, name, proto)); map.put(name, (new FileResource(file)));
} }
return map; return map;

View file

@ -17,10 +17,12 @@
package helma.framework.core; package helma.framework.core;
import helma.objectmodel.db.DbMapping; import helma.objectmodel.db.DbMapping;
import helma.scripting.*; import helma.framework.repository.ZipRepository;
import helma.util.*; import helma.framework.repository.Resource;
import helma.framework.repository.Repository;
import helma.framework.repository.ResourceTracker;
import java.io.*; import java.io.*;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.*; import java.util.*;
@ -37,24 +39,16 @@ public final class TypeManager {
final static String skinExtension = ".skin"; final static String skinExtension = ".skin";
private Application app; private Application app;
private File appDir; Repository[] repositories;
long[] modified;
// map of prototypes // map of prototypes
private HashMap prototypes; private HashMap prototypes;
// map of zipped script files
private HashMap zipfiles;
// set of Java archives // set of Java archives
private HashSet jarfiles; private HashSet jarfiles;
private long lastCheck = 0; private long lastCheck = 0;
private long appDirMod = 0; private long lastCodeUpdate;
// a checksum that changes whenever something in the application files changes.
private long checksum;
// the hopobject prototype
// private Prototype hopobjectProto;
// the global prototype
// private Prototype globalProto;
// app specific class loader, includes jar files in the app directory // app specific class loader, includes jar files in the app directory
private AppClassLoader loader; private AppClassLoader loader;
@ -64,31 +58,14 @@ public final class TypeManager {
* *
* @param app ... * @param app ...
* *
* @throws MalformedURLException ...
* @throws RuntimeException ... * @throws RuntimeException ...
*/ */
public TypeManager(Application app) throws MalformedURLException { public TypeManager(Application app) {
this.app = app; this.app = app;
appDir = app.appDir; repositories = new Repository[app.repositories.size()];
app.repositories.toArray(repositories);
// make sure the directories for the standard prototypes exist, modified = new long[repositories.length];
// and lament otherwise
if (appDir.list().length == 0) {
for (int i = 0; i < standardTypes.length; i++) {
File f = new File(appDir, standardTypes[i]);
if (!f.exists() && !f.mkdir()) {
app.logEvent("Warning: directory " + f.getAbsolutePath() +
" could not be created.");
} else if (!f.isDirectory()) {
app.logEvent("Warning: " + f.getAbsolutePath() +
" is not a directory.");
}
}
}
prototypes = new HashMap(); prototypes = new HashMap();
zipfiles = new HashMap();
jarfiles = new HashSet(); jarfiles = new HashSet();
URL helmajar = TypeManager.class.getResource("/"); URL helmajar = TypeManager.class.getResource("/");
@ -139,90 +116,63 @@ public final class TypeManager {
try { try {
checkFiles(); checkFiles();
} catch (Exception ignore) { } catch (Exception ignore) {}
}
lastCheck = System.currentTimeMillis(); lastCheck = System.currentTimeMillis();
} }
/** private void checkRepository(Repository repository) throws IOException {
* Run through application's prototype directories and check if Repository[] list = repository.getRepositories();
* there are any prototypes to be created. for (int i = 0; i < list.length; i++) {
*/ if (list[i].isScriptRoot()) {
private void checkFiles() { // this is an embedded top-level script repository
// check if any files have been created/removed since last time we checkRepository(list[i]);
// checked... } else {
if (appDir.lastModified() > appDirMod) { // its an prototype
appDirMod = appDir.lastModified(); String name = null;
name = ((Repository) list[i]).getShortName();
String[] list = appDir.list(); Prototype proto = getPrototype(name);
if (list == null) {
throw new RuntimeException("Can't read app directory " + appDir +
" - check permissions");
}
for (int i = 0; i < list.length; i++) {
if (list[i].endsWith(".zip")) {
ZippedAppFile zipped = (ZippedAppFile) zipfiles.get(list[i]);
if (zipped == null) {
File f = new File(appDir, list[i]);
if (!f.isDirectory() && f.exists()) {
zipped = new ZippedAppFile(f, app);
zipfiles.put(list[i], zipped);
}
}
continue;
}
if (list[i].endsWith(".jar")) {
if (!jarfiles.contains(list[i])) {
jarfiles.add(list[i]);
File f = new File(appDir, list[i]);
try {
loader.addURL(new URL("file:" + f.getAbsolutePath()));
} catch (MalformedURLException ignore) {
}
}
continue;
}
if (list[i].indexOf('.') > -1) {
continue;
}
Prototype proto = getPrototype(list[i]);
// if prototype doesn't exist, create it // if prototype doesn't exist, create it
if ((proto == null) && isValidTypeName(list[i])) { if ((proto == null) && isValidTypeName(name)) {
File f = new File(appDir, list[i]); // create new prototype
createPrototype(name, (Repository) list[i]);
if (f.isDirectory()) { } else {
// create new prototype proto.addRepository((Repository) list[i]);
createPrototype(list[i], f);
}
} }
} }
} }
// calculate this app's checksum by adding all checksums from all prototypes Iterator resources = repository.getResources();
long newChecksum = 0; while (resources.hasNext()) {
// check for jar files to add to class loader
// loop through zip files to check for updates Resource resource = (Resource) resources.next();
for (Iterator it = zipfiles.values().iterator(); it.hasNext();) { String name = resource.getName();
ZippedAppFile zipped = (ZippedAppFile) it.next(); if (name.endsWith(".jar")) {
if (!jarfiles.contains(name)) {
if (zipped.needsUpdate()) { jarfiles.add(name);
zipped.update(); loader.addURL(resource.getUrl());
}
} }
}
}
newChecksum += zipped.lastmod; /**
* Run through application's prototype sources and check if
* there are any prototypes to be created.
*/
private void checkFiles() {
// check if any files have been created/removed since last time we checked...
for (int i = 0; i < repositories.length; i++) {
try {
if (repositories[i].lastModified() > modified[i]) {
modified[i] = repositories[i].lastModified();
checkRepository(repositories[i]);
}
} catch (IOException iox) {
iox.printStackTrace();
}
} }
// loop through prototypes and check if type.properties needs updates // loop through prototypes and check if type.properties needs updates
@ -231,9 +181,6 @@ public final class TypeManager {
for (Iterator i = prototypes.values().iterator(); i.hasNext();) { for (Iterator i = prototypes.values().iterator(); i.hasNext();) {
Prototype proto = (Prototype) i.next(); Prototype proto = (Prototype) i.next();
// calculate this app's type checksum
newChecksum += proto.getChecksum();
// update prototype's type mapping // update prototype's type mapping
DbMapping dbmap = proto.getDbMapping(); DbMapping dbmap = proto.getDbMapping();
@ -243,25 +190,10 @@ public final class TypeManager {
// global and HopObject, which is a bit awkward... // global and HopObject, which is a bit awkward...
// I mean we're the type manager, so this should // I mean we're the type manager, so this should
// be part of our job, right? // be part of our job, right?
proto.props.update();
dbmap.update(); dbmap.update();
} }
} }
checksum = newChecksum;
}
protected void removeZipFile(String zipname) {
zipfiles.remove(zipname);
for (Iterator i = prototypes.values().iterator(); i.hasNext();) {
Prototype proto = (Prototype) i.next();
// update prototype's type mapping
DbMapping dbmap = proto.getDbMapping();
SystemProperties props = dbmap.getProperties();
props.removeProps(zipname);
}
} }
private boolean isValidTypeName(String str) { private boolean isValidTypeName(String str) {
@ -283,8 +215,8 @@ public final class TypeManager {
* Return a checksum over all files in all prototypes in this application. * Return a checksum over all files in all prototypes in this application.
* The checksum can be used to find out quickly if any file has changed. * The checksum can be used to find out quickly if any file has changed.
*/ */
public long getChecksum() { public long lastCodeUpdate() {
return checksum; return lastCodeUpdate;
} }
/** /**
@ -320,12 +252,11 @@ public final class TypeManager {
* Create and register a new Prototype. * Create and register a new Prototype.
* *
* @param typename the name of the prototype * @param typename the name of the prototype
* @param dir the prototype directory if it is know, or null if we * @param repository the first prototype source
* ought to find out by ourselves
* @return the newly created prototype * @return the newly created prototype
*/ */
public Prototype createPrototype(String typename, File dir) { public Prototype createPrototype(String typename, Repository repository) {
Prototype proto = new Prototype(typename, dir, app); Prototype proto = new Prototype(typename, repository, app);
// put the prototype into our map // put the prototype into our map
prototypes.put(proto.getLowerCaseName(), proto); prototypes.put(proto.getLowerCaseName(), proto);
@ -334,124 +265,86 @@ public final class TypeManager {
} }
/** /**
* Update a prototype to the files in the prototype directory. * Check a prototype for new or updated resources.
*/
public void updatePrototype(String name) {
// System.err.println ("UPDATE PROTO: "+app.getName()+"/"+name);
Prototype proto = getPrototype(name);
updatePrototype(proto);
}
/**
* Update a prototype to the files in the prototype directory.
*/ */
public void updatePrototype(Prototype proto) { public void updatePrototype(Prototype proto) {
if ((proto == null) || proto.isUpToDate()) {
return;
}
synchronized (proto) { synchronized (proto) {
// check again because another thread may have checked the // check again because another thread may have checked the
// prototype while we were waiting for access to the synchronized section // prototype while we were waiting for access to the synchronized section
/* if (System.currentTimeMillis() - proto.getLastCheck() < 1000) boolean updatedResources = false;
return; */ List createdResources = null;
HashSet updateSet = null;
HashSet createSet = null;
// our plan is to do as little as possible, so first check if // our plan is to do as little as possible, so first check if
// anything the prototype knows about has changed on disk // anything the prototype knows about has changed on disk
for (Iterator i = proto.updatables.values().iterator(); i.hasNext();) { for (Iterator i = proto.trackers.values().iterator(); i.hasNext();) {
Updatable upd = (Updatable) i.next(); ResourceTracker tracker = (ResourceTracker) i.next();
if (upd.needsUpdate()) { try {
if (updateSet == null) { if (tracker.hasChanged()) {
updateSet = new HashSet(); updatedResources = true;
tracker.markClean();
} }
} catch (IOException iox) {
updateSet.add(upd); iox.printStackTrace();
} }
} }
// next we check if files have been created or removed since last update // next we check if files have been created or removed since last update
// if (proto.getLastCheck() < dir.lastModified ()) { Resource[] resources = proto.getResources();
File[] list = proto.getFiles();
for (int i = 0; i < list.length; i++) { for (int i = 0; i < resources.length; i++) {
String fn = list[i].getName(); String name = resources[i].getName();
if (!proto.trackers.containsKey(name)) {
// ignore files starting with ".". if (name.endsWith(templateExtension) ||
if (fn.startsWith(".")) { name.endsWith(scriptExtension) ||
continue; name.endsWith(actionExtension) ||
} name.endsWith(skinExtension)) {
if (createdResources == null) {
if (!proto.updatables.containsKey(fn)) { createdResources = new ArrayList();
if (fn.endsWith(templateExtension) || fn.endsWith(scriptExtension) ||
fn.endsWith(actionExtension) || fn.endsWith(skinExtension) ||
"type.properties".equalsIgnoreCase(fn)) {
if (createSet == null) {
createSet = new HashSet();
} }
createSet.add(list[i]); createdResources.add(resources[i]);
} }
} }
} }
// }
// if nothing needs to be updated, mark prototype as checked and return // if nothing needs to be updated, mark prototype as checked and return
if ((updateSet == null) && (createSet == null)) { if (!updatedResources && createdResources == null) {
proto.markChecked(); proto.markChecked();
return; return;
} }
// first go through new files and create new items // first go through new files and create new items
if (createSet != null) { if (createdResources != null) {
Object[] newFiles = createSet.toArray(); Resource[] newResources = new Resource[createdResources.size()];
createdResources.toArray(newResources);
for (int i = 0; i < newFiles.length; i++) { for (int i = 0; i < newResources.length; i++) {
File file = (File) newFiles[i]; String resourceName = newResources[i].getName();
String filename = file.getName(); if (resourceName.endsWith(templateExtension) ||
int dot = filename.lastIndexOf("."); resourceName.endsWith(scriptExtension) ||
String tmpname = filename.substring(0, dot); resourceName.endsWith(actionExtension)) {
String srcName = getSourceName(file); try {
proto.addCodeResource(newResources[i]);
if (filename.endsWith(templateExtension)) { } catch (Throwable x) {
try { app.logEvent("Error updating prototype: " + x);
Template t = new Template(file, tmpname, srcName, proto); }
} else if (resourceName.endsWith(skinExtension)) {
proto.addTemplate(t); try {
} catch (Throwable x) { proto.addSkinResource(newResources[i]);
app.logEvent("Error updating prototype: " + x); } catch (Throwable x) {
} app.logEvent("Error updating prototype: " + x);
} else if (filename.endsWith(scriptExtension)) { }
try { }
FunctionFile ff = new FunctionFile(file, srcName, proto);
proto.addFunctionFile(ff);
} catch (Throwable x) {
app.logEvent("Error updating prototype: " + x);
}
} else if (filename.endsWith(actionExtension)) {
try {
ActionFile af = new ActionFile(file, tmpname, srcName, proto);
proto.addActionFile(af);
} catch (Throwable x) {
app.logEvent("Error updating prototype: " + x);
}
} else if (filename.endsWith(skinExtension)) {
SkinFile sf = new SkinFile(file, tmpname, proto);
proto.addSkinFile(sf);
}
} }
} }
// next go through existing updatables // next go through existing updatables
if (updateSet != null) { if (updatedResources) {
/*
for (Iterator i = updateSet.iterator(); i.hasNext();) { for (Iterator i = updateSet.iterator(); i.hasNext();) {
Updatable upd = (Updatable) i.next(); Updatable upd = (Updatable) i.next();
@ -467,21 +360,15 @@ public final class TypeManager {
} }
} }
} }
*/
} }
// mark prototype as checked and updated. // mark prototype as checked and updated.
proto.markChecked(); proto.markChecked();
proto.markUpdated(); proto.markUpdated();
} lastCodeUpdate = proto.lastCodeUpdate();
// end of synchronized (proto)
} // end of synchronized (proto)
} }
private String getSourceName(File file) {
StringBuffer b = new StringBuffer(app.getName());
b.append(":");
b.append(file.getParentFile().getName());
b.append("/");
b.append(file.getName());
return b.toString();
}
} }

View file

@ -1,246 +0,0 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.framework.core;
import helma.objectmodel.db.DbMapping;
import helma.scripting.*;
import helma.util.SystemProperties;
import helma.util.Updatable;
import java.io.*;
import java.util.*;
import java.util.zip.*;
/**
* This represents a Zip-File which may contain other Updatables for one or more prototypes.
*/
public class ZippedAppFile implements Updatable {
Application app;
File file;
long lastmod;
// Set of updatables provided by this zip file
Set updatables;
// Set of prototypes this zip files provides updatables for
Set prototypes;
/**
* Creates a new ZippedAppFile object.
*
* @param file ...
* @param app ...
*/
public ZippedAppFile(File file, Application app) {
this.app = app;
this.file = file;
updatables = new HashSet();
prototypes = new HashSet();
}
/**
* Tell the type manager whether we need an update. this is the case when
* the file has been modified or deleted.
*/
public boolean needsUpdate() {
return !file.exists() || (lastmod != file.lastModified());
}
/**
*
*/
public synchronized void update() {
if (!file.exists()) {
remove();
} else {
ZipFile zip = null;
Set newUpdatables = new HashSet();
Set newPrototypes = new HashSet();
try {
lastmod = file.lastModified();
zip = new ZipFile(file);
for (Enumeration en = zip.entries(); en.hasMoreElements();) {
ZipEntry entry = (ZipEntry) en.nextElement();
String ename = entry.getName();
StringTokenizer st = new StringTokenizer(ename, "/");
int tokens = st.countTokens();
if (tokens == 1) {
String fname = st.nextToken();
if ("app.properties".equalsIgnoreCase(fname)) {
app.props.addProps(file.getName(), zip.getInputStream(entry));
} else if ("db.properties".equalsIgnoreCase(fname)) {
app.dbProps.addProps(file.getName(), zip.getInputStream(entry));
}
} else if (tokens == 2) {
String dir = st.nextToken();
String fname = st.nextToken();
// System.err.println ("ZIPENTRY: "+ dir +" ~ "+fname);
Prototype proto = app.typemgr.getPrototype(dir);
if (proto == null) {
proto = app.typemgr.createPrototype(dir, null);
}
if (fname.endsWith(".hac")) {
String name = fname.substring(0, fname.lastIndexOf("."));
String srcName = getSourceName(ename);
String content = getZipEntryContent(zip, entry);
// System.err.println ("["+content+"]");
ActionFile act = new ActionFile(content, name, srcName,
proto);
proto.addActionFile(act);
newUpdatables.add(act);
} else if (fname.endsWith(".hsp")) {
String name = fname.substring(0, fname.lastIndexOf("."));
String srcName = getSourceName(ename);
String content = getZipEntryContent(zip, entry);
// System.err.println ("["+content+"]");
Template tmp = new Template(content, name, srcName, proto);
proto.addTemplate(tmp);
newUpdatables.add(tmp);
} else if (fname.endsWith(".skin")) {
String name = fname.substring(0, fname.lastIndexOf("."));
String content = getZipEntryContent(zip, entry);
// System.err.println ("["+content+"]");
SkinFile skin = new SkinFile(content, name, proto);
proto.addSkinFile(skin);
newUpdatables.add(skin);
} else if (fname.endsWith(".js")) {
String srcName = getSourceName(ename);
String content = getZipEntryContent(zip, entry);
// System.err.println ("["+content+"]");
FunctionFile ff = new FunctionFile(content, srcName, proto);
proto.addFunctionFile(ff);
newUpdatables.add(ff);
} else if ("type.properties".equalsIgnoreCase(fname)) {
DbMapping dbmap = proto.getDbMapping();
SystemProperties props = dbmap.getProperties();
props.addProps(file.getName(), zip.getInputStream(entry));
}
// mark prototype as updated
newPrototypes.add(proto);
}
}
} catch (Throwable x) {
System.err.println("Error updating ZipFile: " + x);
if (app.debug) {
x.printStackTrace();
}
} finally {
// remove updatables that have gone
updatables.removeAll(newUpdatables);
for (Iterator it = updatables.iterator(); it.hasNext();) {
((Updatable) it.next()).remove();
}
updatables = newUpdatables;
// mark both old and new prototypes as updated
prototypes.addAll(newPrototypes);
for (Iterator it = prototypes.iterator(); it.hasNext();) {
((Prototype) it.next()).markUpdated();
}
prototypes = newPrototypes;
try {
zip.close();
} catch (Exception ignore) {
}
}
}
}
/**
*
*/
public void remove() {
// remove updatables from prototypes
for (Iterator it = updatables.iterator(); it.hasNext();) {
((Updatable) it.next()).remove();
}
// mark affected prototypes as updated
for (Iterator it = prototypes.iterator(); it.hasNext();) {
((Prototype) it.next()).markUpdated();
}
// remove self from type manager
app.typemgr.removeZipFile(file.getName());
}
/**
*
*
* @param zip ...
* @param entry ...
*
* @return ...
*
* @throws IOException ...
*/
public String getZipEntryContent(ZipFile zip, ZipEntry entry)
throws IOException {
int size = (int) entry.getSize();
char[] c = new char[size];
InputStreamReader reader = new InputStreamReader(zip.getInputStream(entry));
int read = 0;
while (read < size) {
int r = reader.read(c, read, size-read);
if (r == -1)
break;
read += r;
}
return new String(c);
}
/**
*
*
* @return ...
*/
public String toString() {
return file.getName();
}
private String getSourceName(String entry) {
StringBuffer b = new StringBuffer(app.getName());
b.append(":");
b.append(file.getName());
b.append("/");
b.append(entry);
return b.toString();
}
}

View file

@ -0,0 +1,122 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.framework.repository;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Iterator;
import java.io.IOException;
/**
* Provides common methods and fields for the default implementations of the
* repository interface
*/
public abstract class AbstractRepository implements Repository {
/**
* Parent repository this repository is contained in.
*/
Repository parent;
/**
* Holds direct child repositories
*/
Repository[] repositories;
/**
* Holds direct resources
*/
HashMap resources;
/**
* Cached name for faster access
*/
String name;
/**
* Cached short name for faster access
*/
String shortName;
/**
*
*/
public abstract void update();
/**
*
* @return
*/
public String getName() {
return name;
}
public String getShortName() {
return shortName;
}
public Repository getRootRepository() {
if (parent == null) {
return this;
} else {
return parent.getRootRepository();
}
}
public Resource getResource(String name) {
update();
return (Resource) resources.get(getName() + "/" + name);
}
public Iterator getResources() {
update();
return resources.values().iterator();
}
public Repository[] getRepositories() {
update();
return repositories;
}
public Repository getParentRepository() {
return parent;
}
public List getAllResources() throws IOException {
update();
ArrayList allResources = new ArrayList();
allResources.addAll(resources.values());
for (int i = 0; i < repositories.length; i++) {
allResources.addAll(repositories[i].getAllResources());
}
return allResources;
}
public String toString() {
return getName();
}
}

View file

@ -0,0 +1,165 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.framework.repository;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Repository implementation for directories providing file resources
*/
public class FileRepository extends AbstractRepository {
// Directory serving sub-repositories and file resources
private File dir;
private long lastModified = -1;
private long lastChecksum = 0;
private long lastChecksumTime = 0;
/**
* Defines how long the checksum of the repository will be cached
*/
private final long cacheTime = 1000L;
/**
* Constructs a FileRepository using the given argument
* @param initArgs absolute path to the directory
*/
public FileRepository(String initArgs) {
this(new File(initArgs), null);
}
/**
* Constructs a FileRepository using the given directory as top-level
* repository
* @param dir directory
*/
public FileRepository(File dir) {
this(dir, null);
}
/**
* Constructs a FileRepository using the given directory and top-level
* repository
* @param dir directory
* @param parent top-level repository
*/
private FileRepository(File dir, FileRepository parent) {
this.dir = dir;
if (!dir.exists()) {
create();
}
if (parent == null) {
name = shortName = dir.getAbsolutePath();
} else {
this.parent = parent;
shortName = dir.getName();
name = parent.getName() + "/" + shortName;
}
}
public boolean exists() {
if (dir.exists() && dir.isDirectory()) {
return true;
} else {
return false;
}
}
public void create() {
if (!dir.exists() || !dir.isDirectory()) {
dir.mkdirs();
}
return;
}
/**
* Checks wether the repository is to be considered a top-level
* repository from a scripting point of view. For example, a zip
* file within a file repository is not a root repository from
* a physical point of view, but from the scripting point of view it is.
*
* @return true if the repository is to be considered a top-level script repository
*/
public boolean isScriptRoot() {
return parent == null;
}
public long lastModified() {
return dir.lastModified();
}
public long getChecksum() throws IOException {
// delay checksum check if already checked recently
if (System.currentTimeMillis() > lastChecksumTime + cacheTime) {
update();
long checksum = lastModified;
for (int i = 0; i < repositories.length; i++) {
checksum += repositories[i].getChecksum();
}
lastChecksum = checksum;
lastChecksumTime = System.currentTimeMillis();
}
return lastChecksum;
}
/**
* Updates the content cache of the repository
* Gets called from within all methods returning sub-repositories or
* resources
*/
public synchronized void update() {
if (dir.lastModified() != lastModified) {
lastModified = dir.lastModified();
File[] list = dir.listFiles();
ArrayList newRepositories = new ArrayList(list.length);
HashMap newResources = new HashMap(list.length);
for (int i = 0; i < list.length; i++) {
if (list[i].isDirectory()) {
// a nested directory aka child file repository
newRepositories.add(new FileRepository(list[i], this));
} else if (list[i].getName().endsWith(".zip")) {
// a nested zip repository
newRepositories.add(new ZipRepository(list[i], this));
} else if (list[i].isFile()) {
// a file resource
FileResource resource = new FileResource(list[i], this);
newResources.put(resource.getName(), resource);
}
}
repositories = (Repository[])
newRepositories.toArray(new Repository[newRepositories.size()]);
resources = newResources;
}
}
public String toString() {
return new StringBuffer("FileRepository[").append(name).append("]").toString();
}
}

View file

@ -0,0 +1,106 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.framework.repository;
import java.net.*;
import java.io.*;
public class FileResource implements Resource {
File file;
Repository repository;
String name;
String shortName;
public FileResource(File file) {
this(file, null);
}
protected FileResource(File file, FileRepository repository) {
this.file = file;
if (repository == null) {
name = shortName = file.getName();
} else {
this.repository = repository;
name = repository.getName() + "/" + file.getName();
if (name.lastIndexOf(".") != -1) {
shortName = file.getName().substring(0, file.getName().lastIndexOf("."));
} else {
shortName = file.getName();
}
}
}
public String getName() {
return name;
}
public String getShortName() {
return shortName;
}
public InputStream getInputStream() {
try {
return new FileInputStream(file);
} catch (FileNotFoundException ex) {
return null;
}
}
public URL getUrl() {
try {
return new URL("file:" + file.getAbsolutePath());
} catch (MalformedURLException ex) {
return null;
}
}
public long lastModified() {
return file.lastModified();
}
public String getContent() {
try {
InputStream in = getInputStream();
byte[] byteBuffer = new byte[in.available()];
in.read(byteBuffer);
in.close();
return new String(byteBuffer);
} catch (Exception ignore) {
return "";
}
}
public long getLength() {
return file.length();
}
public boolean exists() {
return file.exists();
}
public Repository getRepository() {
return repository;
}
public String toString() {
return getName();
}
}

View file

@ -0,0 +1,135 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.framework.repository;
import java.util.List;
import java.util.Iterator;
import java.io.IOException;
/**
* Repository represents an abstract container of resources (e.g. code, skins, ...).
* In addition to resources, repositories may contain other repositories, building
* a hierarchical structure.
*/
public interface Repository {
/**
* Checksum of the repository and all its content. Implementations
* should make sure
*
* @return checksum
* @throws IOException
*/
public long getChecksum() throws IOException;
/**
* Returns the date the repository was last modified.
*
* @return last modified date
* @throws IOException
*/
public long lastModified() throws IOException;
/**
* Returns a specific direct resource of the repository
*
* @param resourceName name of the child resource to return
* @return specified child resource
*/
public Resource getResource(String resourceName);
/**
* Returns all direct resources
*
* @return direct resources
* @throws IOException
*/
public Iterator getResources() throws IOException;
/**
* Returns all direct and indirect resources
*
* @return resources recursive
* @throws IOException
*/
public List getAllResources() throws IOException;
/**
* Returns this repository's direct child repositories
*
* @return direct repositories
* @throws IOException
*/
public Repository[] getRepositories() throws IOException;
/**
* Checks wether the repository actually (or still) exists
*
* @return true if the repository exists
* @throws IOException
*/
public boolean exists() throws IOException;
/**
* Creates the repository if does not exist yet
*
* @throws IOException
*/
public void create() throws IOException;
/**
* Checks wether the repository is to be considered a top-level
* repository from a scripting point of view. For example, a zip
* file within a file repository is not a root repository from
* a physical point of view, but from the scripting point of view it is.
*
* @return true if the repository is to be considered a top-level script repository
*/
public boolean isScriptRoot();
/**
* Returns this repository's parent repository.
* Returns null if this repository already is the top-level repository
*
* @return the parent repository
*/
public Repository getParentRepository();
/**
* Returns the top-level repository this repository is contained in
*
* @return top-level repository
*/
public Repository getRootRepository();
/**
* Returns the name of the repository; this is a full name including all
* parent repositories.
*
* @return full name of the repository
*/
public String getName();
/**
* Returns the name of the repository.
*
* @return name of the repository
*/
public String getShortName();
}

View file

@ -0,0 +1,86 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.framework.repository;
import java.io.InputStream;
import java.io.IOException;
import java.net.URL;
/**
* Resource represents a pointer to some kind of information (code, skin, ...)
* from which the content can be fetched
*/
public interface Resource {
/**
* Returns the date the resource was last modified
* @return last modified date
*/
public long lastModified();
/**
* Checks wether this resource actually (still) exists
* @return true if the resource exists
*/
public boolean exists();
/**
* Returns the lengh of the resource's content
* @return content length
*/
public long getLength() throws IOException;
/**
* Returns an input stream to the content of the resource
* @return content input stream
*/
public InputStream getInputStream() throws IOException;
/**
* Returns the content of the resource
* @return content
*/
public String getContent() throws IOException;
/**
* Returns the name of the resource; does not include the name of the
* repository the resource was fetched from
* @return name of the resource
*/
public String getName();
/**
* Returns the short name of the resource which is its name exclusive file
* ending if it exists
* @return short name of the resource
*/
public String getShortName();
/**
* Returns an url to the resource if the repository of this resource is
* able to provide urls
* @return url to the resource
*/
public URL getUrl() throws UnsupportedOperationException;
/**
* Returns the repository the resource does belong to
* @return upper repository
*/
public Repository getRepository();
}

View file

@ -0,0 +1,118 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.framework.repository;
import java.util.Comparator;
import helma.framework.core.Application;
/**
* Sorts resources according to the order of their repositories
*/
public class ResourceComparator implements Comparator {
// the application where the top-level repositories can be found
protected Application app;
/**
* Constructcs a ResourceComparator sorting according to the top-level
* repositories of the given application
* @param app application that provides the top-level repositories
*/
public ResourceComparator(Application app) {
this.app = app;
}
/**
* Compares two Repositories, Resources or RepositoryTrackers
* @param obj1 Repository, Resource or RepositoryTrackers
* @param obj2 Repository, Resource or RepositoryTrackers
* @return a negative integer, zero, or a positive integer as the
* first argument is less than, equal to, or greater than the
* second.
* @throws ClassCastException if the arguments' types prevent them from
* being compared by this Comparator.
*/
public int compare(Object obj1, Object obj2) {
if (obj1.equals(obj2))
return 0;
Repository rep1 = getRootRepository(obj1);
Repository rep2 = getRootRepository(obj2);
int pos1 = app.getRepositoryIndex(rep1);
int pos2 = app.getRepositoryIndex(rep2);
if (rep1 == rep2 || (pos1 == -1 && pos2 == -1)) {
// Same root repository, but we must not return 0 unless objects are equal
// (see JavaDoc on java.util.TreeSet) so we compare full names
return getFullName(obj1).compareTo(getFullName(obj2));
}
return pos1 - pos2;
}
/**
* Checks if the comparator is equal to the given comparator
* A ResourceComparator is equal to another ResourceComparator if the
* applications they belong to are equal
*
* @param obj comparator
* @return true if the given comparator equals
*/
public boolean equals(Object obj) {
return (obj instanceof ResourceComparator) &&
app == ((ResourceComparator) obj).getApplication();
}
/**
* Return the application we're comparing resources for
*
* @return the application instance
*/
public Application getApplication() {
return app;
}
private Repository getRootRepository(Object obj) {
if (obj instanceof Resource)
return ((Resource) obj).getRepository()
.getRootRepository();
if (obj instanceof ResourceTracker)
return ((ResourceTracker) obj).getResource()
.getRepository()
.getRootRepository();
if (obj instanceof Repository)
return ((Repository) obj).getRootRepository();
// something we can't compare
throw new IllegalArgumentException("Can't compare "+obj);
}
private String getFullName(Object obj) {
if (obj instanceof Resource)
return ((Resource) obj).getName();
if (obj instanceof ResourceTracker)
return ((ResourceTracker) obj).getResource()
.getName();
if (obj instanceof Repository)
return ((Repository) obj).getName();
// something we can't compare
throw new IllegalArgumentException("Can't compare "+obj);
}
}

View file

@ -0,0 +1,46 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.framework.repository;
import java.io.IOException;
/**
* A utility class that allows Resource consumers to track changes
* on resources.
*/
public class ResourceTracker {
Resource resource;
long lastModified;
public ResourceTracker(Resource resource) {
this.resource = resource;
markClean();
}
public boolean hasChanged() throws IOException {
return lastModified != resource.lastModified();
}
public void markClean() {
lastModified = resource.lastModified();
}
public Resource getResource() {
return resource;
}
}

View file

@ -0,0 +1,187 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.framework.repository;
import helma.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public final class ZipRepository extends AbstractRepository {
// zip file serving sub-repositories and zip file resources
private File file;
// the nested directory depth of this repository
private int depth;
private long lastModified = -1;
/**
* Constructs a ZipRespository using the given argument
* @param initArgs absolute path to the zip file
*/
public ZipRepository(String initArgs) {
this(new File(initArgs), null, null);
}
/**
* Constructs a ZipRepository using the given zip file as top-level
* repository
* @param file a zip file
*/
protected ZipRepository(File file, Repository parent) {
this(file, parent, null);
}
/**
* Constructs a ZipRepository using the zip entry belonging to the given
* zip file and top-level repository
* @param file a zip file
* @param zipentry zip entry
* @param parent repository
*/
private ZipRepository(File file, Repository parent, ZipEntry zipentry) {
this.file = file;
this.parent = parent;
if (zipentry == null) {
name = shortName = file.getName();
depth = 0;
} else {
String[] entrypath = StringUtils.split(zipentry.getName(), "/");
depth = entrypath.length;
shortName = entrypath[depth - 1];
name = new StringBuffer(parent.getName())
.append('/').append(shortName).toString();
}
}
/**
* Returns a java.util.zip.ZipFile for this repository. It is the caller's
* responsability to call close() in it when it is no longer needed.
* @return a ZipFile for reading
* @throws IOException
*/
protected ZipFile getZipFile() throws IOException {
return new ZipFile(file);
}
public synchronized void update() {
if (file.lastModified() != lastModified) {
lastModified = file.lastModified();
ZipFile zipfile = null;
try {
zipfile = getZipFile();
Enumeration enum = zipfile.entries();
ArrayList newRepositories = new ArrayList();
HashMap newResources = new HashMap();
while (enum.hasMoreElements()) {
ZipEntry entry = (ZipEntry) enum.nextElement();
String entryname = entry.getName();
String[] entrypath = StringUtils.split(entryname, "/");
// create new repositories and resources for all entries with a
// path depth of this.depth + 1
if (entrypath.length == depth + 1) {
if (entry.isDirectory()) {
newRepositories.add(new ZipRepository(file, this, entry));
} else {
ZipResource resource = new ZipResource(file, entry, this);
newResources.put(resource.getName(), resource);
}
}
}
repositories = (Repository[])
newRepositories.toArray(new Repository[newRepositories.size()]);
resources = newResources;
} catch (IOException ex) {
ex.printStackTrace();
repositories = new Repository[0];
if (resources == null) {
resources = new HashMap();
} else {
resources.clear();
}
} finally {
try {
// unlocks the zip file in the underlying filesystem
zipfile.close();
} catch (Exception ex) {}
}
}
}
public long getChecksum() {
return file.lastModified();
}
public boolean exists() {
ZipFile zipfile = null;
try {
/* a ZipFile needs to be created to see if the zip file actually
exists; this is not cached to provide blocking the zip file in
the underlying filesystem */
zipfile = getZipFile();
return true;
} catch (IOException ex) {
return false;
}
finally {
try {
// unlocks the zip file in the underlying filesystem
zipfile.close();
} catch (Exception ex) {
return false;
}
}
}
public void create() {
// we do not create zip files as it makes no sense
throw new UnsupportedOperationException("create() not implemented for ZipRepository");
}
/**
* Checks wether the repository is to be considered a top-level
* repository from a scripting point of view. For example, a zip
* file within a file repository is not a root repository from
* a physical point of view, but from the scripting point of view it is.
*
* @return true if the repository is to be considered a top-level script repository
*/
public boolean isScriptRoot() {
return depth == 0;
}
public long lastModified() {
return file.lastModified();
}
public String toString() {
return new StringBuffer("ZipRepository[").append(name).append("]").toString();
}
}

View file

@ -0,0 +1,139 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.framework.repository;
import java.io.*;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public final class ZipResource implements Resource {
private ZipEntry zipentry;
private File zipfile;
private ZipRepository repository;
private String name;
private String shortName;
protected ZipResource(File zipfile, ZipEntry zipentry, ZipRepository repository) {
this.zipentry = zipentry;
this.zipfile = zipfile;
this.repository = repository;
String entryname = zipentry.getName();
int lastDot = entryname.lastIndexOf('.');
int lastSlash = entryname.lastIndexOf('/');
if (lastDot != -1 && lastDot > lastSlash) {
shortName = entryname.substring(lastSlash + 1, lastDot);
} else {
shortName = entryname.substring(lastSlash + 1);
}
StringBuffer buf = new StringBuffer(repository.getName())
.append('/').append(shortName);
if (lastDot != -1 && lastDot > lastSlash) {
name = buf.append(entryname.substring(lastDot)).toString();
} else {
name = buf.toString();
}
}
public long lastModified() {
return zipfile.lastModified();
}
public InputStream getInputStream() throws IOException {
ZipFile zipfile = null;
try {
zipfile = repository.getZipFile();
int size = (int) zipentry.getSize();
byte[] buf = new byte[size];
InputStream in = zipfile.getInputStream(zipentry);
int read = 0;
while (read < size) {
int r = in.read(buf, read, size-read);
if (r == -1)
break;
read += r;
}
in.close();
return new ByteArrayInputStream(buf);
} finally {
zipfile.close();
}
}
public boolean exists() {
ZipFile zipfile = null;
try {
zipfile = repository.getZipFile();
return (zipfile.getEntry(zipentry.getName()) != null);
} catch (Exception ex) {
return false;
} finally {
try {
zipfile.close();
} catch (Exception ex) {}
}
}
public String getContent() throws IOException {
ZipFile zipfile = null;
try {
zipfile = repository.getZipFile();
InputStreamReader in = new InputStreamReader(zipfile.getInputStream(zipentry));
int size = (int) zipentry.getSize();
char[] buf = new char[size];
int read = 0;
while (read < size) {
int r = in.read(buf, read, size-read);
if (r == -1)
break;
read += r;
}
in.close();
return new String(buf);
} finally {
zipfile.close();
}
}
public String getName() {
return name;
}
public String getShortName() {
return shortName;
}
public URL getUrl() {
throw new UnsupportedOperationException("getUrl() not implemented for ZipResource");
}
public long getLength() {
return zipentry.getSize();
}
public Repository getRepository() {
return repository;
}
public String toString() {
return getName();
}
}

View file

@ -17,8 +17,9 @@
package helma.main; package helma.main;
import helma.framework.core.*; import helma.framework.core.*;
import helma.framework.repository.Repository;
import helma.framework.repository.FileRepository;
import helma.util.StringUtils; import helma.util.StringUtils;
import helma.util.SystemProperties;
import org.apache.xmlrpc.XmlRpcHandler; import org.apache.xmlrpc.XmlRpcHandler;
import org.mortbay.http.*; import org.mortbay.http.*;
import org.mortbay.http.handler.*; import org.mortbay.http.handler.*;
@ -26,6 +27,7 @@ import org.mortbay.jetty.servlet.*;
import java.io.*; import java.io.*;
import java.rmi.*; import java.rmi.*;
import java.util.*; import java.util.*;
import helma.util.ResourceProperties;
/** /**
* This class is responsible for starting and stopping Helma applications. * This class is responsible for starting and stopping Helma applications.
@ -35,7 +37,7 @@ public class ApplicationManager implements XmlRpcHandler {
private Hashtable applications; private Hashtable applications;
private Hashtable xmlrpcHandlers; private Hashtable xmlrpcHandlers;
private int rmiPort; private int rmiPort;
private SystemProperties props; private ResourceProperties props;
private Server server; private Server server;
private long lastModified; private long lastModified;
@ -46,7 +48,7 @@ public class ApplicationManager implements XmlRpcHandler {
* @param server the server instance * @param server the server instance
* @param port The RMI port we're binding to * @param port The RMI port we're binding to
*/ */
public ApplicationManager(SystemProperties props, public ApplicationManager(ResourceProperties props,
Server server, int port) { Server server, int port) {
this.props = props; this.props = props;
this.server = server; this.server = server;
@ -282,6 +284,7 @@ public class ApplicationManager implements XmlRpcHandler {
String uploadLimit; String uploadLimit;
String debug; String debug;
boolean encode; boolean encode;
Repository[] repositories;
/** /**
* Creates an AppDescriptor from the properties. * Creates an AppDescriptor from the properties.
@ -313,6 +316,60 @@ public class ApplicationManager implements XmlRpcHandler {
appDir = (appDirName == null) ? null : new File(appDirName); appDir = (appDirName == null) ? null : new File(appDirName);
String dbDirName = props.getProperty(name + ".dbdir"); String dbDirName = props.getProperty(name + ".dbdir");
dbDir = (dbDirName == null) ? null : new File(dbDirName); dbDir = (dbDirName == null) ? null : new File(dbDirName);
// read and configure app repositories
ArrayList repositoryList = new ArrayList();
for (int i = 0; true; i++) {
Class[] parameters = { String.class };
String[] repositoryArgs = { props.getProperty(name + ".repository." + i) };
if (repositoryArgs[0] != null) {
// lookup repository implementation
String repositoryImpl = props.getProperty(name + ".repository." + i +
".implementation");
if (repositoryImpl == null) {
// implementation not set manually, have to guess it
if (repositoryArgs[0].endsWith(".zip")) {
repositoryImpl = "helma.framework.repository.ZipRepository";
} else {
repositoryImpl = "helma.framework.repository.FileRepository";
}
}
Repository newRepository = null;
try {
newRepository = (Repository) Class.forName(repositoryImpl)
.getConstructor(parameters).newInstance(repositoryArgs);
repositoryList.add(newRepository);
} catch (Exception ex) {
System.out.println("Adding repository " + repositoryArgs + " failed. " +
"Will not use that repository. Check your initArgs!");
}
} else {
// no more repositories to add
break;
}
}
if (repositoryList.size() > 0) {
repositories = new Repository[repositoryList.size()];
repositoryList.toArray(repositories);
} else {
repositories = new Repository[1];
if (appDir != null) {
repositories[0] = new FileRepository(appDir);
} else {
repositories[0] = new FileRepository(new File(server.getAppsHome(), appName));
}
try {
if (!repositories[0].exists()) {
repositories[0].create();
}
} catch (Exception swallow) {
// couldn't create repository
}
}
} }
@ -321,7 +378,7 @@ public class ApplicationManager implements XmlRpcHandler {
try { try {
// create the application instance // create the application instance
app = new Application(appName, server, appDir, dbDir); app = new Application(appName, server, repositories, dbDir);
// register ourselves // register ourselves
descriptors.put(appName, this); descriptors.put(appName, this);

View file

@ -18,6 +18,7 @@ package helma.main;
import helma.extensions.HelmaExtension; import helma.extensions.HelmaExtension;
import helma.framework.*; import helma.framework.*;
import helma.framework.repository.FileResource;
import helma.framework.core.*; import helma.framework.core.*;
import helma.objectmodel.db.DbSource; import helma.objectmodel.db.DbSource;
import helma.util.*; import helma.util.*;
@ -31,10 +32,10 @@ import org.mortbay.util.LogSink;
import org.mortbay.util.MultiException; import org.mortbay.util.MultiException;
import org.mortbay.util.Frame; import org.mortbay.util.Frame;
import java.io.*; import java.io.*;
import java.net.*;
import java.rmi.registry.*; import java.rmi.registry.*;
import java.rmi.server.*; import java.rmi.server.*;
import java.util.*; import java.util.*;
import helma.util.ResourceProperties;
/** /**
* Helma server main class. * Helma server main class.
@ -50,9 +51,9 @@ public class Server implements IPathElement, Runnable {
protected File hopHome; protected File hopHome;
// server-wide properties // server-wide properties
SystemProperties appsProps; ResourceProperties appsProps;
SystemProperties dbProps; ResourceProperties dbProps;
SystemProperties sysProps; ResourceProperties sysProps;
// our logger // our logger
private Log logger; private Log logger;
@ -105,7 +106,8 @@ public class Server implements IPathElement, Runnable {
hopHome = config.homeDir; hopHome = config.homeDir;
// create system properties // create system properties
sysProps = new SystemProperties(config.propFile.getAbsolutePath()); sysProps = new ResourceProperties();
sysProps.addResource(new FileResource(config.propFile));
} }
@ -184,7 +186,8 @@ public class Server implements IPathElement, Runnable {
guessConfig(config); guessConfig(config);
// create system properties // create system properties
SystemProperties sysProps = new SystemProperties(config.propFile.getAbsolutePath()); ResourceProperties sysProps = new ResourceProperties();
sysProps.addResource(new FileResource(config.propFile));
// check if there's a property setting for those ports not specified via command line // check if there's a property setting for those ports not specified via command line
if ((config.websrvPort == null) && (sysProps.getProperty("webPort") != null)) { if ((config.websrvPort == null) && (sysProps.getProperty("webPort") != null)) {
@ -283,7 +286,8 @@ public class Server implements IPathElement, Runnable {
} }
// create system properties // create system properties
SystemProperties sysProps = new SystemProperties(config.propFile.getAbsolutePath()); ResourceProperties sysProps = new ResourceProperties();
sysProps.addResource(new FileResource(config.propFile));
// try to get hopHome from property file // try to get hopHome from property file
if (config.homeDir == null && sysProps.getProperty("hophome") != null) { if (config.homeDir == null && sysProps.getProperty("hophome") != null) {
@ -442,18 +446,21 @@ public class Server implements IPathElement, Runnable {
// read db.properties file in helma home directory // read db.properties file in helma home directory
File helper = new File(hopHome, "db.properties"); dbProps = new ResourceProperties();
dbProps = new SystemProperties(helper.getAbsolutePath()); dbProps.addResource(new FileResource(new File(hopHome, "db.properties")));
DbSource.setDefaultProps(dbProps); DbSource.setDefaultProps(dbProps);
// read apps.properties file // read apps.properties file
String appsPropfile = sysProps.getProperty("appsPropFile"); String appsPropfile = sysProps.getProperty("appsPropFile");
File file;
if ((appsPropfile != null) && !"".equals(appsPropfile.trim())) { if ((appsPropfile != null) && !"".equals(appsPropfile.trim())) {
helper = new File(appsPropfile); file = new File(appsPropfile);
appsProps = new ResourceProperties();
} else { } else {
helper = new File(hopHome, "apps.properties"); file = new File(hopHome, "apps.properties");
appsProps = new ResourceProperties();
} }
appsProps = new SystemProperties(helper.getAbsolutePath()); appsProps.addResource(new FileResource(file));
paranoid = "true".equalsIgnoreCase(sysProps.getProperty("paranoid")); paranoid = "true".equalsIgnoreCase(sysProps.getProperty("paranoid"));
@ -804,7 +811,7 @@ public class Server implements IPathElement, Runnable {
* *
* @return ... * @return ...
*/ */
public SystemProperties getProperties() { public ResourceProperties getProperties() {
return sysProps; return sysProps;
} }
@ -813,7 +820,7 @@ public class Server implements IPathElement, Runnable {
* *
* @return ... * @return ...
*/ */
public SystemProperties getDbProperties() { public ResourceProperties getDbProperties() {
return dbProps; return dbProps;
} }

View file

@ -18,8 +18,7 @@ package helma.objectmodel.db;
import helma.framework.core.Application; import helma.framework.core.Application;
import helma.framework.core.Prototype; import helma.framework.core.Prototype;
import helma.util.SystemProperties; import helma.util.ResourceProperties;
import helma.util.Updatable;
import java.sql.*; import java.sql.*;
import java.util.*; import java.util.*;
@ -29,7 +28,7 @@ import java.util.*;
* relational database table. Basically it consists of a set of JavaScript property-to- * relational database table. Basically it consists of a set of JavaScript property-to-
* Database row bindings which are represented by instances of the Relation class. * Database row bindings which are represented by instances of the Relation class.
*/ */
public final class DbMapping implements Updatable { public final class DbMapping {
// DbMappings belong to an application // DbMappings belong to an application
protected Application app; protected Application app;
@ -37,7 +36,7 @@ public final class DbMapping implements Updatable {
private String typename; private String typename;
// properties from where the mapping is read // properties from where the mapping is read
private SystemProperties props; private ResourceProperties props;
// name of data dbSource to which this mapping writes // name of data dbSource to which this mapping writes
private DbSource dbSource; private DbSource dbSource;
@ -131,7 +130,7 @@ public final class DbMapping implements Updatable {
/** /**
* Create a DbMapping from a type.properties property file * Create a DbMapping from a type.properties property file
*/ */
public DbMapping(Application app, String typename, SystemProperties props) { public DbMapping(Application app, String typename, ResourceProperties props) {
this.app = app; this.app = app;
// create a unique instance of the string. This is useful so // create a unique instance of the string. This is useful so
// we can compare types just by using == instead of equals. // we can compare types just by using == instead of equals.
@ -1349,7 +1348,7 @@ public final class DbMapping implements Updatable {
* *
* @return ... * @return ...
*/ */
public SystemProperties getProperties() { public ResourceProperties getProperties() {
return props; return props;
} }
} }

View file

@ -16,7 +16,7 @@
package helma.objectmodel.db; package helma.objectmodel.db;
import helma.util.SystemProperties; import helma.util.ResourceProperties;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
@ -28,10 +28,10 @@ import java.util.Properties;
* This class describes a releational data source (URL, driver, user and password). * This class describes a releational data source (URL, driver, user and password).
*/ */
public class DbSource { public class DbSource {
private static SystemProperties defaultProps = null; private static ResourceProperties defaultProps = null;
private Properties conProps; private Properties conProps;
private String name; private String name;
private SystemProperties props; private ResourceProperties props;
protected String url; protected String url;
private String driver; private String driver;
private boolean isOracle; private boolean isOracle;
@ -45,7 +45,7 @@ public class DbSource {
* *
* @throws ClassNotFoundException ... * @throws ClassNotFoundException ...
*/ */
public DbSource(String name, SystemProperties props) public DbSource(String name, ResourceProperties props)
throws ClassNotFoundException { throws ClassNotFoundException {
this.name = name; this.name = name;
this.props = props; this.props = props;
@ -158,7 +158,7 @@ public class DbSource {
* *
* @param props ... * @param props ...
*/ */
public static void setDefaultProps(SystemProperties props) { public static void setDefaultProps(ResourceProperties props) {
defaultProps = props; defaultProps = props;
} }

View file

@ -1,213 +0,0 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.scripting;
import helma.framework.core.*;
import helma.util.Updatable;
import java.io.*;
/**
* An ActionFile is a file containing function code that is exposed as a URI
* of objects of this class/type. It is
* usually represented by a file with extension .hac (hop action file)
* that contains the raw body of the function.
*/
public class ActionFile implements Updatable {
String name;
String sourceName;
Prototype prototype;
File file;
String content;
long lastmod;
/**
* Creates a new ActionFile object.
*
* @param file ...
* @param name ...
* @param sourceName ...
* @param proto ...
*/
public ActionFile(File file, String name, String sourceName, Prototype proto) {
this.prototype = proto;
this.name = name;
this.sourceName = sourceName;
this.file = file;
this.lastmod = file.lastModified();
this.content = null;
}
/**
* Creates a new ActionFile object.
*
* @param content ...
* @param name ...
* @param sourceName ...
* @param proto ...
*/
public ActionFile(String content, String name, String sourceName, Prototype proto) {
this.prototype = proto;
this.name = name;
this.sourceName = sourceName;
this.file = null;
this.content = content;
}
/**
* Tell the type manager whether we need an update. this is the case when
* the file has been modified or deleted.
*/
public boolean needsUpdate() {
return lastmod != file.lastModified();
}
/**
*
*/
public void update() {
if (!file.exists()) {
// remove functions declared by this from all object prototypes
remove();
} else {
lastmod = file.lastModified();
}
}
/**
*
*/
public void remove() {
prototype.removeActionFile(this);
}
/**
*
*
* @return ...
*/
public File getFile() {
return file;
}
/**
*
*
* @return ...
*/
public String getName() {
return name;
}
/**
*
*
* @return ...
*/
public String getSourceName() {
return sourceName;
}
/**
*
*
* @return ...
*
* @throws FileNotFoundException ...
*/
public Reader getReader() throws FileNotFoundException {
if (content != null) {
return new StringReader(content);
} else if (file.length() == 0) {
return new StringReader(";");
} else {
return new FileReader(file);
}
}
/**
*
*
* @return ...
*/
public String getContent() {
if (content != null) {
return content;
} else {
try {
FileReader reader = new FileReader(file);
char[] cbuf = new char[(int) file.length()];
reader.read(cbuf);
reader.close();
return new String(cbuf);
} catch (Exception filex) {
prototype.getApplication().logEvent("Error reading " + this + ": " + filex);
return null;
}
}
}
/**
*
*
* @return ...
*/
public String getFunctionName() {
return name + "_action";
}
/**
*
*
* @return ...
*/
public Prototype getPrototype() {
return prototype;
}
/**
*
*
* @return ...
*/
public String toString() {
return "ActionFile[" + sourceName + "]";
}
/**
* Override to produce hash code depending on source name
*
* @return a hash code value for this object.
*/
public int hashCode() {
return sourceName.hashCode();
}
/**
* Override to equal other ActionFile with the same source name
*
* @param obj the object to compare to
* @return true if obj is a ActionFile with the same source name
*/
public boolean equals(Object obj) {
return (obj instanceof ActionFile) &&
sourceName.equals(((ActionFile) obj).getSourceName());
}
}

View file

@ -1,146 +0,0 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.scripting;
import helma.framework.core.*;
import helma.util.Updatable;
import java.io.*;
/**
* This represents a File containing script functions for a given class/prototype.
*/
public class FunctionFile implements Updatable {
Prototype prototype;
File file;
String sourceName;
String content;
long lastmod;
/**
* Creates a new FunctionFile object.
*
* @param file ...
* @param sourceName ...
* @param proto ...
*/
public FunctionFile(File file, String sourceName, Prototype proto) {
this.prototype = proto;
this.sourceName = sourceName;
this.file = file;
update();
}
/**
* Create a function file without a file, passing the code directly. This is used for
* files contained in zipped applications. The whole update mechanism is bypassed
* by immediately parsing the code.
*
* @param body ...
* @param sourceName ...
* @param proto ...
*/
public FunctionFile(String body, String sourceName, Prototype proto) {
this.prototype = proto;
this.sourceName = sourceName;
this.file = null;
this.content = body;
}
/**
* Tell the type manager whether we need an update. this is the case when
* the file has been modified or deleted.
*/
public boolean needsUpdate() {
return (file != null) && (lastmod != file.lastModified());
}
/**
*
*/
public void update() {
if (file != null) {
if (!file.exists()) {
remove();
} else {
lastmod = file.lastModified();
}
}
}
/**
*
*
* @return ...
*/
public File getFile() {
return file;
}
/**
*
*
* @return ...
*/
public String getContent() {
return content;
}
/**
*
*
* @return ...
*/
public String getSourceName() {
return sourceName;
}
/**
*
*/
public void remove() {
prototype.removeFunctionFile(this);
}
/**
*
*
* @return ...
*/
public String toString() {
return sourceName;
}
/**
* Override to produce hash code depending on source name
*
* @return a hash code value for this object.
*/
public int hashCode() {
return sourceName.hashCode();
}
/**
* Override to equal other FunctionFiles with the same source name
*
* @param obj the object to compare to
* @return true if obj is a FunctionFile with the same source name
*/
public boolean equals(Object obj) {
return (obj instanceof FunctionFile) &&
sourceName.equals(((FunctionFile) obj).getSourceName());
}
}

View file

@ -1,259 +0,0 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.scripting;
import helma.framework.core.*;
import java.io.*;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* This represents a Helma template, i.e. a file with the extension .hsp
* (Helma server page) that contains both parts that are to be evaluated
* as EcmaScript and parts that are to be delivered to the client as-is.
* Internally, templates are regular functions.
* Helma templates are callable via URL, but this is just a leftover from the
* days when there were no .hac (action) files. The recommended way
* now is to have a .hac file with all the logic which in turn calls one or more
* template files to do the formatting.
*/
public class Template extends ActionFile {
/**
* Creates a new Template object.
*
* @param file ...
* @param name ...
* @param sourceName
* @param proto ...
*/
public Template(File file, String name, String sourceName, Prototype proto) {
super(file, name, sourceName, proto);
}
/**
* Creates a new Template object.
*
* @param content ...
* @param name ...
* @param sourceName ...
* @param proto ...
*/
public Template(String content, String name, String sourceName, Prototype proto) {
super(content, name, sourceName, proto);
}
/**
*
*
* @return ...
*/
public String getFunctionName() {
return name;
}
/**
*
*
* @return ...
*/
public Reader getReader() {
return new StringReader(getContent());
}
/**
*
*
* @return ...
*/
public String getContent() {
Vector partBuffer = new Vector();
String cstring = super.getContent();
char[] cnt = cstring.toCharArray();
int l = cnt.length;
if (l == 0) {
return "";
}
// if last charackter is whitespace, swallow it. this is necessary for some inner templates to look ok.
if (Character.isWhitespace(cnt[l - 1])) {
l -= 1;
}
int lastIdx = 0;
for (int i = 0; i < (l - 1); i++) {
if ((cnt[i] == '<') && (cnt[i + 1] == '%')) {
int j = i + 2;
while ((j < (l - 1)) && ((cnt[j] != '%') || (cnt[j + 1] != '>'))) {
j++;
}
if (j > (i + 2)) {
if ((i - lastIdx) > 0) {
partBuffer.addElement(new Part(this,
new String(cnt, lastIdx,
i - lastIdx), true));
}
String script = new String(cnt, i + 2, (j - i) - 2);
partBuffer.addElement(new Part(this, script, false));
lastIdx = j + 2;
}
i = j + 1;
}
}
if (lastIdx < l) {
partBuffer.addElement(new Part(this, new String(cnt, lastIdx, l - lastIdx),
true));
}
StringBuffer templateBody = new StringBuffer();
int nparts = partBuffer.size();
for (int k = 0; k < nparts; k++) {
Part nextPart = (Part) partBuffer.elementAt(k);
if (nextPart.isStatic || nextPart.content.trim().startsWith("=")) {
// check for <%= ... %> statements
if (!nextPart.isStatic) {
nextPart.content = nextPart.content.trim().substring(1).trim();
// cut trailing ";"
while (nextPart.content.endsWith(";"))
nextPart.content = nextPart.content.substring(0,
nextPart.content.length() -
1);
}
StringTokenizer st = new StringTokenizer(nextPart.content, "\r\n", true);
String nextLine = st.hasMoreTokens() ? st.nextToken() : null;
// count newLines we "swallow", see explanation below
int newLineCount = 0;
templateBody.append("res.write (");
if (nextPart.isStatic) {
templateBody.append("\"");
}
while (nextLine != null) {
if ("\n".equals(nextLine)) {
// append a CRLF
newLineCount++;
templateBody.append("\\r\\n");
} else if (!"\r".equals(nextLine)) {
try {
StringReader lineReader = new StringReader(nextLine);
int c = lineReader.read();
while (c > -1) {
if (nextPart.isStatic &&
(((char) c == '"') || ((char) c == '\\'))) {
templateBody.append('\\');
}
templateBody.append((char) c);
c = lineReader.read();
}
} catch (IOException srx) {
}
}
nextLine = st.hasMoreTokens() ? st.nextToken() : null;
}
if (nextPart.isStatic) {
templateBody.append("\"");
}
templateBody.append("); ");
// append the number of lines we have "swallowed" into
// one write statement, so error messages will *approximately*
// give correct line numbers.
for (int i = 0; i < newLineCount; i++) {
templateBody.append("\r\n");
}
} else {
templateBody.append(nextPart.content);
if (!nextPart.content.trim().endsWith(";")) {
templateBody.append(";");
}
}
}
// templateBody.append ("\r\nreturn null;\r\n");
return templateBody.toString();
}
/**
*
*/
public void remove() {
prototype.removeTemplate(this);
}
class Part {
String content;
Template parent;
boolean isPart;
boolean isStatic;
public Part(Template parent, String content, boolean isStatic) {
isPart = false;
this.parent = parent;
this.content = content;
this.isStatic = isStatic;
}
public String getName() {
return isStatic ? null : content;
}
public String toString() {
return "Template.Part [" + content + "," + isStatic + "]";
}
}
/**
* Override to produce hash code depending on source name
*
* @return a hash code value for this object.
*/
public int hashCode() {
return sourceName.hashCode();
}
/**
* Override to equal other Template with the same source name
*
* @param obj the object to compare to
* @return true if obj is a Template with the same source name
*/
public boolean equals(Object obj) {
return (obj instanceof Template) &&
sourceName.equals(((Template) obj).getSourceName());
}
}

View file

@ -105,7 +105,7 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
* @return ... * @return ...
*/ */
public boolean renderSkin(Object skinobj, Object paramobj) public boolean renderSkin(Object skinobj, Object paramobj)
throws UnsupportedEncodingException { throws UnsupportedEncodingException, IOException {
Context cx = Context.getCurrentContext(); Context cx = Context.getCurrentContext();
RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval"); RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval");
RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine"); RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine");
@ -139,7 +139,7 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
* @return ... * @return ...
*/ */
public String renderSkinAsString(Object skinobj, Object paramobj) public String renderSkinAsString(Object skinobj, Object paramobj)
throws UnsupportedEncodingException { throws UnsupportedEncodingException, IOException {
Context cx = Context.getCurrentContext(); Context cx = Context.getCurrentContext();
RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval"); RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval");
RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine"); RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine");

View file

@ -27,6 +27,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.*; import java.util.*;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.io.IOException;
/** /**
* *
@ -250,7 +251,7 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
* @return ... * @return ...
*/ */
public boolean jsFunction_renderSkin(Object skinobj, Object paramobj) public boolean jsFunction_renderSkin(Object skinobj, Object paramobj)
throws UnsupportedEncodingException { throws UnsupportedEncodingException, IOException {
Context cx = Context.getCurrentContext(); Context cx = Context.getCurrentContext();
RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval"); RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval");
RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine"); RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine");
@ -284,7 +285,7 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
* @return ... * @return ...
*/ */
public String jsFunction_renderSkinAsString(Object skinobj, Object paramobj) public String jsFunction_renderSkinAsString(Object skinobj, Object paramobj)
throws UnsupportedEncodingException { throws UnsupportedEncodingException, IOException {
Context cx = Context.getCurrentContext(); Context cx = Context.getCurrentContext();
RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval"); RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval");
RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine"); RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine");
@ -319,7 +320,8 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
* *
* @return ... * @return ...
*/ */
public Object jsFunction_href(Object action) throws UnsupportedEncodingException { public Object jsFunction_href(Object action) throws UnsupportedEncodingException,
IOException {
if (node == null) { if (node == null) {
return null; return null;
} }
@ -790,9 +792,6 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
* @return ... * @return ...
*/ */
public Object get(String name, Scriptable start) { public Object get(String name, Scriptable start) {
// System.err.println("GET from "+this+": "+name+" ->"+super.get(name, start));
Object retval = null;
if (node == null) { if (node == null) {
return super.get(name, start); return super.get(name, start);
} else { } else {

View file

@ -22,6 +22,7 @@ import java.lang.reflect.Method;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.io.IOException;
/** /**
* *
@ -70,7 +71,7 @@ public class JavaObject extends NativeJavaObject {
* @return ... * @return ...
*/ */
public boolean renderSkin(Object skinobj, Object paramobj) public boolean renderSkin(Object skinobj, Object paramobj)
throws UnsupportedEncodingException { throws UnsupportedEncodingException, IOException {
Context cx = Context.getCurrentContext(); Context cx = Context.getCurrentContext();
RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval"); RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval");
RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine"); RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine");
@ -104,7 +105,7 @@ public class JavaObject extends NativeJavaObject {
* @return ... * @return ...
*/ */
public String renderSkinAsString(Object skinobj, Object paramobj) public String renderSkinAsString(Object skinobj, Object paramobj)
throws UnsupportedEncodingException { throws UnsupportedEncodingException, IOException {
Context cx = Context.getCurrentContext(); Context cx = Context.getCurrentContext();
RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval"); RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval");
RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine"); RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine");
@ -139,7 +140,8 @@ public class JavaObject extends NativeJavaObject {
* *
* @return ... * @return ...
*/ */
public Object href(Object action) throws UnsupportedEncodingException { public Object href(Object action) throws UnsupportedEncodingException,
IOException {
if (javaObject == null) { if (javaObject == null) {
return null; return null;
} }

View file

@ -16,7 +16,9 @@
package helma.scripting.rhino; package helma.scripting.rhino;
import helma.scripting.*; import helma.framework.repository.Resource;
import java.io.IOException;
/** /**
* An class that updates fesi interpreters with actionfiles and templates. * An class that updates fesi interpreters with actionfiles and templates.
@ -31,9 +33,9 @@ public class RhinoActionAdapter {
* *
* @param action ... * @param action ...
*/ */
public RhinoActionAdapter(ActionFile action) { public RhinoActionAdapter(Resource action) throws IOException {
String content = action.getContent(); String content = action.getContent();
String functionName = action.getFunctionName().replace('.', '_'); String functionName = action.getShortName().replace('.', '_');
sourceName = action.toString(); sourceName = action.toString();
function = composeFunction(functionName, function = composeFunction(functionName,
@ -41,7 +43,7 @@ public class RhinoActionAdapter {
content); content);
// check if this is a template and we need to generate an "_as_string" variant // check if this is a template and we need to generate an "_as_string" variant
if (action instanceof Template) { if (action.getName().endsWith(".hsp")) {
functionAsString = composeFunction(functionName + "_as_string", functionAsString = composeFunction(functionName + "_as_string",
"arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10",
"res.pushStringBuffer(); " + content + "res.pushStringBuffer(); " + content +

View file

@ -18,6 +18,7 @@ package helma.scripting.rhino;
import helma.scripting.rhino.extensions.*; import helma.scripting.rhino.extensions.*;
import helma.framework.core.*; import helma.framework.core.*;
import helma.framework.repository.Resource;
import helma.objectmodel.*; import helma.objectmodel.*;
import helma.objectmodel.db.DbMapping; import helma.objectmodel.db.DbMapping;
import helma.objectmodel.db.DbKey; import helma.objectmodel.db.DbKey;
@ -215,18 +216,9 @@ public final class RhinoCore {
} }
// loop through the prototype's code elements and evaluate them // loop through the prototype's code elements and evaluate them
// first the zipped ones ... Iterator code = prototype.getCodeResources();
for (Iterator it = prototype.getZippedCode().values().iterator(); it.hasNext();) { while (code.hasNext()) {
Object code = it.next(); evaluate(type, (Resource) code.next());
evaluate(type, code);
}
// then the unzipped ones (this is to make sure unzipped code overwrites zipped code)
for (Iterator it = prototype.getCode().values().iterator(); it.hasNext();) {
Object code = it.next();
evaluate(type, code);
} }
type.commitCompilation(); type.commitCompilation();
@ -310,7 +302,9 @@ public final class RhinoCore {
TypeInfo type = (TypeInfo) prototypes.get("global"); TypeInfo type = (TypeInfo) prototypes.get("global");
updatePrototype(type, checked); if (type != null) {
updatePrototype(type, checked);
}
for (Iterator i = protos.iterator(); i.hasNext();) { for (Iterator i = protos.iterator(); i.hasNext();) {
Prototype proto = (Prototype) i.next(); Prototype proto = (Prototype) i.next();
@ -624,7 +618,7 @@ public final class RhinoCore {
} }
protected String postProcessHref(Object obj, String protoName, String basicHref) protected String postProcessHref(Object obj, String protoName, String basicHref)
throws UnsupportedEncodingException { throws UnsupportedEncodingException, IOException {
// check if the app.properties specify a href-function to post-process the // check if the app.properties specify a href-function to post-process the
// basic href. // basic href.
String hrefFunction = app.getProperty("hrefFunction", null); String hrefFunction = app.getProperty("hrefFunction", null);
@ -750,46 +744,10 @@ public final class RhinoCore {
// private evaluation/compilation methods // private evaluation/compilation methods
//////////////////////////////////////////////// ////////////////////////////////////////////////
private synchronized void evaluate(TypeInfo type, Object code) { private synchronized void evaluate (TypeInfo type, Resource code) {
if (code instanceof FunctionFile) {
FunctionFile funcfile = (FunctionFile) code;
File file = funcfile.getFile();
if (file != null) {
try {
FileReader fr = new FileReader(file);
updateEvaluator(type, fr, funcfile.getSourceName(), 1);
} catch (IOException iox) {
app.logEvent("Error updating function file: " + iox);
}
} else {
StringReader reader = new StringReader(funcfile.getContent());
updateEvaluator(type, reader, funcfile.getSourceName(), 1);
}
} else if (code instanceof ActionFile) {
ActionFile action = (ActionFile) code;
RhinoActionAdapter fa = new RhinoActionAdapter(action);
try {
updateEvaluator(type, new StringReader(fa.function),
action.getSourceName(), 0);
if (fa.functionAsString != null) {
// templates have an _as_string variant that needs to be compiled
updateEvaluator(type, new StringReader(fa.functionAsString),
action.getSourceName(), 0);
}
} catch (Exception esx) {
app.logEvent("Error parsing " + action + ": " + esx);
}
}
}
private synchronized void updateEvaluator(TypeInfo type, Reader reader,
String sourceName, int firstline) {
// System.err.println("UPDATE EVALUATOR: "+prototype+" - "+sourceName);
Scriptable threadScope = global.unregisterScope(); Scriptable threadScope = global.unregisterScope();
String sourceName = code.getName();
Reader reader = null;
try { try {
// get the current context // get the current context
@ -798,8 +756,14 @@ public final class RhinoCore {
Scriptable op = type.objProto; Scriptable op = type.objProto;
// do the update, evaluating the file // do the update, evaluating the file
// Script script = cx.compileReader(reader, sourceName, firstline, null); if (sourceName.endsWith(".js")) {
cx.evaluateReader(op, reader, sourceName, firstline, null); reader = new InputStreamReader(code.getInputStream());
cx.evaluateReader(op, reader, sourceName, 1, null);
} else if (sourceName.endsWith(".hac")) {
RhinoActionAdapter raa = new RhinoActionAdapter(code);
reader = new StringReader(raa.function);
cx.evaluateReader(op, reader, sourceName, 0, null);
}
} catch (Exception e) { } catch (Exception e) {
app.logEvent("Error parsing file " + sourceName + ": " + e); app.logEvent("Error parsing file " + sourceName + ": " + e);
@ -935,7 +899,7 @@ public final class RhinoCore {
} }
// mark this type as updated // mark this type as updated
lastUpdate = frameworkProto.getLastUpdate(); lastUpdate = frameworkProto.lastCodeUpdate();
// If this prototype defines a postCompile() function, call it // If this prototype defines a postCompile() function, call it
Context cx = Context.getCurrentContext(); Context cx = Context.getCurrentContext();
@ -953,7 +917,7 @@ public final class RhinoCore {
} }
public boolean needsUpdate() { public boolean needsUpdate() {
return frameworkProto.getLastUpdate() > lastUpdate; return frameworkProto.lastCodeUpdate() > lastUpdate;
} }
public void setParentType(TypeInfo type) { public void setParentType(TypeInfo type) {

View file

@ -30,6 +30,8 @@ import helma.scripting.rhino.debug.Tracer;
import org.mozilla.javascript.*; import org.mozilla.javascript.*;
import java.util.*; import java.util.*;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
/** /**
@ -432,7 +434,7 @@ public class RhinoEngine implements ScriptingEngine {
*/ */
public IPathElement getIntrospector() { public IPathElement getIntrospector() {
if (doc == null) { if (doc == null) {
doc = new DocApplication(app.getName(), app.getAppDir().toString()); doc = new DocApplication(app.getName(), new File(Server.getServer().getAppsHome(), app.getName()));
doc.readApplication(); doc.readApplication();
} }
return doc; return doc;
@ -482,7 +484,7 @@ public class RhinoEngine implements ScriptingEngine {
* skinpath set in the current response object and does per-response skin * skinpath set in the current response object and does per-response skin
* caching. * caching.
*/ */
public Skin getSkin(String protoName, String skinName) { public Skin getSkin(String protoName, String skinName) throws IOException {
SkinKey key = new SkinKey(protoName, skinName); SkinKey key = new SkinKey(protoName, skinName);
Skin skin = reval.res.getCachedSkin(key); Skin skin = reval.res.getCachedSkin(key);

View file

@ -17,7 +17,10 @@
package helma.servlet; package helma.servlet;
import helma.framework.*; import helma.framework.*;
import helma.framework.repository.Repository;
import helma.framework.core.Application; import helma.framework.core.Application;
import helma.framework.repository.Repository;
import helma.framework.repository.FileRepository;
import java.io.*; import java.io.*;
import javax.servlet.*; import javax.servlet.*;
@ -92,10 +95,11 @@ public final class StandaloneServletClient extends AbstractServletClient {
} }
try { try {
File appHome = new File(appDir); Repository[] repositories = new Repository[1];
repositories[0] = new FileRepository(new File(appDir));
File dbHome = new File(dbDir); File dbHome = new File(dbDir);
app = new Application(appName, appHome, dbHome); app = new Application(appName, repositories, dbHome);
app.init(); app.init();
app.start(); app.start();
} catch (Exception x) { } catch (Exception x) {

View file

@ -0,0 +1,124 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.util;
import java.io.BufferedReader;
import java.io.StringReader;
import java.util.Properties;
import java.util.StringTokenizer;
import helma.framework.repository.Resource;
import helma.framework.repository.Resource;
/**
* This file authenticates against a passwd source
*/
public class CryptResource {
private Properties users;
private CryptResource parentResource;
private Resource resource;
private long lastRead = 0;
/**
* Creates a new CryptSource object.
*
* @param resource ...
* @param parentResource ...
*/
public CryptResource(Resource resource, CryptResource parentResource) {
this.resource = resource;
this.parentResource = parentResource;
users = new Properties();
}
/**
*
*
* @param username ...
* @param pw ...
*
* @return ...
*/
public boolean authenticate(String username, String pw) {
if (resource.exists() && (resource.lastModified() > lastRead)) {
readFile();
} else if (!resource.exists() && (users.size() > 0)) {
users.clear();
}
String realpw = users.getProperty(username);
if (realpw != null) {
try {
// check if password matches
// first we try with unix crypt algorithm
String cryptpw = Crypt.crypt(realpw, pw);
if (realpw.equals(cryptpw)) {
return true;
}
// then try MD5
if (realpw.equals(MD5Encoder.encode(pw))) {
return true;
}
} catch (Exception x) {
return false;
}
} else {
if (parentResource != null) {
return parentResource.authenticate(username, pw);
}
}
return false;
}
private synchronized void readFile() {
BufferedReader reader = null;
users = new Properties();
try {
reader = new BufferedReader(new StringReader(resource.getContent()));
String line = reader.readLine();
while (line != null) {
StringTokenizer st = new StringTokenizer(line, ":");
if (st.countTokens() > 1) {
users.put(st.nextToken(), st.nextToken());
}
line = reader.readLine();
}
} catch (Exception ignore) {
} finally {
if (reader != null) {
try {
reader.close();
} catch (Exception x) {
}
}
lastRead = System.currentTimeMillis();
}
}
}

View file

@ -0,0 +1,411 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.util;
import java.io.IOException;
import java.util.*;
import helma.framework.core.*;
import helma.framework.repository.Resource;
import helma.framework.repository.Repository;
/**
* A property dictionary that is updated from property resources
*/
public final class ResourceProperties extends Properties {
// Delay between checks
private final long cacheTime = 1500L;
// Default properties
public ResourceProperties defaultProperties;
// Defines wether keys are case-sensitive or not
private boolean ignoreCase = true;
// Cached checksum of last check
private long lastChecksum = -1;
// Time of last check
private long lastCheck = 0;
// Time porperties were last modified
private long lastModified = 0;
// Application where to fetch additional resources
private Application app;
// Name of possible resources to fetch from the applications's repositories
private String resourceName;
// Sorted map of resources
private TreeSet resources;
/**
* Constructs an empty ResourceProperties
* Resources must be added manually afterwards
*/
public ResourceProperties() {
resources = new TreeSet();
}
/**
* Constructs a ResourceProperties retrieving resources from the given
* application using the given name to fetch resources
* @param app application to fetch resources from
* @param resourceName name to use when fetching resources from the application
*/
public ResourceProperties(Application app, String resourceName) {
this.app = app;
this.resourceName = resourceName;
resources = new TreeSet(app.getResourceComparator());
}
/**
* Constructs a ResourceProperties retrieving resources from the given
* application using the given name to fetch resources and falling back
* to the given default properties
* @param app application to fetch resources from
* @param sourceName name to use when fetching resources from the application
* @param defaultProperties default properties
*/
public ResourceProperties(Application app, String sourceName, ResourceProperties defaultProperties) {
this(app, sourceName);
this.defaultProperties = defaultProperties;
forceUpdate();
}
/**
* Updates the properties regardless of an actual need
*/
private void forceUpdate() {
lastChecksum = -1;
update();
}
/**
* Sets the default properties and updates all properties
* @param defaultProperties default properties
*/
public void setDefaultProperties(ResourceProperties defaultProperties) {
this.defaultProperties = defaultProperties;
update();
}
/**
* Adds a resource to the list of resources and updates all properties if
* needed
* @param resource resource to add
*/
public void addResource(Resource resource) {
if (resource != null) {
resources.add(resource);
forceUpdate();
}
}
/**
* Removes a resource from the list of resources and updates all properties
* if needed
* @param resource resource to remove
*/
public void removeResource(Resource resource) {
if (resources.contains(resource)) {
resources.remove(resource);
forceUpdate();
}
return;
}
/**
* Checks wether the properties need to be updated
* @return true if the properties need tu be updated
*/
public boolean needsUpdate() {
lastCheck = System.currentTimeMillis();
if (getChecksum() != lastChecksum) {
return true;
} else {
return false;
}
}
/**
* Updates all properties if there is a need to update
*/
public void update() {
if (needsUpdate() || (defaultProperties != null && defaultProperties.needsUpdate())) {
clear();
// first of all, properties are load from default properties
if (defaultProperties != null) {
defaultProperties.update();
this.putAll(defaultProperties);
}
/* next we try to load properties from the application's
repositories, if we blong to any application */
if (app != null) {
Iterator iterator = app.getRepositories();
while (iterator.hasNext()) {
try {
Repository repository = (Repository) iterator.next();
Resource resource = repository.getResource(resourceName);
if (resource != null) {
load(resource.getInputStream());
}
} catch (IOException iox) {
iox.printStackTrace();
}
}
}
// at last we try to load properties from the resource list
if (resources != null) {
Iterator iterator = resources.iterator();
while (iterator.hasNext()) {
try {
load(((Resource) iterator.next()).getInputStream());
} catch (IOException ignore) {}
}
}
lastChecksum = getChecksum();
lastCheck = lastModified = System.currentTimeMillis();
}
return;
}
/**
* Checks wether the given object is in the value list
* @param value value to look for
* @return true if the value is found in the value list
*/
public boolean contains(Object value) {
if ((System.currentTimeMillis() - lastCheck) > cacheTime) {
update();
}
return super.contains(value.toString());
}
/**
* Checks wether the given object is in the key list
* @param key key to look for
* @return true if the key is found in the key list
*/
public boolean containsKey(Object key) {
if ((System.currentTimeMillis() - lastCheck) > cacheTime) {
update();
}
return super.containsKey(key.toString());
}
/**
* Returns an enumeration of all values
* @return values enumeration
*/
public Enumeration elements() {
if ((System.currentTimeMillis() - lastCheck) > cacheTime) {
update();
}
return super.elements();
}
/**
* Returns a value in this list fetched by the given key
* @param key key to use for fetching the value
* @return value belonging to the given key
*/
public Object get(Object key) {
if ((System.currentTimeMillis() - lastCheck) > cacheTime) {
update();
}
return (String) super.get(ignoreCase == true ? key.toString().toLowerCase() : key.toString());
}
/**
* Returns the date the resources were last modified
* @return last modified date
*/
public long lastModified() {
return lastModified;
}
/**
* Returns a checksum for all resources
* @return checksum
*/
public long getChecksum() {
long checksum = 0;
if (app != null) {
Iterator iterator = app.getRepositories();
while (iterator.hasNext()) {
try {
Repository repository = (Repository) iterator.next();
Resource resource = repository.getResource(resourceName);
checksum += resource != null ?
resource.lastModified() : repository.lastModified();
} catch (IOException iox) {
iox.printStackTrace();
}
}
}
if (resources != null) {
Iterator iterator = resources.iterator();
while (iterator.hasNext()) {
checksum += ((Resource) iterator.next()).lastModified();
}
}
return checksum;
}
/**
* Returns a value in the list fetched by the given key or a default value
* if no corresponding key is found
* @param key key to use for fetching the value
* @param defaultValue default value to return if key is not found
* @return spiecific value or default value if not found
*/
public String getProperty(String key, String defaultValue) {
if ((System.currentTimeMillis() - lastCheck) > cacheTime) {
update();
}
return super.getProperty(ignoreCase == true ? key.toLowerCase() : key, defaultValue);
}
/**
* Returns a value in this list fetched by the given key
* @param key key to use for fetching the value
* @return value belonging to the given key
*/
public String getProperty(String key) {
if ((System.currentTimeMillis() - lastCheck) > cacheTime) {
update();
}
return super.getProperty(ignoreCase == true ? key.toLowerCase() : key);
}
/**
* Checks wether the properties list is empty
* @return true if the properties list is empty
*/
public boolean isEmpty() {
if ((System.currentTimeMillis() - lastCheck) > cacheTime) {
update();
}
return super.isEmpty();
}
/**
* Checks wether case-sensitivity is ignored for keys
* @return true if case-sensitivity is ignored for keys
*/
public boolean isIgnoreCase() {
return ignoreCase;
}
/**
* Returns an enumeration of all keys
* @return keys enumeration
*/
public Enumeration keys() {
if ((System.currentTimeMillis() - lastCheck) > cacheTime) {
update();
}
return super.keys();
}
/**
* Returns a set of all keys
* @return keys set
*/
public Set keySet() {
if ((System.currentTimeMillis() - lastCheck) > cacheTime) {
update();
}
return super.keySet();
}
/**
* Puts a new key-value pair into the properties list
* @param key key
* @param value value
* @return the old value, if an old value got replaced
*/
public Object put(Object key, Object value) {
if (value != null) {
value = value.toString().trim();
}
return super.put(ignoreCase == true ? key.toString().toLowerCase() : key.toString(), value);
}
/**
* Removes a key-value pair from the properties list
* @param key key
* @return the old value
*/
public Object remove(Object key) {
return super.remove(ignoreCase == true ? key.toString().toLowerCase() : key.toString());
}
/**
* Changes how keys are handled
* @param ignore true if to ignore case-sensitivity for keys
*/
public void setIgnoreCase(boolean ignore) {
if (!super.isEmpty()) {
throw new RuntimeException("setIgnoreCase() can only be called on empty Properties");
}
ignoreCase = ignore;
return;
}
/**
* Returns the number of peroperties in the list
* @return number of properties
*/
public int size() {
if ((System.currentTimeMillis() - lastCheck) > cacheTime) {
update();
}
return super.size();
}
/**
* Returns a string-representation of the class
* @return string
*/
public String toString() {
return super.toString();
}
}

View file

@ -1,40 +0,0 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.util;
/**
* An interface of classes that can update themselves and know when to do so.
*/
public interface Updatable {
/**
*
*
* @return ...
*/
public boolean needsUpdate();
/**
*
*/
public void update();
/**
*
*/
public void remove();
}