Check in current Hop snapshot
This commit is contained in:
commit
09d1fdd4e3
85 changed files with 16603 additions and 0 deletions
21
src/helma/framework/ApplicationStoppedException.java
Normal file
21
src/helma/framework/ApplicationStoppedException.java
Normal file
|
@ -0,0 +1,21 @@
|
|||
// ApplicationStoppedException.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.framework;
|
||||
|
||||
import FESI.Exceptions.EcmaScriptException;
|
||||
|
||||
/**
|
||||
* This is thrown when a request is made to a stopped
|
||||
* application
|
||||
*/
|
||||
|
||||
public class ApplicationStoppedException extends RuntimeException {
|
||||
|
||||
|
||||
public ApplicationStoppedException () {
|
||||
super ("The application has been stopped");
|
||||
}
|
||||
|
||||
|
||||
}
|
17
src/helma/framework/FrameworkException.java
Normal file
17
src/helma/framework/FrameworkException.java
Normal file
|
@ -0,0 +1,17 @@
|
|||
// FrameworkException.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.framework;
|
||||
|
||||
/**
|
||||
* The basic exception class used to tell when certain things go
|
||||
* wrong in evaluation of requests.
|
||||
*/
|
||||
|
||||
public class FrameworkException extends RuntimeException {
|
||||
|
||||
public FrameworkException (String msg) {
|
||||
super (msg);
|
||||
}
|
||||
|
||||
}
|
20
src/helma/framework/IRemoteApp.java
Normal file
20
src/helma/framework/IRemoteApp.java
Normal file
|
@ -0,0 +1,20 @@
|
|||
// RemoteApp.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.framework;
|
||||
|
||||
import java.rmi.*;
|
||||
|
||||
/**
|
||||
* RMI interface for an application. Currently only execute is used and supported.
|
||||
*/
|
||||
|
||||
public interface IRemoteApp extends Remote {
|
||||
|
||||
public ResponseTrans execute (RequestTrans param) throws RemoteException;
|
||||
|
||||
public ResponseTrans get (String path, String sessionID) throws RemoteException;
|
||||
|
||||
public void ping () throws RemoteException;
|
||||
|
||||
}
|
34
src/helma/framework/RedirectException.java
Normal file
34
src/helma/framework/RedirectException.java
Normal file
|
@ -0,0 +1,34 @@
|
|||
// RedirectException.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.framework;
|
||||
|
||||
import FESI.Exceptions.EcmaScriptException;
|
||||
|
||||
/**
|
||||
* RedirectException is thrown internally when a response is redirected to a
|
||||
* new URL.
|
||||
*/
|
||||
|
||||
public class RedirectException extends EcmaScriptException {
|
||||
|
||||
String url;
|
||||
|
||||
public RedirectException (String url) {
|
||||
super ("Redirection Request to "+url);
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getMessage () {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void printStackTrace(java.io.PrintStream s) {
|
||||
|
||||
}
|
||||
|
||||
public void printStackTrace(java.io.PrintWriter w) {
|
||||
|
||||
}
|
||||
|
||||
}
|
52
src/helma/framework/RequestTrans.java
Normal file
52
src/helma/framework/RequestTrans.java
Normal file
|
@ -0,0 +1,52 @@
|
|||
// RequestTrans.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.framework;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import helma.objectmodel.*;
|
||||
|
||||
/**
|
||||
* A Transmitter for a request from the servlet client. Objects of this
|
||||
* class are directly exposed to JavaScript as global property req.
|
||||
*/
|
||||
|
||||
public class RequestTrans implements Serializable {
|
||||
|
||||
public String path;
|
||||
public String session;
|
||||
private Hashtable values;
|
||||
// this is used to hold the EcmaScript form data object
|
||||
public transient Object data;
|
||||
|
||||
public RequestTrans () {
|
||||
super ();
|
||||
values = new Hashtable ();
|
||||
}
|
||||
|
||||
public RequestTrans (Hashtable values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public void set (String name, Object value) {
|
||||
values.put (name, value);
|
||||
}
|
||||
|
||||
public Enumeration keys () {
|
||||
return values.keys ();
|
||||
}
|
||||
|
||||
public Object get (String name) {
|
||||
try {
|
||||
return values.get (name);
|
||||
} catch (Exception x) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Hashtable getReqData () {
|
||||
return values;
|
||||
}
|
||||
|
||||
}
|
207
src/helma/framework/ResponseTrans.java
Normal file
207
src/helma/framework/ResponseTrans.java
Normal file
|
@ -0,0 +1,207 @@
|
|||
// ResponseTrans.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.framework;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import helma.objectmodel.*;
|
||||
import helma.util.*;
|
||||
|
||||
/**
|
||||
* A Transmitter for a response to the servlet client. Objects of this
|
||||
* class are directly exposed to JavaScript as global property res.
|
||||
*/
|
||||
|
||||
public class ResponseTrans implements Serializable {
|
||||
|
||||
public String contentType = "text/html";
|
||||
// the page body
|
||||
private char[] body = null;
|
||||
// contains the redirect URL
|
||||
public String redirect = null;
|
||||
|
||||
// cookies
|
||||
public String cookieKeys[];
|
||||
public String cookieValues[];
|
||||
public int cookieDays[];
|
||||
int nCookies = 0;
|
||||
|
||||
// used to allow or disable client side caching
|
||||
public boolean cache = true;
|
||||
|
||||
// the buffer used to build the body
|
||||
private transient StringBuffer buffer = null;
|
||||
// these are used to implement the _as_string variants for Hop templates.
|
||||
private transient Stack buffers;
|
||||
|
||||
|
||||
public ResponseTrans () {
|
||||
super ();
|
||||
}
|
||||
|
||||
public void reset () {
|
||||
if (buffer != null)
|
||||
buffer.setLength (0);
|
||||
redirect = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is called before a template is rendered as string (xxx_as_string) to redirect the output
|
||||
* to a new string buffer.
|
||||
*/
|
||||
public void pushStringBuffer () {
|
||||
if (buffers == null)
|
||||
buffers = new Stack();
|
||||
if (buffer != null)
|
||||
buffers.push (buffer);
|
||||
buffer = new StringBuffer (128);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content of the current string buffer and switches back to the previos one.
|
||||
*/
|
||||
public String popStringBuffer () {
|
||||
StringBuffer b = buffer;
|
||||
buffer = buffers.empty() ? null : (StringBuffer) buffers.pop ();
|
||||
return b.toString ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a string to the response unchanged.
|
||||
*/
|
||||
public void write (Object what) {
|
||||
if (what != null) {
|
||||
if (buffer == null)
|
||||
buffer = new StringBuffer (512);
|
||||
buffer.append (what.toString ());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace special characters with entities, including <, > and ", thus allowing
|
||||
* no HTML tags.
|
||||
*/
|
||||
public void encode (Object what) {
|
||||
if (what != null) {
|
||||
if (buffer == null)
|
||||
buffer = new StringBuffer (512);
|
||||
HtmlEncoder.encodeAll (what.toString (), buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace special characters with entities but leave <, > and ", allowing HTML tags
|
||||
* in the response.
|
||||
*/
|
||||
public void format (Object what) {
|
||||
if (what != null) {
|
||||
if (buffer == null)
|
||||
buffer = new StringBuffer (512);
|
||||
HtmlEncoder.encode (what.toString (), buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace special characters with entities, including <, > and ", thus allowing
|
||||
* no HTML tags.
|
||||
*/
|
||||
public void encodeXml (Object what) {
|
||||
if (what != null) {
|
||||
if (buffer == null)
|
||||
buffer = new StringBuffer (512);
|
||||
HtmlEncoder.encodeXml (what.toString (), buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void append (String what) {
|
||||
if (what != null) {
|
||||
if (buffer == null)
|
||||
buffer = new StringBuffer (512);
|
||||
buffer.append (what);
|
||||
}
|
||||
}
|
||||
|
||||
public void redirect (String url) throws RedirectException {
|
||||
redirect = url;
|
||||
throw new RedirectException (url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This has to be called after writin to this response has finished and before it is shipped back to the
|
||||
* web server. Transforms the string buffer into a char array to minimize size.
|
||||
*/
|
||||
public void close () {
|
||||
if (buffer != null)
|
||||
body = new String (buffer).toCharArray();
|
||||
}
|
||||
|
||||
public String getContentString () {
|
||||
return body == null ? "" : new String (body);
|
||||
}
|
||||
|
||||
public int getContentLength () {
|
||||
if (body != null)
|
||||
return body.length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getContentType () {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public synchronized void setCookie (String key, String value) {
|
||||
setCookie (key, value, -1);
|
||||
}
|
||||
|
||||
public synchronized void setCookie (String key, String value, int days) {
|
||||
if (nCookies == 0) {
|
||||
cookieKeys = new String [3];
|
||||
cookieValues = new String [3];
|
||||
cookieDays = new int [3];
|
||||
}
|
||||
if (nCookies == cookieKeys.length) {
|
||||
String nk[] = new String [nCookies+3];
|
||||
System.arraycopy (cookieKeys, 0, nk, 0, nCookies);
|
||||
String nv[] = new String [nCookies+3];
|
||||
System.arraycopy (cookieValues, 0, nv, 0, nCookies);
|
||||
int nd[] = new int [nCookies+3];
|
||||
System.arraycopy (cookieDays, 0, nd, 0, nCookies);
|
||||
cookieKeys = nk;
|
||||
cookieValues = nv;
|
||||
cookieDays = nd;
|
||||
}
|
||||
cookieKeys [nCookies] = key;
|
||||
cookieValues [nCookies] = value;
|
||||
cookieDays [nCookies] = days;
|
||||
nCookies += 1;
|
||||
}
|
||||
|
||||
public void resetCookies () {
|
||||
nCookies = 0;
|
||||
}
|
||||
|
||||
public int countCookies () {
|
||||
return nCookies;
|
||||
}
|
||||
|
||||
public int getDaysAt (int i) {
|
||||
return cookieDays[i];
|
||||
}
|
||||
|
||||
public String getKeyAt (int i) {
|
||||
return cookieKeys[i];
|
||||
}
|
||||
|
||||
public String getValueAt (int i) {
|
||||
return cookieValues[i];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
18
src/helma/framework/TimeoutException.java
Normal file
18
src/helma/framework/TimeoutException.java
Normal file
|
@ -0,0 +1,18 @@
|
|||
// TimeoutException.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.framework;
|
||||
|
||||
/**
|
||||
* TimeoutException is thrown by the request evaluator when a request could
|
||||
* not be serviced within the timeout period specified for an application.
|
||||
*/
|
||||
|
||||
public class TimeoutException extends RuntimeException {
|
||||
public TimeoutException () {
|
||||
super ("Request timed out");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
1066
src/helma/framework/extensions/Database.java
Normal file
1066
src/helma/framework/extensions/Database.java
Normal file
File diff suppressed because it is too large
Load diff
214
src/helma/framework/extensions/ESMail.java
Normal file
214
src/helma/framework/extensions/ESMail.java
Normal file
|
@ -0,0 +1,214 @@
|
|||
// ESMail.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
|
||||
package helma.framework.extensions;
|
||||
|
||||
import javax.mail.*;
|
||||
import javax.mail.internet.*;
|
||||
import javax.activation.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import helma.framework.core.*;
|
||||
import helma.objectmodel.*;
|
||||
import FESI.Data.*;
|
||||
import FESI.Interpreter.*;
|
||||
import FESI.Exceptions.*;
|
||||
|
||||
/**
|
||||
* A JavaScript wrapper around a JavaMail message class to send
|
||||
* mail via SMTP from HOP
|
||||
*/
|
||||
|
||||
public class ESMail extends ESObject implements Serializable {
|
||||
|
||||
INode node;
|
||||
MailExtension mailx;
|
||||
Properties mprops;
|
||||
MimeMessage message;
|
||||
Multipart multipart;
|
||||
StringBuffer buffer;
|
||||
|
||||
int status;
|
||||
public static final int OK=0;
|
||||
public static final int SUBJECT=10;
|
||||
public static final int TEXT=11;
|
||||
public static final int MIMEPART=12;
|
||||
public static final int TO=20;
|
||||
public static final int CC=21;
|
||||
public static final int BCC=22;
|
||||
public static final int FROM=23;
|
||||
public static final int REPLYTO=24;
|
||||
public static final int SEND=30;
|
||||
|
||||
|
||||
public ESMail (MailExtension mailx) {
|
||||
|
||||
super (mailx.esMailPrototype, mailx.evaluator);
|
||||
this.status = OK;
|
||||
this.mailx = mailx;
|
||||
this.mprops = mailx.mprops;
|
||||
|
||||
// create some properties and get the default Session
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
props.put ("mail.smtp.host", mprops.getProperty ("smtp", "mail"));
|
||||
|
||||
Session session = Session.getDefaultInstance(props, null);
|
||||
message = new MimeMessage (session);
|
||||
} catch (Throwable t) {
|
||||
IServer.getLogger().log ("caught in mail constructor: "+t);
|
||||
}
|
||||
}
|
||||
|
||||
public void setStatus (int status) {
|
||||
// Only register the first error that occurrs
|
||||
if (this.status == 0)
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public int getStatus () {
|
||||
return status;
|
||||
}
|
||||
|
||||
public ESValue getProperty(String propertyName, int hash) throws EcmaScriptException {
|
||||
if ("status".equalsIgnoreCase (propertyName))
|
||||
return new ESNumber (status);
|
||||
return super.getProperty (propertyName, hash);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
public void setText (ESValue val) throws Exception {
|
||||
if (buffer == null)
|
||||
buffer = new StringBuffer ();
|
||||
if (val != null)
|
||||
buffer.append (val.toString ());
|
||||
}
|
||||
|
||||
public void addPart (ESValue val[]) throws Exception {
|
||||
if (val == null || val.length == 0) return;
|
||||
if (multipart == null) {
|
||||
multipart = new MimeMultipart ();
|
||||
}
|
||||
for (int i=0; i<val.length; i++) {
|
||||
INode node = getNode (val[i]);
|
||||
if (node != null) {
|
||||
BodyPart part = new MimeBodyPart ();
|
||||
IServer.getLogger().log ("Adding MimePart: "+node.getContentType ());
|
||||
NodeDataSource nds=new NodeDataSource (node);
|
||||
part.setDataHandler(new DataHandler(nds));
|
||||
// part.setFileName(filename);
|
||||
// part.setDataHandler (new javax.activation.DataHandler (node.getContent(), node.getContentType ()));
|
||||
multipart.addBodyPart (part);
|
||||
} else if (val[i] != null) {
|
||||
BodyPart part = new MimeBodyPart ();
|
||||
part.setContent (val[i].toString (), "text/plain");
|
||||
multipart.addBodyPart (part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setSubject (ESValue val) throws Exception {
|
||||
if (val == null)
|
||||
return;
|
||||
message.setSubject (MimeUtility.encodeWord (val.toString (), "iso-8859-1", null));
|
||||
}
|
||||
|
||||
public void setReplyTo (ESValue add) throws Exception {
|
||||
String addstring = add.toString ();
|
||||
if (addstring.indexOf ("@") < 0)
|
||||
throw new AddressException ();
|
||||
Address replyTo[] = new Address[1];
|
||||
replyTo[0] = new InternetAddress (addstring);
|
||||
message.setReplyTo (replyTo);
|
||||
}
|
||||
|
||||
public void setFrom (ESValue add[]) throws Exception {
|
||||
String addstring = add[0].toString ();
|
||||
if (addstring.indexOf ("@") < 0)
|
||||
throw new AddressException ();
|
||||
Address address = null;
|
||||
if (add.length > 1)
|
||||
address = new InternetAddress (addstring, MimeUtility.encodeWord (add[1].toString (), "iso-8859-1", null));
|
||||
else
|
||||
address = new InternetAddress (addstring);
|
||||
message.setFrom (address);
|
||||
}
|
||||
|
||||
public void addTo (ESValue add[]) throws Exception {
|
||||
String addstring = add[0].toString ();
|
||||
if (addstring.indexOf ("@") < 0)
|
||||
throw new AddressException ();
|
||||
Address address = null;
|
||||
if (add.length > 1)
|
||||
address = new InternetAddress (addstring, MimeUtility.encodeWord (add[1].toString (), "iso-8859-1", null));
|
||||
else
|
||||
address = new InternetAddress (addstring);
|
||||
message.addRecipient (Message.RecipientType.TO, address);
|
||||
}
|
||||
|
||||
public void addCC (ESValue add[]) throws Exception {
|
||||
String addstring = add[0].toString ();
|
||||
if (addstring.indexOf ("@") < 0)
|
||||
throw new AddressException ();
|
||||
Address address = null;
|
||||
if (add.length > 1)
|
||||
address = new InternetAddress (addstring, MimeUtility.encodeWord (add[1].toString (), "iso-8859-1", null));
|
||||
else
|
||||
address = new InternetAddress (addstring);
|
||||
message.addRecipient (Message.RecipientType.CC, address);
|
||||
}
|
||||
|
||||
public void addBCC (ESValue add[]) throws Exception {
|
||||
String addstring = add[0].toString ();
|
||||
if (addstring.indexOf ("@") < 0)
|
||||
throw new AddressException ();
|
||||
Address address = null;
|
||||
if (add.length > 1)
|
||||
address = new InternetAddress (addstring, MimeUtility.encodeWord (add[1].toString (), "iso-8859-1", null));
|
||||
else
|
||||
address = new InternetAddress (addstring);
|
||||
message.addRecipient (Message.RecipientType.BCC, address);
|
||||
}
|
||||
|
||||
public void send () throws Exception {
|
||||
if (buffer != null)
|
||||
message.setText (buffer.toString ());
|
||||
else if (multipart != null)
|
||||
message.setContent (multipart);
|
||||
else
|
||||
message.setText ("");
|
||||
Transport.send (message);
|
||||
}
|
||||
|
||||
|
||||
private final INode getNode (Object obj) {
|
||||
if (obj == null)
|
||||
return null;
|
||||
if (obj instanceof ESNode)
|
||||
return ((ESNode) obj).getNode ();
|
||||
if (obj instanceof ESWrapper) {
|
||||
Object n = ((ESWrapper) obj).getJavaObject();
|
||||
if (n instanceof INode)
|
||||
return (INode) n;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
419
src/helma/framework/extensions/FtpExtension.java
Normal file
419
src/helma/framework/extensions/FtpExtension.java
Normal file
|
@ -0,0 +1,419 @@
|
|||
// FtpExtension.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.framework.extensions;
|
||||
|
||||
import helma.objectmodel.*;
|
||||
import FESI.Parser.*;
|
||||
import FESI.AST.*;
|
||||
import FESI.Interpreter.*;
|
||||
import FESI.Exceptions.*;
|
||||
import FESI.Extensions.*;
|
||||
import FESI.Data.*;
|
||||
import java.io.*;
|
||||
|
||||
import com.oroinc.net.ftp.*;
|
||||
|
||||
|
||||
/**
|
||||
* A FTP-client object that allows to do some FTP from HOP applications.
|
||||
* FTP support is far from complete but can easily be extended if more
|
||||
* functionality is needed.
|
||||
* This uses the NetComponent classes from savarese.org (ex oroinc.com).
|
||||
*/
|
||||
|
||||
class ESFtpClient extends ESObject {
|
||||
|
||||
private FTPClient ftpclient;
|
||||
private String server;
|
||||
private Exception lastError = null;
|
||||
private File localDir = null;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new FTP Client
|
||||
*
|
||||
* @param prototype The prototype object for the FTP object
|
||||
* @param evaluator The current evaluator
|
||||
*/
|
||||
ESFtpClient(ESObject prototype, Evaluator evaluator, ESValue srvstr) {
|
||||
super(prototype, evaluator);
|
||||
this.server = srvstr.toString ();
|
||||
}
|
||||
|
||||
ESFtpClient(ESObject prototype, Evaluator evaluator) {
|
||||
super(prototype, evaluator);
|
||||
}
|
||||
|
||||
|
||||
public String getESClassName() {
|
||||
return "FtpClient";
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[FtpClient]";
|
||||
}
|
||||
|
||||
public String toDetailString() {
|
||||
return "ES:[Object: builtin " + this.getClass().getName() + ":" +
|
||||
this.toString() + "]";
|
||||
}
|
||||
|
||||
ESValue getLastError() throws EcmaScriptException {
|
||||
if (lastError == null) {
|
||||
return ESNull.theNull;
|
||||
} else {
|
||||
return ESLoader.normalizeValue(lastError, evaluator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Login to the FTP server
|
||||
*
|
||||
* @param arguments The argument list
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
ESValue login(ESValue arguments[]) throws EcmaScriptException {
|
||||
if (server == null)
|
||||
return ESBoolean.makeBoolean(false);
|
||||
try {
|
||||
ftpclient = new FTPClient ();
|
||||
ftpclient.connect (server);
|
||||
ftpclient.login (arguments[0].toString(), arguments[1].toString());
|
||||
return ESBoolean.makeBoolean (true);
|
||||
} catch (Exception x) {
|
||||
return ESBoolean.makeBoolean (false);
|
||||
} catch (NoClassDefFoundError x) {
|
||||
return ESBoolean.makeBoolean (false);
|
||||
}
|
||||
}
|
||||
|
||||
ESValue cd (ESValue arguments[]) throws EcmaScriptException {
|
||||
if (ftpclient == null)
|
||||
return ESBoolean.makeBoolean(false);
|
||||
try {
|
||||
ftpclient.changeWorkingDirectory (arguments[0].toString ());
|
||||
return ESBoolean.makeBoolean(true);
|
||||
} catch (Exception wrong) {}
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
|
||||
|
||||
ESValue mkdir (ESValue arguments[]) throws EcmaScriptException {
|
||||
if (ftpclient == null)
|
||||
return ESBoolean.makeBoolean(false);
|
||||
try {
|
||||
return ESBoolean.makeBoolean(ftpclient.makeDirectory (arguments[0].toString ()));
|
||||
} catch (Exception wrong) {}
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
|
||||
ESValue lcd (ESValue arguments[]) throws EcmaScriptException {
|
||||
try {
|
||||
localDir = new File (arguments[0].toString());
|
||||
if (!localDir.exists())
|
||||
localDir.mkdirs();
|
||||
return ESBoolean.makeBoolean(true);
|
||||
} catch (Exception wrong) {}
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
|
||||
ESValue putFile(ESValue arguments[]) throws EcmaScriptException {
|
||||
if (ftpclient == null)
|
||||
return ESBoolean.makeBoolean(false);
|
||||
try {
|
||||
String fn = arguments[0].toString();
|
||||
File f = localDir == null ? new File (fn) : new File (localDir, fn);
|
||||
InputStream fin = new BufferedInputStream (new FileInputStream (f));
|
||||
ftpclient.storeFile (arguments[1].toString (), fin);
|
||||
fin.close ();
|
||||
return ESBoolean.makeBoolean(true);
|
||||
} catch (Exception wrong) {}
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
|
||||
ESValue putString(ESValue arguments[]) throws EcmaScriptException {
|
||||
if (ftpclient == null)
|
||||
return ESBoolean.makeBoolean(false);
|
||||
try {
|
||||
byte[] bytes = null;
|
||||
// check if this already is a byte array
|
||||
if (arguments[0] instanceof ESArrayWrapper) {
|
||||
Object o = ((ESArrayWrapper) arguments[0]).toJavaObject ();
|
||||
if (o instanceof byte[])
|
||||
bytes = (byte[]) o;
|
||||
}
|
||||
if (bytes == null)
|
||||
bytes = arguments[0].toString().getBytes();
|
||||
ByteArrayInputStream bin = new ByteArrayInputStream (bytes);
|
||||
ftpclient.storeFile (arguments[1].toString (), bin);
|
||||
return ESBoolean.makeBoolean(true);
|
||||
} catch (Exception wrong) {}
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
|
||||
|
||||
ESValue getFile(ESValue arguments[]) throws EcmaScriptException {
|
||||
if (ftpclient == null )
|
||||
return ESBoolean.makeBoolean(false);
|
||||
try {
|
||||
String fn = arguments[0].toString();
|
||||
File f = localDir == null ? new File (fn) : new File (localDir, fn);
|
||||
OutputStream out = new BufferedOutputStream (new FileOutputStream(f));
|
||||
ftpclient.retrieveFile (arguments[0].toString (), out);
|
||||
out.close ();
|
||||
return ESBoolean.makeBoolean(true);
|
||||
} catch (Exception wrong) {}
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
|
||||
ESValue getString(ESValue arguments[]) throws EcmaScriptException {
|
||||
if (ftpclient == null )
|
||||
return ESNull.theNull;
|
||||
try {
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream ();
|
||||
ftpclient.retrieveFile (arguments[0].toString (), bout);
|
||||
return new ESString (bout.toString ());
|
||||
} catch (Exception wrong) {}
|
||||
return ESNull.theNull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from FTP server
|
||||
*
|
||||
* @param arguments The argument list
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
ESValue logout (ESValue arguments[]) throws EcmaScriptException {
|
||||
if (ftpclient != null) {
|
||||
try {
|
||||
ftpclient.logout ();
|
||||
} catch (IOException ignore) {}
|
||||
try {
|
||||
ftpclient.disconnect ();
|
||||
} catch (IOException ignore) {}
|
||||
}
|
||||
return ESBoolean.makeBoolean (true);
|
||||
}
|
||||
|
||||
ESValue binary (ESValue arguments[]) throws EcmaScriptException {
|
||||
if (ftpclient != null) {
|
||||
try {
|
||||
ftpclient.setFileType (FTP.BINARY_FILE_TYPE);
|
||||
return ESBoolean.makeBoolean (true);
|
||||
} catch (IOException ignore) {}
|
||||
}
|
||||
return ESBoolean.makeBoolean (false);
|
||||
}
|
||||
|
||||
ESValue ascii (ESValue arguments[]) throws EcmaScriptException {
|
||||
if (ftpclient != null) {
|
||||
try {
|
||||
ftpclient.setFileType (FTP.ASCII_FILE_TYPE);
|
||||
return ESBoolean.makeBoolean (true);
|
||||
} catch (IOException ignore) {}
|
||||
}
|
||||
return ESBoolean.makeBoolean (false);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class FtpExtension extends Extension {
|
||||
|
||||
private transient Evaluator evaluator = null;
|
||||
private ESObject esFtpPrototype = null;
|
||||
|
||||
public FtpExtension () {
|
||||
super();
|
||||
}
|
||||
|
||||
class GlobalObjectFtpClient extends BuiltinFunctionObject {
|
||||
GlobalObjectFtpClient(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
|
||||
public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
|
||||
throws EcmaScriptException {
|
||||
return doConstruct(thisObject, arguments);
|
||||
}
|
||||
|
||||
public ESObject doConstruct(ESObject thisObject, ESValue[] arguments)
|
||||
throws EcmaScriptException {
|
||||
ESFtpClient ftp = null;
|
||||
if (arguments.length != 1)
|
||||
throw new EcmaScriptException("FtpClient requires 1 argument");
|
||||
ftp = new ESFtpClient (esFtpPrototype,
|
||||
this.evaluator,
|
||||
arguments[0]);
|
||||
return ftp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class FtpClientLogin extends BuiltinFunctionObject {
|
||||
FtpClientLogin(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction(ESObject thisObject,
|
||||
ESValue[] arguments)
|
||||
throws EcmaScriptException {
|
||||
ESFtpClient ftp = (ESFtpClient) thisObject;
|
||||
return ftp.login (arguments);
|
||||
}
|
||||
}
|
||||
|
||||
class FtpClientCD extends BuiltinFunctionObject {
|
||||
FtpClientCD(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction(ESObject thisObject,
|
||||
ESValue[] arguments)
|
||||
throws EcmaScriptException {
|
||||
ESFtpClient ftp = (ESFtpClient) thisObject;
|
||||
return ftp.cd (arguments);
|
||||
}
|
||||
}
|
||||
|
||||
class FtpClientMKDIR extends BuiltinFunctionObject {
|
||||
FtpClientMKDIR(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction(ESObject thisObject,
|
||||
ESValue[] arguments)
|
||||
throws EcmaScriptException {
|
||||
ESFtpClient ftp = (ESFtpClient) thisObject;
|
||||
return ftp.mkdir (arguments);
|
||||
}
|
||||
}
|
||||
|
||||
class FtpClientLCD extends BuiltinFunctionObject {
|
||||
FtpClientLCD(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction(ESObject thisObject,
|
||||
ESValue[] arguments)
|
||||
throws EcmaScriptException {
|
||||
ESFtpClient ftp = (ESFtpClient) thisObject;
|
||||
return ftp.lcd (arguments);
|
||||
}
|
||||
}
|
||||
|
||||
class FtpClientPutFile extends BuiltinFunctionObject {
|
||||
FtpClientPutFile (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction(ESObject thisObject,
|
||||
ESValue[] arguments)
|
||||
throws EcmaScriptException {
|
||||
ESFtpClient ftp = (ESFtpClient) thisObject;
|
||||
return ftp.putFile (arguments);
|
||||
}
|
||||
}
|
||||
|
||||
class FtpClientPutString extends BuiltinFunctionObject {
|
||||
FtpClientPutString (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction(ESObject thisObject,
|
||||
ESValue[] arguments)
|
||||
throws EcmaScriptException {
|
||||
ESFtpClient ftp = (ESFtpClient) thisObject;
|
||||
return ftp.putString (arguments);
|
||||
}
|
||||
}
|
||||
|
||||
class FtpClientGetFile extends BuiltinFunctionObject {
|
||||
FtpClientGetFile (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction(ESObject thisObject,
|
||||
ESValue[] arguments)
|
||||
throws EcmaScriptException {
|
||||
ESFtpClient ftp = (ESFtpClient) thisObject;
|
||||
return ftp.getFile (arguments);
|
||||
}
|
||||
}
|
||||
|
||||
class FtpClientGetString extends BuiltinFunctionObject {
|
||||
FtpClientGetString (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction(ESObject thisObject,
|
||||
ESValue[] arguments)
|
||||
throws EcmaScriptException {
|
||||
ESFtpClient ftp = (ESFtpClient) thisObject;
|
||||
return ftp.getString (arguments);
|
||||
}
|
||||
}
|
||||
|
||||
class FtpClientLogout extends BuiltinFunctionObject {
|
||||
FtpClientLogout(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction(ESObject thisObject,
|
||||
ESValue[] arguments)
|
||||
throws EcmaScriptException {
|
||||
ESFtpClient ftp = (ESFtpClient) thisObject;
|
||||
return ftp.logout (arguments);
|
||||
}
|
||||
}
|
||||
|
||||
class FtpClientBinary extends BuiltinFunctionObject {
|
||||
FtpClientBinary(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction(ESObject thisObject,
|
||||
ESValue[] arguments)
|
||||
throws EcmaScriptException {
|
||||
ESFtpClient ftp = (ESFtpClient) thisObject;
|
||||
return ftp.binary (arguments);
|
||||
}
|
||||
}
|
||||
|
||||
class FtpClientAscii extends BuiltinFunctionObject {
|
||||
FtpClientAscii(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction(ESObject thisObject,
|
||||
ESValue[] arguments)
|
||||
throws EcmaScriptException {
|
||||
ESFtpClient ftp = (ESFtpClient) thisObject;
|
||||
return ftp.ascii (arguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void initializeExtension(Evaluator evaluator) throws EcmaScriptException {
|
||||
|
||||
this.evaluator = evaluator;
|
||||
GlobalObject go = evaluator.getGlobalObject();
|
||||
ObjectPrototype op = (ObjectPrototype) evaluator.getObjectPrototype();
|
||||
FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype();
|
||||
|
||||
esFtpPrototype = new ESFtpClient (op, evaluator);
|
||||
|
||||
ESObject globalFtpObject = new GlobalObjectFtpClient("FtpClient", evaluator, fp);
|
||||
globalFtpObject.putHiddenProperty("prototype",esFtpPrototype);
|
||||
globalFtpObject.putHiddenProperty("length",new ESNumber(1));
|
||||
|
||||
|
||||
esFtpPrototype.putHiddenProperty("login", new FtpClientLogin("login", evaluator, fp));
|
||||
esFtpPrototype.putHiddenProperty("cd", new FtpClientCD("cd", evaluator, fp));
|
||||
esFtpPrototype.putHiddenProperty("mkdir", new FtpClientMKDIR("mkdir", evaluator, fp));
|
||||
esFtpPrototype.putHiddenProperty("lcd", new FtpClientLCD("lcd", evaluator, fp));
|
||||
esFtpPrototype.putHiddenProperty("putFile", new FtpClientPutFile("putFile", evaluator, fp));
|
||||
esFtpPrototype.putHiddenProperty("putString", new FtpClientPutString("putString", evaluator, fp));
|
||||
esFtpPrototype.putHiddenProperty("getFile", new FtpClientGetFile("getFile", evaluator, fp));
|
||||
esFtpPrototype.putHiddenProperty("getString", new FtpClientGetString("getString", evaluator, fp));
|
||||
esFtpPrototype.putHiddenProperty("logout", new FtpClientLogout("logout", evaluator, fp));
|
||||
esFtpPrototype.putHiddenProperty("binary", new FtpClientBinary("binary", evaluator, fp));
|
||||
esFtpPrototype.putHiddenProperty("ascii", new FtpClientAscii("ascii", evaluator, fp));
|
||||
|
||||
go.putHiddenProperty("FtpClient", globalFtpObject);
|
||||
|
||||
}
|
||||
}
|
127
src/helma/framework/extensions/ImageExtension.java
Normal file
127
src/helma/framework/extensions/ImageExtension.java
Normal file
|
@ -0,0 +1,127 @@
|
|||
// ImageExtension.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.framework.extensions;
|
||||
|
||||
import helma.objectmodel.*;
|
||||
import helma.util.*;
|
||||
import helma.image.*;
|
||||
|
||||
import FESI.Interpreter.*;
|
||||
import FESI.Exceptions.*;
|
||||
import FESI.Extensions.*;
|
||||
import FESI.Data.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.rmi.Naming;
|
||||
|
||||
|
||||
/**
|
||||
* Extension to do Image manipulation from HOP.
|
||||
*/
|
||||
|
||||
public class ImageExtension extends Extension {
|
||||
|
||||
protected Evaluator evaluator = null;
|
||||
|
||||
static boolean remote = false;
|
||||
|
||||
|
||||
public ImageExtension () {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
class GlobalObjectImage extends BuiltinFunctionObject {
|
||||
|
||||
ImageExtension imagex;
|
||||
ImageGenerator imggen;
|
||||
|
||||
GlobalObjectImage (String name, Evaluator evaluator, FunctionPrototype fp, ImageExtension imagex) {
|
||||
super(fp, evaluator, name, 1);
|
||||
this.imagex = imagex;
|
||||
}
|
||||
|
||||
public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
return doConstruct(thisObject, arguments);
|
||||
}
|
||||
|
||||
public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
Object img = null;
|
||||
IRemoteGenerator rgen = null;
|
||||
|
||||
try {
|
||||
if (imggen == null && !remote) {
|
||||
try {
|
||||
imggen = new ImageGenerator ();
|
||||
} catch (UnsatisfiedLinkError noawt) {
|
||||
remote = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (remote)
|
||||
rgen = (IRemoteGenerator) Naming.lookup ("//localhost:3033/server");
|
||||
|
||||
if (arguments.length == 1) {
|
||||
if (arguments[0] instanceof ESArrayWrapper) {
|
||||
Object obj = ((ESArrayWrapper) arguments[0]).toJavaObject ();
|
||||
if (obj instanceof byte[]) {
|
||||
img = remote ?
|
||||
(Object) rgen.createImage ((byte[]) obj) :
|
||||
(Object) imggen.createImage ((byte[]) obj);
|
||||
}
|
||||
} else if (arguments[0] instanceof ESString) {
|
||||
String imgurl = arguments[0].toString ();
|
||||
img = remote ?
|
||||
(Object) rgen.createPaintableImage (imgurl) :
|
||||
(Object) imggen.createPaintableImage (imgurl);
|
||||
}
|
||||
} else if (arguments.length == 2) {
|
||||
if (arguments[0].isNumberValue () && arguments[1].isNumberValue ()) {
|
||||
img = remote ?
|
||||
(Object) rgen.createPaintableImage (arguments[0].toInt32(), arguments[1].toInt32()) :
|
||||
(Object) imggen.createPaintableImage (arguments[0].toInt32(), arguments[1].toInt32());
|
||||
}
|
||||
}
|
||||
} catch (Exception error) {
|
||||
System.err.println ("Error creating Image: "+error);
|
||||
}
|
||||
|
||||
if (img == null)
|
||||
throw new EcmaScriptException ("Error creating image: Bad parameters or setup problem.");
|
||||
|
||||
return new ESWrapper (img, this.evaluator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called by the evaluator after the extension is loaded.
|
||||
*/
|
||||
public void initializeExtension(Evaluator evaluator) throws EcmaScriptException {
|
||||
|
||||
this.evaluator = evaluator;
|
||||
GlobalObject go = evaluator.getGlobalObject();
|
||||
FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype();
|
||||
|
||||
ESObject image = new GlobalObjectImage ("Image", evaluator, fp, this); // the Image constructor
|
||||
|
||||
go.putHiddenProperty("Image", image); // register the constructor for a Image object.
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
255
src/helma/framework/extensions/MailExtension.java
Normal file
255
src/helma/framework/extensions/MailExtension.java
Normal file
|
@ -0,0 +1,255 @@
|
|||
// MailExtension.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.framework.extensions;
|
||||
|
||||
import helma.objectmodel.*;
|
||||
import helma.util.*;
|
||||
|
||||
import FESI.Interpreter.*;
|
||||
import FESI.Exceptions.*;
|
||||
import FESI.Extensions.*;
|
||||
import FESI.Data.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* Extension to create and send mail messages via SMTP from HOP applications
|
||||
*/
|
||||
|
||||
public class MailExtension extends Extension {
|
||||
|
||||
|
||||
protected Evaluator evaluator = null;
|
||||
protected ObjectPrototype esMailPrototype = null;
|
||||
protected Properties mprops;
|
||||
|
||||
|
||||
public MailExtension () {
|
||||
super();
|
||||
}
|
||||
|
||||
public void setProperties (Properties props) {
|
||||
this.mprops = props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the evaluator after the extension is loaded.
|
||||
*/
|
||||
public void initializeExtension(Evaluator evaluator) throws EcmaScriptException {
|
||||
|
||||
this.evaluator = evaluator;
|
||||
GlobalObject go = evaluator.getGlobalObject();
|
||||
FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype();
|
||||
|
||||
ESObject op = evaluator.getObjectPrototype();
|
||||
esMailPrototype = new ObjectPrototype(op, evaluator); // the Node prototype
|
||||
|
||||
ESObject mail = new GlobalObjectMail ("Mail", evaluator, fp, this); // the Mail constructor
|
||||
|
||||
go.putHiddenProperty("Mail", mail); // register the constructor for a Mail object.
|
||||
|
||||
|
||||
// methods for sending mail from JS...
|
||||
ESObject p = new MailSetText ("setText", evaluator, fp);
|
||||
esMailPrototype.putHiddenProperty("setText", p);
|
||||
esMailPrototype.putHiddenProperty("addText", p);
|
||||
|
||||
esMailPrototype.putHiddenProperty("addPart", new MailAddPart ("addPart", evaluator, fp));
|
||||
esMailPrototype.putHiddenProperty("setSubject", new MailSetSubject ("setSubject", evaluator, fp));
|
||||
esMailPrototype.putHiddenProperty("setReplyTo", new MailSetReplyTo ("setReplyTo", evaluator, fp));
|
||||
esMailPrototype.putHiddenProperty("setFrom", new MailSetFrom ("setFrom", evaluator, fp));
|
||||
|
||||
p = new MailAddTo ("addTo", evaluator, fp);
|
||||
esMailPrototype.putHiddenProperty("addTo", p);
|
||||
esMailPrototype.putHiddenProperty("setTo", p);
|
||||
|
||||
p = new MailAddCC ("addCC", evaluator, fp);
|
||||
esMailPrototype.putHiddenProperty("addCC", p);
|
||||
esMailPrototype.putHiddenProperty("setCC", p);
|
||||
|
||||
p = new MailAddBCC ("addBCC", evaluator, fp);
|
||||
esMailPrototype.putHiddenProperty("addBCC", p);
|
||||
esMailPrototype.putHiddenProperty("setBCC", p);
|
||||
|
||||
esMailPrototype.putHiddenProperty("send", new MailSend ("send", evaluator, fp));
|
||||
|
||||
}
|
||||
|
||||
|
||||
class GlobalObjectMail extends BuiltinFunctionObject {
|
||||
|
||||
MailExtension mailx;
|
||||
|
||||
GlobalObjectMail (String name, Evaluator evaluator, FunctionPrototype fp, MailExtension mailx) {
|
||||
super(fp, evaluator, name, 1);
|
||||
this.mailx = mailx;
|
||||
}
|
||||
|
||||
public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
return doConstruct(thisObject, arguments);
|
||||
}
|
||||
|
||||
public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
ESMail mail = null;
|
||||
|
||||
if (arguments.length == 0) {
|
||||
mail = new ESMail (mailx);
|
||||
} else {
|
||||
mail = new ESMail (mailx);
|
||||
// should/could do something with extra arguments...
|
||||
}
|
||||
return mail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MailSetText extends BuiltinFunctionObject {
|
||||
MailSetText (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super (fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
ESMail mail = (ESMail) thisObject;
|
||||
if (arguments.length == 1) try {
|
||||
mail.setText (arguments[0]);
|
||||
} catch (Exception x) {
|
||||
mail.setStatus (ESMail.TEXT);
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
return ESBoolean.makeBoolean(true);
|
||||
}
|
||||
}
|
||||
|
||||
class MailAddPart extends BuiltinFunctionObject {
|
||||
MailAddPart (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super (fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
ESMail mail = (ESMail) thisObject;
|
||||
try {
|
||||
mail.addPart (arguments);
|
||||
} catch (Exception x) {
|
||||
mail.setStatus (ESMail.MIMEPART);
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
return ESBoolean.makeBoolean(true);
|
||||
}
|
||||
}
|
||||
|
||||
class MailSetSubject extends BuiltinFunctionObject {
|
||||
MailSetSubject (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super (fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
ESMail mail = (ESMail) thisObject;
|
||||
if (arguments.length == 1) try {
|
||||
mail.setSubject (arguments[0]);
|
||||
} catch (Exception x) {
|
||||
mail.setStatus (ESMail.SUBJECT);
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
return ESBoolean.makeBoolean(true);
|
||||
}
|
||||
}
|
||||
|
||||
class MailSetReplyTo extends BuiltinFunctionObject {
|
||||
MailSetReplyTo (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super (fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
ESMail mail = (ESMail) thisObject;
|
||||
if (arguments.length == 1) try {
|
||||
mail.setReplyTo (arguments[0]);
|
||||
} catch (Exception x) {
|
||||
mail.setStatus (ESMail.REPLYTO);
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
return ESBoolean.makeBoolean(true);
|
||||
}
|
||||
}
|
||||
|
||||
class MailSetFrom extends BuiltinFunctionObject {
|
||||
MailSetFrom (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super (fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
ESMail mail = (ESMail) thisObject;
|
||||
try {
|
||||
mail.setFrom (arguments);
|
||||
} catch (Exception x) {
|
||||
mail.setStatus (ESMail.FROM);
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
return ESBoolean.makeBoolean(true);
|
||||
}
|
||||
}
|
||||
|
||||
class MailAddTo extends BuiltinFunctionObject {
|
||||
MailAddTo (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super (fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
ESMail mail = (ESMail) thisObject;
|
||||
try {
|
||||
mail.addTo (arguments);
|
||||
} catch (Exception x) {
|
||||
mail.setStatus (ESMail.TO);
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
return ESBoolean.makeBoolean(true);
|
||||
}
|
||||
}
|
||||
|
||||
class MailAddCC extends BuiltinFunctionObject {
|
||||
MailAddCC (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super (fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
ESMail mail = (ESMail) thisObject;
|
||||
try {
|
||||
mail.addCC (arguments);
|
||||
} catch (Exception x) {
|
||||
mail.setStatus (ESMail.CC);
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
return ESBoolean.makeBoolean(true);
|
||||
}
|
||||
}
|
||||
|
||||
class MailAddBCC extends BuiltinFunctionObject {
|
||||
MailAddBCC (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super (fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
ESMail mail = (ESMail) thisObject;
|
||||
try {
|
||||
mail.addBCC (arguments);
|
||||
} catch (Exception x) {
|
||||
mail.setStatus (ESMail.BCC);
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
return ESBoolean.makeBoolean(true);
|
||||
}
|
||||
}
|
||||
|
||||
class MailSend extends BuiltinFunctionObject {
|
||||
MailSend (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super (fp, evaluator, name, 1);
|
||||
}
|
||||
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
ESMail mail = (ESMail) thisObject;
|
||||
try {
|
||||
mail.send ();
|
||||
} catch (Exception x) {
|
||||
IServer.getLogger().log ("Error sending mail: "+x);
|
||||
mail.setStatus (ESMail.SEND);
|
||||
return ESBoolean.makeBoolean(false);
|
||||
}
|
||||
return ESBoolean.makeBoolean(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
47
src/helma/image/ActivatedImageWrapper.java
Normal file
47
src/helma/image/ActivatedImageWrapper.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
// ActivatedImageWrapper.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.image;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import com.activated.jimi.*;
|
||||
import com.activated.jimi.util.*;
|
||||
|
||||
/**
|
||||
* A wrapper for an image that uses the Activated version of JIMI.
|
||||
*/
|
||||
|
||||
public class ActivatedImageWrapper extends ImageWrapper {
|
||||
|
||||
public ActivatedImageWrapper (Image img, Graphics g, int width, int height,
|
||||
ImageGenerator imggen) throws ClassNotFoundException {
|
||||
super (img, g, width, height, imggen);
|
||||
Class.forName ("com.activated.jimi.Jimi");
|
||||
}
|
||||
|
||||
|
||||
public void reduceColors (int colors) {
|
||||
try {
|
||||
ColorReducer redux = new ColorReducer (colors, true);
|
||||
img = redux.getColorReducedImage (img);
|
||||
} catch (Exception x) {
|
||||
throw new RuntimeException (x.getMessage ());
|
||||
}
|
||||
}
|
||||
|
||||
public void saveAs (String filename) {
|
||||
try {
|
||||
Jimi.putImage (img, filename);
|
||||
} catch (JimiException x) {
|
||||
throw new RuntimeException (x.getMessage ());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
24
src/helma/image/IRemoteGenerator.java
Normal file
24
src/helma/image/IRemoteGenerator.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
// IRemoteGenerator.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.image;
|
||||
|
||||
import java.util.*;
|
||||
import java.rmi.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* RMI interface for accessing remote image generators.
|
||||
*/
|
||||
|
||||
public interface IRemoteGenerator extends Remote {
|
||||
|
||||
public IRemoteImage createPaintableImage (int w, int h) throws RemoteException;
|
||||
|
||||
public IRemoteImage createPaintableImage (byte src[]) throws RemoteException;
|
||||
|
||||
public IRemoteImage createPaintableImage (String urlstring) throws RemoteException;
|
||||
|
||||
public IRemoteImage createImage (byte src[]) throws RemoteException;
|
||||
|
||||
}
|
37
src/helma/image/IRemoteImage.java
Normal file
37
src/helma/image/IRemoteImage.java
Normal file
|
@ -0,0 +1,37 @@
|
|||
// ActivatedImageWrapper.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.image;
|
||||
|
||||
import java.util.*;
|
||||
import java.rmi.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* RMI interface for accessing images on remote image servers.
|
||||
*/
|
||||
|
||||
public interface IRemoteImage extends Remote {
|
||||
|
||||
public void setFont (String name, int style, int size) throws RemoteException;
|
||||
public void setColor (int color) throws RemoteException;
|
||||
public void setColor (int r, int g, int b) throws RemoteException;
|
||||
public void reduceColors (int colors) throws RemoteException;
|
||||
|
||||
public void drawString (String str, int x, int y) throws RemoteException;
|
||||
public void drawRect (int x, int y, int w, int h) throws RemoteException;
|
||||
public void drawLine (int x1, int y1, int x2, int y2) throws RemoteException;
|
||||
public void fillRect (int x, int y, int w, int h) throws RemoteException;
|
||||
|
||||
public int getWidth () throws RemoteException;
|
||||
public int getHeight () throws RemoteException;
|
||||
public void crop (int x, int y, int w, int h) throws RemoteException;
|
||||
public void resize (int w, int h) throws RemoteException;
|
||||
|
||||
public void saveAs (String filename) throws RemoteException;
|
||||
public void readFrom (String filename) throws RemoteException;
|
||||
|
||||
public byte[] getBytes (String type) throws RemoteException;
|
||||
public void setBytes (byte[] bytes, String type) throws RemoteException;
|
||||
|
||||
}
|
133
src/helma/image/ImageGenerator.java
Normal file
133
src/helma/image/ImageGenerator.java
Normal file
|
@ -0,0 +1,133 @@
|
|||
// ImageGenerator.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.image;
|
||||
|
||||
import java.awt.*;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* This creates an invisible frame in order to be able to create images
|
||||
* from Java. (Java needs a window context in order to user the Image class).
|
||||
*/
|
||||
|
||||
public class ImageGenerator extends Window {
|
||||
|
||||
public ImageGenerator () {
|
||||
super (new Frame() {
|
||||
public void setVisible (boolean b) {
|
||||
// This frame can never be shown
|
||||
}
|
||||
public synchronized void dispose() {
|
||||
try {
|
||||
getToolkit().getSystemEventQueue();
|
||||
super.dispose();
|
||||
} catch (Exception e) {
|
||||
// untrusted code not allowed to dispose
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
this.setBounds (0, 0, 0, 0);
|
||||
this.setVisible (true);
|
||||
}
|
||||
|
||||
public ImageWrapper createPaintableImage (int w, int h) {
|
||||
Image img = createImage (w, h);
|
||||
Graphics g = img.getGraphics ();
|
||||
ImageWrapper rimg = null;
|
||||
try {
|
||||
try {
|
||||
rimg = new ActivatedImageWrapper (img, g, w, h, this);
|
||||
} catch (NoClassDefFoundError notfound) {
|
||||
rimg = new SunImageWrapper (img, g, w, h, this);
|
||||
} catch (ClassNotFoundException notfound) {
|
||||
rimg = new SunImageWrapper (img, g, w, h, this);
|
||||
}
|
||||
} catch (Exception x) {}
|
||||
return rimg;
|
||||
}
|
||||
|
||||
public ImageWrapper createPaintableImage (byte src[]) {
|
||||
ImageWrapper rimg = null;
|
||||
MediaTracker tracker = new MediaTracker (this);
|
||||
try {
|
||||
Image img1 = Toolkit.getDefaultToolkit ().createImage (src);
|
||||
tracker.addImage (img1, 0);
|
||||
tracker.waitForAll ();
|
||||
int w = img1.getWidth (null);
|
||||
int h = img1.getHeight (null);
|
||||
Image img = createImage (w, h);
|
||||
Graphics g = img.getGraphics ();
|
||||
g.drawImage (img1, 0, 0, null);
|
||||
try {
|
||||
rimg = new ActivatedImageWrapper (img, g, w, h, this);
|
||||
} catch (ClassNotFoundException notfound) {
|
||||
rimg = new SunImageWrapper (img, g, w, h, this);
|
||||
} catch (NoClassDefFoundError notfound) {
|
||||
rimg = new SunImageWrapper (img, g, w, h, this);
|
||||
}
|
||||
} catch (Exception x) {}
|
||||
return rimg;
|
||||
}
|
||||
|
||||
public ImageWrapper createImage (byte src[]) {
|
||||
ImageWrapper rimg = null;
|
||||
MediaTracker tracker = new MediaTracker (this);
|
||||
try {
|
||||
Image img = Toolkit.getDefaultToolkit ().createImage (src);
|
||||
tracker.addImage (img, 0);
|
||||
tracker.waitForAll ();
|
||||
int w = img.getWidth (null);
|
||||
int h = img.getHeight (null);
|
||||
try {
|
||||
rimg = new ActivatedImageWrapper (img, null, w, h, this);
|
||||
} catch (ClassNotFoundException notfound) {
|
||||
rimg = new SunImageWrapper (img, null, w, h, this);
|
||||
} catch (NoClassDefFoundError notfound) {
|
||||
rimg = new SunImageWrapper (img, null, w, h, this);
|
||||
}
|
||||
} catch (Exception x) {}
|
||||
return rimg;
|
||||
}
|
||||
|
||||
|
||||
public ImageWrapper createPaintableImage (String urlstring) {
|
||||
ImageWrapper rimg = null;
|
||||
MediaTracker tracker = new MediaTracker (this);
|
||||
try {
|
||||
URL url = new URL (urlstring);
|
||||
Image img1 = Toolkit.getDefaultToolkit ().getImage (url);
|
||||
tracker.addImage (img1, 0);
|
||||
tracker.waitForAll ();
|
||||
int w = img1.getWidth (null);
|
||||
int h = img1.getHeight (null);
|
||||
Image img = createImage (w, h);
|
||||
Graphics g = img.getGraphics ();
|
||||
g.drawImage (img1, 0, 0, null);
|
||||
try {
|
||||
rimg = new ActivatedImageWrapper (img, g, w, h, this);
|
||||
} catch (ClassNotFoundException notfound) {
|
||||
rimg = new SunImageWrapper (img, g, w, h, this);
|
||||
} catch (NoClassDefFoundError notfound) {
|
||||
rimg = new SunImageWrapper (img, g, w, h, this);
|
||||
}
|
||||
} catch (Exception x) {
|
||||
x.printStackTrace ();
|
||||
}
|
||||
return rimg;
|
||||
}
|
||||
|
||||
public Image createImage (String filename) {
|
||||
Image img = null;
|
||||
MediaTracker tracker = new MediaTracker (this);
|
||||
try {
|
||||
img = Toolkit.getDefaultToolkit ().getImage (filename);
|
||||
tracker.addImage (img, 0);
|
||||
tracker.waitForAll ();
|
||||
} catch (Exception x) {
|
||||
x.printStackTrace ();
|
||||
}
|
||||
return img;
|
||||
}
|
||||
}
|
245
src/helma/image/ImageWrapper.java
Normal file
245
src/helma/image/ImageWrapper.java
Normal file
|
@ -0,0 +1,245 @@
|
|||
// ImageWrapper.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.image;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import java.util.*;
|
||||
import java.rmi.*;
|
||||
import java.rmi.server.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Abstract base class for Image Wrappers.
|
||||
*/
|
||||
|
||||
public abstract class ImageWrapper {
|
||||
|
||||
Image img;
|
||||
Graphics g;
|
||||
int width, height;
|
||||
int fontstyle, fontsize;
|
||||
String fontname;
|
||||
ImageGenerator imggen;
|
||||
|
||||
public ImageWrapper (Image img, Graphics g, int width, int height, ImageGenerator imggen) {
|
||||
this.img = img;
|
||||
this.g = g;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.imggen = imggen;
|
||||
if (g != null) {
|
||||
Font f = g.getFont ();
|
||||
fontname = f.getName ();
|
||||
fontstyle = f.getStyle ();
|
||||
fontsize = f.getSize ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* image manipulation methods
|
||||
*/
|
||||
|
||||
public void setFont (String name, int style, int size) {
|
||||
this.fontname = name;
|
||||
this.fontstyle = style;
|
||||
this.fontsize = size;
|
||||
g.setFont (new Font (name, style, size));
|
||||
}
|
||||
|
||||
public void setColor (int red, int green, int blue) {
|
||||
g.setColor (new Color (red, green, blue));
|
||||
}
|
||||
|
||||
public void setColor (int color) {
|
||||
g.setColor (new Color (color));
|
||||
}
|
||||
|
||||
public void drawString (String str, int x, int y) {
|
||||
g.drawString (str, x, y);
|
||||
}
|
||||
|
||||
public void drawLine (int x1, int y1, int x2, int y2) {
|
||||
g.drawLine (x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
public void drawRect (int x, int y, int w, int h) {
|
||||
g.drawRect (x, y, w, h);
|
||||
}
|
||||
|
||||
public void drawImage (String filename, int x, int y) {
|
||||
try {
|
||||
Image i = imggen.createImage (filename);
|
||||
g.drawImage (i, x, y, null);
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
|
||||
public void fillRect (int x, int y, int w, int h) {
|
||||
g.fillRect (x, y, w, h);
|
||||
}
|
||||
|
||||
public int getWidth () {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight () {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void crop (int x, int y, int w, int h) {
|
||||
ImageFilter filter = new CropImageFilter (x, y, w, h);
|
||||
img = Toolkit.getDefaultToolkit ().createImage(new FilteredImageSource(img.getSource(), filter));
|
||||
|
||||
}
|
||||
|
||||
public void resize (int w, int h) {
|
||||
ImageFilter filter = new AreaAveragingScaleFilter (w, h);
|
||||
img = Toolkit.getDefaultToolkit ().createImage(new FilteredImageSource(img.getSource(), filter));
|
||||
}
|
||||
|
||||
public abstract void reduceColors (int colors);
|
||||
|
||||
public abstract void saveAs (String filename);
|
||||
|
||||
public void readFrom (String filename) {
|
||||
throw new RuntimeException ("Image.readFrom() is currently not implemented.");
|
||||
}
|
||||
|
||||
public byte[] getBytes (String type) {
|
||||
throw new RuntimeException ("Image.getBytes() is currently not implemented.");
|
||||
}
|
||||
|
||||
public void setBytes (byte[] bytes, String type) {
|
||||
throw new RuntimeException ("Image.setBytes() is currently not implemented.");
|
||||
}
|
||||
|
||||
public void fillString (String str) {
|
||||
Filler filler = new Filler (0, 0, width, height);
|
||||
filler.layout (str);
|
||||
}
|
||||
|
||||
public void fillString (String str, int x, int y, int w, int h) {
|
||||
Filler filler = new Filler (x, y, w, h);
|
||||
filler.layout (str);
|
||||
}
|
||||
|
||||
class Filler {
|
||||
|
||||
int x, y, w, h;
|
||||
int addedSpace = 0;
|
||||
int xLeft, yLeft;
|
||||
int realHeight;
|
||||
transient Vector lines;
|
||||
|
||||
public Filler (int x, int y, int w, int h) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
}
|
||||
|
||||
public void layout (String str) {
|
||||
int size = fontsize;
|
||||
lines = new Vector ();
|
||||
while (!splitMessage (str, size) && size > 10) {
|
||||
lines.setSize (0);
|
||||
size = Math.max (2, size-1);
|
||||
}
|
||||
Font oldfont = g.getFont ();
|
||||
g.setFont (new Font (fontname, fontstyle, size));
|
||||
int l = lines.size();
|
||||
for (int i = 0; i < l; i++) {
|
||||
((Line) lines.elementAt (i)).paint (g, xLeft/2, yLeft/2 + y);
|
||||
}
|
||||
g.setFont (oldfont);
|
||||
}
|
||||
|
||||
private boolean splitMessage (String string, int size) {
|
||||
|
||||
Font font = new Font (fontname, fontstyle, size);
|
||||
FontMetrics metrics = Toolkit.getDefaultToolkit ().getFontMetrics (font);
|
||||
int longestLine = 0;
|
||||
int heightSoFar = 0;
|
||||
int heightIncrement = (int) (0.84f * metrics.getHeight ());
|
||||
StringTokenizer tk = new StringTokenizer (string);
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
int spaceWidth = metrics.stringWidth(" ");
|
||||
int currentLine = 0;
|
||||
int currentWidth = 0;
|
||||
int maxWidth = w - 2, maxHeight = h + addedSpace - 2;
|
||||
while (tk.hasMoreTokens()) {
|
||||
String nextToken = tk.nextToken();
|
||||
int nextWidth = metrics.stringWidth(nextToken);
|
||||
if ((currentWidth + nextWidth >= maxWidth && currentWidth != 0)) {
|
||||
Line line = new Line (buffer.toString(), x, heightSoFar, metrics);
|
||||
lines.addElement (line);
|
||||
if (line.textwidth > longestLine)
|
||||
longestLine = line.textwidth;
|
||||
buffer = new StringBuffer();
|
||||
|
||||
currentWidth = 0;
|
||||
heightSoFar += heightIncrement;
|
||||
}
|
||||
buffer.append (nextToken);
|
||||
buffer.append (" ");
|
||||
currentWidth += (nextWidth + spaceWidth);
|
||||
if (1.18*heightSoFar > maxHeight && fontsize > 10)
|
||||
return false;
|
||||
}
|
||||
if (! "".equals (buffer.toString().trim())) {
|
||||
Line line = new Line (buffer.toString(), x, heightSoFar, metrics);
|
||||
lines.addElement (line);
|
||||
|
||||
if (line.textwidth > longestLine)
|
||||
longestLine = line.textwidth;
|
||||
if (longestLine > maxWidth && fontsize > 10)
|
||||
return false;
|
||||
heightSoFar += heightIncrement;
|
||||
}
|
||||
xLeft = w - longestLine;
|
||||
yLeft = addedSpace + h - heightSoFar;
|
||||
realHeight = heightSoFar;
|
||||
return (1.18*heightSoFar <= maxHeight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class Line implements Serializable {
|
||||
|
||||
String text;
|
||||
int xoff, yoff;
|
||||
FontMetrics fm;
|
||||
public int textwidth, len;
|
||||
int ascent;
|
||||
|
||||
|
||||
public Line (String text, int xoff, int yoff, FontMetrics fm) {
|
||||
this.text = text.trim();
|
||||
len = text.length();
|
||||
this.xoff = xoff;
|
||||
this.yoff = yoff;
|
||||
this.fm = fm;
|
||||
textwidth = (len == 0) ? 0 : fm.stringWidth(this.text);
|
||||
ascent = (int) (0.9f * fm.getAscent());
|
||||
}
|
||||
|
||||
public void paint (Graphics g, int xadd, int yadd) {
|
||||
g.drawString (text, xoff+xadd, yoff+ascent+yadd);
|
||||
}
|
||||
|
||||
|
||||
public boolean contains (int y) {
|
||||
return (y < yoff+fm.getHeight()) ? true : false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
106
src/helma/image/RemoteImage.java
Normal file
106
src/helma/image/RemoteImage.java
Normal file
|
@ -0,0 +1,106 @@
|
|||
// RemoteImage.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.image;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import java.util.*;
|
||||
import java.rmi.*;
|
||||
import java.rmi.server.*;
|
||||
|
||||
/**
|
||||
* Implementation of an image that is accessible via RMI.
|
||||
*/
|
||||
|
||||
public class RemoteImage extends UnicastRemoteObject implements IRemoteImage {
|
||||
|
||||
ImageWrapper wrapped;
|
||||
|
||||
public RemoteImage (ImageWrapper wrapped) throws RemoteException {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
public void setFont (String name, int style, int size) {
|
||||
wrapped.setFont (name, style, size);
|
||||
}
|
||||
|
||||
public void setColor (int red, int green, int blue) {
|
||||
wrapped.setColor (red, green, blue);
|
||||
}
|
||||
|
||||
public void setColor (int color) {
|
||||
wrapped.setColor (color);
|
||||
}
|
||||
|
||||
public void drawString (String str, int x, int y) {
|
||||
wrapped.drawString (str, x, y);
|
||||
}
|
||||
|
||||
public void drawLine (int x1, int y1, int x2, int y2) {
|
||||
wrapped.drawLine (x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
public void drawRect (int x, int y, int w, int h) {
|
||||
wrapped.drawRect (x, y, w, h);
|
||||
}
|
||||
|
||||
public void drawImage (String filename, int x, int y) {
|
||||
wrapped.drawImage (filename, x, y);
|
||||
}
|
||||
|
||||
public void fillRect (int x, int y, int w, int h) {
|
||||
wrapped.fillRect (x, y, w, h);
|
||||
}
|
||||
|
||||
public int getWidth () {
|
||||
return wrapped.getWidth();
|
||||
}
|
||||
|
||||
public int getHeight () {
|
||||
return wrapped.getHeight();
|
||||
}
|
||||
|
||||
public void crop (int x, int y, int w, int h) {
|
||||
wrapped.crop (x, y, w, h);
|
||||
}
|
||||
|
||||
public void resize (int w, int h) {
|
||||
wrapped.resize (w, h);
|
||||
}
|
||||
|
||||
public void reduceColors (int colors) {
|
||||
wrapped.reduceColors (colors);
|
||||
}
|
||||
|
||||
public void saveAs (String filename) {
|
||||
wrapped.saveAs (filename);
|
||||
}
|
||||
|
||||
public void readFrom (String filename) {
|
||||
wrapped.readFrom (filename);
|
||||
}
|
||||
|
||||
public byte[] getBytes (String type) {
|
||||
return wrapped.getBytes (type);
|
||||
}
|
||||
|
||||
public void setBytes (byte[] bytes, String type) {
|
||||
wrapped.setBytes (bytes, type);
|
||||
}
|
||||
|
||||
public void fillString (String str) {
|
||||
wrapped.fillString (str);
|
||||
}
|
||||
|
||||
public void fillString (String str, int x, int y, int w, int h) {
|
||||
wrapped.fillString (str, x, y, w, h);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
90
src/helma/image/Server.java
Normal file
90
src/helma/image/Server.java
Normal file
|
@ -0,0 +1,90 @@
|
|||
// Server.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.image;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.rmi.*;
|
||||
import java.rmi.server.*;
|
||||
import java.rmi.registry.*;
|
||||
|
||||
/**
|
||||
* Implementation of RMI Image Generator. This accepts only connection from localhost.
|
||||
*/
|
||||
|
||||
public class Server extends UnicastRemoteObject implements IRemoteGenerator {
|
||||
|
||||
static int port = 3033;
|
||||
ImageGenerator imggen;
|
||||
|
||||
public static void main (String args[]) throws Exception {
|
||||
new Server ();
|
||||
}
|
||||
|
||||
|
||||
public Server () throws Exception {
|
||||
|
||||
imggen = new ImageGenerator ();
|
||||
|
||||
// the following seems not to be necessary after all ...
|
||||
// System.setSecurityManager(new RMISecurityManager());
|
||||
|
||||
System.out.println ("Starting server on port "+port);
|
||||
LocateRegistry.createRegistry (port);
|
||||
try {
|
||||
Naming.bind ("//:"+port+"/server", this);
|
||||
} catch (Exception x) {
|
||||
System.out.println ("error binding remote objects: " + x);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IRemoteImage createPaintableImage (int x, int y) throws RemoteException {
|
||||
try {
|
||||
String client = RemoteServer.getClientHost ();
|
||||
if (!InetAddress.getLocalHost ().equals (InetAddress.getByName (client)))
|
||||
throw new RemoteException ("Access Denied");
|
||||
} catch (ServerNotActiveException ignore) {
|
||||
} catch (UnknownHostException ignore) {}
|
||||
return new RemoteImage (imggen.createPaintableImage (x, y));
|
||||
}
|
||||
|
||||
public IRemoteImage createPaintableImage (byte[] bytes) throws RemoteException {
|
||||
try {
|
||||
String client = RemoteServer.getClientHost ();
|
||||
if (!InetAddress.getLocalHost ().equals (InetAddress.getByName (client)))
|
||||
throw new RemoteException ("Access Denied");
|
||||
} catch (ServerNotActiveException ignore) {
|
||||
} catch (UnknownHostException ignore) {}
|
||||
return new RemoteImage (imggen.createPaintableImage (bytes));
|
||||
}
|
||||
|
||||
public IRemoteImage createPaintableImage (String url) throws RemoteException {
|
||||
try {
|
||||
String client = RemoteServer.getClientHost ();
|
||||
if (!InetAddress.getLocalHost ().equals (InetAddress.getByName (client)))
|
||||
throw new RemoteException ("Access Denied");
|
||||
} catch (ServerNotActiveException ignore) {
|
||||
} catch (UnknownHostException ignore) {}
|
||||
return new RemoteImage (imggen.createPaintableImage (url));
|
||||
}
|
||||
|
||||
public IRemoteImage createImage (byte[] bytes) throws RemoteException {
|
||||
try {
|
||||
String client = RemoteServer.getClientHost ();
|
||||
if (!InetAddress.getLocalHost ().equals (InetAddress.getByName (client)))
|
||||
throw new RemoteException ("Access Denied");
|
||||
} catch (ServerNotActiveException ignore) {
|
||||
} catch (UnknownHostException ignore) {}
|
||||
return new RemoteImage (imggen.createImage (bytes));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
56
src/helma/image/SunImageWrapper.java
Normal file
56
src/helma/image/SunImageWrapper.java
Normal file
|
@ -0,0 +1,56 @@
|
|||
// ActivatedImageWrapper.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.image;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import com.sun.jimi.core.*;
|
||||
import com.sun.jimi.core.util.*;
|
||||
import Acme.JPM.Encoders.GifEncoder;
|
||||
import java.io.IOException;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
/**
|
||||
* A wrapper for an image that uses the Sun version of JIMI available at
|
||||
* http://java.sun.com/products/jimi.
|
||||
*/
|
||||
|
||||
public class SunImageWrapper extends ImageWrapper {
|
||||
|
||||
public SunImageWrapper (Image img, Graphics g, int width, int height,
|
||||
ImageGenerator imggen) throws ClassNotFoundException {
|
||||
super (img, g, width, height, imggen);
|
||||
Class.forName ("com.sun.jimi.core.Jimi");
|
||||
}
|
||||
|
||||
|
||||
public void reduceColors (int colors) {
|
||||
try {
|
||||
ColorReducer redux = new ColorReducer (colors, true);
|
||||
img = redux.getColorReducedImage (img);
|
||||
} catch (Exception x) {
|
||||
throw new RuntimeException (x.getMessage ());
|
||||
}
|
||||
}
|
||||
|
||||
public void saveAs (String filename) {
|
||||
try {
|
||||
if (filename.toLowerCase().endsWith (".gif")) {
|
||||
// sun's jimi package doesn't encode gifs, use Acme encoder
|
||||
FileOutputStream fout = new FileOutputStream (filename);
|
||||
GifEncoder enc = new GifEncoder (img, fout);
|
||||
enc.encode ();
|
||||
fout.close ();
|
||||
} else {
|
||||
Jimi.putImage (img, filename);
|
||||
}
|
||||
} catch (JimiException x) {
|
||||
throw new RuntimeException (x.getMessage ());
|
||||
} catch (IOException iox) {
|
||||
throw new RuntimeException (iox.getMessage ());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
161
src/helma/mime/LanguageTag.java
Normal file
161
src/helma/mime/LanguageTag.java
Normal file
|
@ -0,0 +1,161 @@
|
|||
// LanguageTag.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT, INRIA and Keio, 1999
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.mime;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* This class is used to represent parsed Language tags,
|
||||
* It creates a representation from a string based representation
|
||||
* of the Language tag, as defined in RFC 1766
|
||||
* NOTE, we don't check that languages are defined according to ISO 639
|
||||
*/
|
||||
|
||||
public class LanguageTag implements Serializable, Cloneable {
|
||||
public static int NO_MATCH = -1;
|
||||
public static int MATCH_LANGUAGE = 1;
|
||||
public static int MATCH_SPECIFIC_LANGUAGE = 2;
|
||||
// subtag is not dialect as subtype can be
|
||||
// dialect or country identification or script variation, etc...
|
||||
public static int MATCH_SUBTAG = 3;
|
||||
public static int MATCH_SPECIFIC_SUBTAG = 4;
|
||||
|
||||
/**
|
||||
* String representation of the language
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String language = null ;
|
||||
/**
|
||||
* String representation of subtag
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String subtag = null ;
|
||||
|
||||
/**
|
||||
* external form of this language tag
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String external = null ;
|
||||
|
||||
/**
|
||||
* How good the given LanguageTag matches the receiver of the method ?
|
||||
* This method returns a matching level among:
|
||||
* <dl>
|
||||
* <dt>NO_MATCH<dd>Language not matching,</dd>
|
||||
* <dt>MATCH_LANGUAGE<dd>Languages match roughly (with *),</dd>
|
||||
* <dt>MATCH_SPECIFIC_LANGUAGE<dd>Languages match exactly,</dd>
|
||||
* <dt>MATCH_SUBTAG<dd>Languages match, subtags matches roughly</dd>
|
||||
* <dt>MATCH_SPECIFIC_SUBAG<dd>Languages match, subtag matches exactly</dd>
|
||||
* </dl>
|
||||
* The matches are ranked from worst match to best match, a simple
|
||||
* Max ( match[i], matched) will give the best match.
|
||||
* @param other The other LanguageTag to match against ourself.
|
||||
*/
|
||||
|
||||
public int match (LanguageTag other) {
|
||||
int match = NO_MATCH;
|
||||
// match types:
|
||||
if ( language.equals("*") || other.language.equals("*") ) {
|
||||
match = MATCH_LANGUAGE;
|
||||
} else if ( ! language.equalsIgnoreCase(other.language) ) {
|
||||
return NO_MATCH ;
|
||||
} else {
|
||||
match = MATCH_SPECIFIC_LANGUAGE;
|
||||
}
|
||||
// match subtypes:
|
||||
if ((subtag == null) || (other.subtag == null))
|
||||
return match;
|
||||
if ( subtag.equals("*") || other.subtag.equals("*") ) {
|
||||
match = MATCH_SUBTAG ;
|
||||
} else if ( ! subtag.equalsIgnoreCase(other.subtag) ) {
|
||||
return NO_MATCH;
|
||||
} else {
|
||||
match = MATCH_SPECIFIC_SUBTAG;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* A printable representation of this LanguageTag.
|
||||
* The printed representation is guaranteed to be parseable by the
|
||||
* String constructor.
|
||||
*/
|
||||
|
||||
public String toString () {
|
||||
if ( external == null ) {
|
||||
if (subtag != null) {
|
||||
external = language + "-" + subtag;
|
||||
} else {
|
||||
external = language;
|
||||
}
|
||||
}
|
||||
return external ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the language
|
||||
* @return The language, encoded as a String.
|
||||
*/
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subtag
|
||||
* @return The subtag, encoded as a string
|
||||
*/
|
||||
|
||||
public String getSubtag() {
|
||||
return language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a Language tag from a spec
|
||||
* @parameter spec, A string representing a LangateTag
|
||||
*/
|
||||
public LanguageTag(String spec) {
|
||||
int strl = spec.length() ;
|
||||
int start = 0, look = -1 ;
|
||||
// skip leading/trailing blanks:
|
||||
while ((start < strl) && (spec.charAt (start)) <= ' ')
|
||||
start++ ;
|
||||
while ((strl > start) && (spec.charAt (strl-1) <= ' '))
|
||||
strl-- ;
|
||||
// get the type:
|
||||
StringBuffer sb = new StringBuffer () ;
|
||||
while ((start < strl) && ((look = spec.charAt(start)) != '-')
|
||||
&& ((look = spec.charAt(start)) != ';')) {
|
||||
sb.append ((char) look) ;
|
||||
start++ ;
|
||||
}
|
||||
this.language = sb.toString() ;
|
||||
if ( look == '-' ) {
|
||||
start++ ;
|
||||
sb.setLength(0) ;
|
||||
while ((start < strl)
|
||||
&& ((look = spec.charAt(start)) > ' ') && (look != ';')) {
|
||||
sb.append ((char) look) ;
|
||||
start++ ;
|
||||
}
|
||||
this.subtag = sb.toString() ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* construct directly a language tag
|
||||
* it NEEDS both language and subtype parameters
|
||||
*/
|
||||
|
||||
public LanguageTag(String language, String subtag) {
|
||||
this.language = language;
|
||||
this.subtag = subtag;
|
||||
}
|
||||
}
|
51
src/helma/mime/MimeHeaderHolder.java
Normal file
51
src/helma/mime/MimeHeaderHolder.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
// MimeHeaderHolder.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.mime;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public interface MimeHeaderHolder {
|
||||
|
||||
/**
|
||||
* A new header has been parsed.
|
||||
* @param name The name of the encountered header.
|
||||
* @param buf The byte buffer containing the value.
|
||||
* @param off Offset of the header value in the above buffer.
|
||||
* @param len Length of the value in the above header.
|
||||
* @exception MimeParserException if the parsing failed
|
||||
*/
|
||||
|
||||
public void notifyHeader(String name, byte buf[], int off, int len)
|
||||
throws MimeParserException;
|
||||
|
||||
/**
|
||||
* The parsing is now about to start, take any appropriate action.
|
||||
* This hook can return a <strong>true</strong> boolean value to enforce
|
||||
* the MIME parser into transparent mode (eg the parser will <em>not</em>
|
||||
* try to parse any headers.
|
||||
* <p>This hack is primarily defined for HTTP/0.9 support, it might
|
||||
* also be usefull for other hacks.
|
||||
* @param parser The Mime parser.
|
||||
* @return A boolean <strong>true</strong> if the MimeParser shouldn't
|
||||
* continue the parsing, <strong>false</strong> otherwise.
|
||||
* @exception MimeParserException if the parsing failed
|
||||
* @exception IOException if an IO error occurs.
|
||||
*/
|
||||
|
||||
public boolean notifyBeginParsing(MimeParser parser)
|
||||
throws MimeParserException, IOException;
|
||||
|
||||
/**
|
||||
* All the headers have been parsed, take any appropriate actions.
|
||||
* @param parser The Mime parser.
|
||||
* @exception MimeParserException if the parsing failed
|
||||
* @exception IOException if an IO error occurs.
|
||||
*/
|
||||
|
||||
public void notifyEndParsing(MimeParser parser)
|
||||
throws MimeParserException, IOException;
|
||||
|
||||
}
|
147
src/helma/mime/MimeHeaders.java
Normal file
147
src/helma/mime/MimeHeaders.java
Normal file
|
@ -0,0 +1,147 @@
|
|||
// MimeHeaders.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.mime;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The most stupid MIME header holder.
|
||||
* This class uses a hashtable mapping header names (as String), to header
|
||||
* values (as String). Header names are lowered before entering the hashtable.
|
||||
*/
|
||||
|
||||
public class MimeHeaders implements MimeHeaderHolder {
|
||||
Hashtable headers = null;
|
||||
MimeParser parser = null;
|
||||
|
||||
/**
|
||||
* A new header has been parsed.
|
||||
* @param name The name of the encountered header.
|
||||
* @param buf The byte buffer containing the value.
|
||||
* @param off Offset of the header value in the above buffer.
|
||||
* @param len Length of the value in the above header.
|
||||
* @exception MimeParserException if the parsing failed
|
||||
*/
|
||||
|
||||
public void notifyHeader(String name, byte buf[], int off, int len)
|
||||
throws MimeParserException
|
||||
{
|
||||
String lname = name.toLowerCase();
|
||||
String oldval = null;
|
||||
if ( headers == null ) {
|
||||
headers = new Hashtable(5);
|
||||
} else {
|
||||
oldval = (String) headers.get(lname);
|
||||
}
|
||||
String newval = ((oldval != null)
|
||||
? oldval + "," + new String(buf, 0, off, len)
|
||||
: new String(buf, 0, off, len));
|
||||
headers.put(lname, newval);
|
||||
}
|
||||
|
||||
/**
|
||||
* The parsing is now about to start, take any appropriate action.
|
||||
* This hook can return a <strong>true</strong> boolean value to enforce
|
||||
* the MIME parser into transparent mode (eg the parser will <em>not</em>
|
||||
* try to parse any headers.
|
||||
* <p>This hack is primarily defined for HTTP/0.9 support, it might
|
||||
* also be usefull for other hacks.
|
||||
* @param parser The Mime parser.
|
||||
* @return A boolean <strong>true</strong> if the MimeParser shouldn't
|
||||
* continue the parsing, <strong>false</strong> otherwise.
|
||||
* @exception IOException if an IO error occurs.
|
||||
*/
|
||||
|
||||
public boolean notifyBeginParsing(MimeParser parser)
|
||||
throws IOException
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* All the headers have been parsed, take any appropriate actions.
|
||||
* @param parser The Mime parser.
|
||||
* @exception IOException if an IO error occurs.
|
||||
*/
|
||||
|
||||
public void notifyEndParsing(MimeParser parser)
|
||||
throws IOException
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a header value.
|
||||
* @param name The header name.
|
||||
* @param value The header value.
|
||||
*/
|
||||
|
||||
public void setValue(String name, String value) {
|
||||
if ( headers == null )
|
||||
headers = new Hashtable(5);
|
||||
headers.put(name.toLowerCase(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreive a header value.
|
||||
* @param name The name of the header.
|
||||
* @return The value for this header, or <strong>null</strong> if
|
||||
* undefined.
|
||||
*/
|
||||
|
||||
public String getValue(String name) {
|
||||
return ((headers != null)
|
||||
? (String) headers.get(name.toLowerCase())
|
||||
: null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate the headers defined by the holder.
|
||||
* @return A enumeration of header names, or <strong>null</strong> if no
|
||||
* header is defined.
|
||||
*/
|
||||
|
||||
public Enumeration enumerateHeaders() {
|
||||
if ( headers == null )
|
||||
return null;
|
||||
return headers.keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entity stream attached to these headers, if any.
|
||||
* @return An InputStream instance, or <strong>null</strong> if no
|
||||
* entity available.
|
||||
*/
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return ((parser != null) ? parser.getInputStream() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump all headers to the given stream.
|
||||
* @param out The stream to dump to.
|
||||
*/
|
||||
|
||||
public void dump(PrintStream out) {
|
||||
Enumeration names = enumerateHeaders();
|
||||
if ( names != null ) {
|
||||
while (names.hasMoreElements()) {
|
||||
String name = (String) names.nextElement();
|
||||
out.println(name+": "+headers.get(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MimeHeaders(MimeParser parser) {
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
public MimeHeaders() {
|
||||
}
|
||||
|
||||
|
||||
}
|
25
src/helma/mime/MimeHeadersFactory.java
Normal file
25
src/helma/mime/MimeHeadersFactory.java
Normal file
|
@ -0,0 +1,25 @@
|
|||
// MimeHeadersFactory.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.mime;
|
||||
|
||||
/**
|
||||
* A Mime header factory, that will build instances of the MimeHeaders class
|
||||
* to hold MIME headers.
|
||||
*/
|
||||
|
||||
public class MimeHeadersFactory implements MimeParserFactory {
|
||||
|
||||
/**
|
||||
* Create a new header holder to hold the parser's result.
|
||||
* @param parser The parser that has something to parse.
|
||||
* @return A MimeParserHandler compliant object.
|
||||
*/
|
||||
|
||||
public MimeHeaderHolder createHeaderHolder(MimeParser parser) {
|
||||
return new MimeHeaders(parser);
|
||||
}
|
||||
|
||||
}
|
228
src/helma/mime/MimeParser.java
Normal file
228
src/helma/mime/MimeParser.java
Normal file
|
@ -0,0 +1,228 @@
|
|||
// MimeParser.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.mime;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.* ;
|
||||
|
||||
/**
|
||||
* The MimeParser class parses an input MIME stream.
|
||||
*/
|
||||
|
||||
public class MimeParser {
|
||||
protected int ch = -1 ;
|
||||
protected InputStream input = null ;
|
||||
protected byte buffer[] = new byte[128] ;
|
||||
protected int bsize = 0 ;
|
||||
|
||||
/**
|
||||
* The factory used to create new MIME header holders.
|
||||
*/
|
||||
protected MimeParserFactory factory = null ;
|
||||
|
||||
protected void expect (int car)
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
if ( car != ch ) {
|
||||
String sc = (new Character((char) car)).toString() ;
|
||||
String se = (new Character((char) ch)).toString() ;
|
||||
throw new MimeParserException ("expecting "
|
||||
+ sc + "("+car+")"
|
||||
+ " got "
|
||||
+ se + "("+ch+")\n"
|
||||
+ "context: "
|
||||
+ new String (buffer, 0, 0, bsize)
|
||||
+ "\n") ;
|
||||
}
|
||||
ch = input.read() ;
|
||||
}
|
||||
|
||||
protected void skipSpaces ()
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
while ( (ch == ' ') || (ch == '\t') )
|
||||
ch = input.read() ;
|
||||
}
|
||||
|
||||
protected final void append (int c) {
|
||||
if ( bsize+1 >= buffer.length ) {
|
||||
byte nb[] = new byte[buffer.length*2] ;
|
||||
System.arraycopy (buffer, 0, nb, 0, buffer.length) ;
|
||||
buffer = nb ;
|
||||
}
|
||||
buffer[bsize++] = (byte) c ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the header name:
|
||||
*/
|
||||
|
||||
protected String parse822HeaderName ()
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
bsize = 0 ;
|
||||
while ( (ch >= 32) && (ch != ':') ) {
|
||||
append ((char) ch) ;
|
||||
ch = input.read() ;
|
||||
}
|
||||
expect (':') ;
|
||||
if ( bsize <= 0 )
|
||||
throw new MimeParserException ("expected a header name.") ;
|
||||
return new String (buffer, 0, 0, bsize) ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the header body, still trying to be 822 compliant *and* HTTP
|
||||
* robust, which is unfortunatelly a contrdiction.
|
||||
*/
|
||||
|
||||
protected void parse822HeaderBody ()
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
bsize = 0 ;
|
||||
skipSpaces () ;
|
||||
loop:
|
||||
while ( true ) {
|
||||
switch (ch) {
|
||||
case -1:
|
||||
break loop ;
|
||||
case '\r':
|
||||
if ( (ch = input.read()) != '\n' ) {
|
||||
append ('\r') ;
|
||||
continue ;
|
||||
}
|
||||
// no break intentional
|
||||
case '\n':
|
||||
// do as if '\r' had been received. This defeats 822, but
|
||||
// makes HTTP more "robust". I wish HTTP were a binary
|
||||
// protocol.
|
||||
switch (ch = input.read()) {
|
||||
case ' ': case '\t':
|
||||
do {
|
||||
ch = input.read () ;
|
||||
} while ((ch == ' ') || (ch == '\t')) ;
|
||||
append(ch);
|
||||
break ;
|
||||
default:
|
||||
break loop ;
|
||||
}
|
||||
break ;
|
||||
default:
|
||||
append ((char) ch) ;
|
||||
break ;
|
||||
}
|
||||
ch = input.read() ;
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the given input stream for an HTTP 1.1 token.
|
||||
*/
|
||||
|
||||
protected String parseToken (boolean lower)
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
bsize = 0 ;
|
||||
while ( true ) {
|
||||
switch ( ch ) {
|
||||
// CTLs
|
||||
case -1:
|
||||
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
|
||||
case 8: case 9: case 10: case 11: case 12: case 13: case 14:
|
||||
case 15: case 16: case 17: case 18: case 19: case 20: case 21:
|
||||
case 22: case 23: case 24: case 25: case 26: case 27: case 28:
|
||||
case 29: case 30: case 31:
|
||||
// tspecials
|
||||
case '(': case ')': case '<': case '>': case '@':
|
||||
case ',': case ';': case ':': case '\\': case '\"':
|
||||
case '/': case '[': case ']': case '?': case '=':
|
||||
case '{': case '}': case ' ':
|
||||
return new String (buffer, 0, 0, bsize) ;
|
||||
default:
|
||||
append ((char) (lower
|
||||
? Character.toLowerCase((char) ch)
|
||||
: ch)) ;
|
||||
}
|
||||
ch = input.read() ;
|
||||
}
|
||||
}
|
||||
|
||||
protected void parse822Headers(MimeHeaderHolder msg)
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
while ( true ) {
|
||||
if ( ch == '\r' ) {
|
||||
if ( (ch = input.read()) == '\n' )
|
||||
return ;
|
||||
} else if ( ch == '\n' ) {
|
||||
return ;
|
||||
}
|
||||
String name = parse822HeaderName () ;
|
||||
skipSpaces() ;
|
||||
parse822HeaderBody () ;
|
||||
msg.notifyHeader(name, buffer, 0, bsize);
|
||||
}
|
||||
}
|
||||
|
||||
public MimeHeaderHolder parse()
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
MimeHeaderHolder msg = factory.createHeaderHolder(this);
|
||||
ch = input.read() ;
|
||||
cached = true ;
|
||||
if ( ! msg.notifyBeginParsing(this) ) {
|
||||
if ( ! cached )
|
||||
ch = input.read();
|
||||
parse822Headers (msg) ;
|
||||
}
|
||||
msg.notifyEndParsing(this);
|
||||
return msg;
|
||||
}
|
||||
|
||||
boolean cached = false ;
|
||||
|
||||
public int read()
|
||||
throws IOException
|
||||
{
|
||||
if ( cached )
|
||||
cached = false;
|
||||
else
|
||||
ch = input.read();
|
||||
return ch;
|
||||
}
|
||||
|
||||
public void unread(int ch) {
|
||||
if ( cached )
|
||||
throw new RuntimeException("cannot unread more then once !");
|
||||
this.ch = ch;
|
||||
cached = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message body, as an input stream.
|
||||
* @return The input stream used by the parser to get data, after
|
||||
* a call to <code>parse</code>, this input stream contains exactly
|
||||
* the body of the message.
|
||||
*/
|
||||
|
||||
public InputStream getInputStream () {
|
||||
return input ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the MIMEParser class.
|
||||
* @param in The input stream to be parsed as a MIME stream.
|
||||
* @param factory The factory used to create MIME header holders.
|
||||
*/
|
||||
|
||||
public MimeParser (InputStream input, MimeParserFactory factory) {
|
||||
this.input = input ;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
|
||||
}
|
14
src/helma/mime/MimeParserException.java
Normal file
14
src/helma/mime/MimeParserException.java
Normal file
|
@ -0,0 +1,14 @@
|
|||
// MimeParserException.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.mime;
|
||||
|
||||
public class MimeParserException extends Exception {
|
||||
|
||||
public MimeParserException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
24
src/helma/mime/MimeParserFactory.java
Normal file
24
src/helma/mime/MimeParserFactory.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
// MimeParserFactory.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.mime;
|
||||
|
||||
/**
|
||||
* This class is used by the MimeParser, to create new MIME message holders.
|
||||
* Each MIME parse instances is custmozied wit hits own factory, which it
|
||||
* will use to create MIME header holders.
|
||||
*/
|
||||
|
||||
public interface MimeParserFactory {
|
||||
|
||||
/**
|
||||
* Create a new header holder to hold the parser's result.
|
||||
* @param parser The parser that has something to parse.
|
||||
* @return A MimeParserHandler compliant object.
|
||||
*/
|
||||
|
||||
abstract public MimeHeaderHolder createHeaderHolder(MimeParser parser);
|
||||
|
||||
}
|
374
src/helma/mime/MimeType.java
Normal file
374
src/helma/mime/MimeType.java
Normal file
|
@ -0,0 +1,374 @@
|
|||
// MimeType.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.mime;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* This class is used to represent parsed MIME types.
|
||||
* It creates this representation from a string based representation of
|
||||
* the MIME type, as defined in the RFC 1345.
|
||||
*/
|
||||
|
||||
public class MimeType implements Serializable, Cloneable {
|
||||
/**
|
||||
* List of well known MIME types:
|
||||
*/
|
||||
public static MimeType TEXT_HTML = null ;
|
||||
public static MimeType APPLICATION_POSTSCRIPT = null ;
|
||||
public static MimeType TEXT_PLAIN = null ;
|
||||
public static MimeType APPLICATION_X_WWW_FORM_URLENCODED = null ;
|
||||
public static MimeType MULTIPART_FORM_DATA = null ;
|
||||
public static MimeType APPLICATION_X_JAVA_AGENT = null ;
|
||||
public static MimeType MESSAGE_HTTP = null ;
|
||||
public static MimeType TEXT_CSS = null ;
|
||||
public static MimeType TEXT = null ;
|
||||
|
||||
static {
|
||||
try {
|
||||
TEXT_HTML
|
||||
= new MimeType("text/html");
|
||||
APPLICATION_POSTSCRIPT
|
||||
= new MimeType ("application/postscript") ;
|
||||
TEXT_PLAIN
|
||||
= new MimeType("text/plain") ;
|
||||
APPLICATION_X_WWW_FORM_URLENCODED
|
||||
= new MimeType("application/x-www-form-urlencoded") ;
|
||||
MULTIPART_FORM_DATA
|
||||
= new MimeType("multipart/form-data") ;
|
||||
APPLICATION_X_JAVA_AGENT
|
||||
= new MimeType("application/x-java-agent") ;
|
||||
MESSAGE_HTTP
|
||||
= new MimeType("message/http");
|
||||
TEXT_CSS
|
||||
= new MimeType("text/css");
|
||||
TEXT
|
||||
= new MimeType("text/*");
|
||||
} catch (MimeTypeFormatException e) {
|
||||
System.out.println ("httpd.MimeType: invalid static init.") ;
|
||||
System.exit (1) ;
|
||||
}
|
||||
}
|
||||
|
||||
public final static int NO_MATCH = -1 ;
|
||||
public final static int MATCH_TYPE = 1 ;
|
||||
public final static int MATCH_SPECIFIC_TYPE = 2 ;
|
||||
public final static int MATCH_SUBTYPE = 3 ;
|
||||
public final static int MATCH_SPECIFIC_SUBTYPE = 4 ;
|
||||
|
||||
/**
|
||||
* String representation of type
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String type = null ;
|
||||
/**
|
||||
* String representation of subtype
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String subtype = null ;
|
||||
/**
|
||||
* parameter names
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String pnames[] = null;
|
||||
/**
|
||||
* parameter values
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String pvalues[] = null;
|
||||
/**
|
||||
* external form of this mime type
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String external = null ;
|
||||
|
||||
/**
|
||||
* How good the given MimeType matches the receiver of the method ?
|
||||
* This method returns a matching level among:
|
||||
* <dl>
|
||||
* <dt>NO_MATCH<dd>Types not matching,</dd>
|
||||
* <dt>MATCH_TYPE<dd>Types match,</dd>
|
||||
* <dt>MATCH_SPECIFIC_TYPE<dd>Types match exactly,</dd>
|
||||
* <dt>MATCH_SUBTYPE<dd>Types match, subtypes matches too</dd>
|
||||
* <dt>MATCH_SPECIFIC_SUBTYPE<dd>Types match, subtypes matches exactly</dd>
|
||||
* </dl>
|
||||
* The matches are ranked from worst match to best match, a simple
|
||||
* Max ( match[i], matched) will give the best match.
|
||||
* @param other The other MimeType to match against ourself.
|
||||
*/
|
||||
|
||||
public int match (MimeType other) {
|
||||
int match = NO_MATCH;
|
||||
// match types:
|
||||
if ( type.equals("*") || other.type.equals("*") ) {
|
||||
return MATCH_TYPE;
|
||||
} else if ( ! type.equals (other.type) ) {
|
||||
return NO_MATCH ;
|
||||
} else {
|
||||
match = MATCH_SPECIFIC_TYPE;
|
||||
}
|
||||
// match subtypes:
|
||||
if ( subtype.equals("*") || other.subtype.equals("*") ) {
|
||||
match = MATCH_SUBTYPE ;
|
||||
} else if ( ! subtype.equals (other.subtype) ) {
|
||||
return NO_MATCH;
|
||||
} else {
|
||||
match = MATCH_SPECIFIC_SUBTYPE;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* A printable representation of this MimeType.
|
||||
* The printed representation is guaranteed to be parseable by the
|
||||
* String constructor.
|
||||
*/
|
||||
|
||||
public String toString () {
|
||||
if ( external == null ) {
|
||||
StringBuffer sb = new StringBuffer (type) ;
|
||||
sb.append((char) '/') ;
|
||||
sb.append (subtype) ;
|
||||
if ( pnames != null ) {
|
||||
for (int i = 0 ; i < pnames.length; i++) {
|
||||
sb.append(';');
|
||||
sb.append(pnames[i]);
|
||||
if ( pvalues[i] != null ) {
|
||||
sb.append('=');
|
||||
sb.append(pvalues[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
external = sb.toString() ;
|
||||
}
|
||||
return external ;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does this MIME type has some value for the given parameter ?
|
||||
* @param name The parameter to check.
|
||||
* @return <strong>True</strong> if this parameter has a value, false
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean hasParameter (String name) {
|
||||
if ( pnames != null ) {
|
||||
for (int i = 0 ; i < pnames.length ; i++) {
|
||||
if ( pnames[i].equals(name) )
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
return false ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the major type of this mime type.
|
||||
* @return The major type, encoded as a String.
|
||||
*/
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minor type (subtype) of this mime type.
|
||||
* @return The minor or subtype encoded as a String.
|
||||
*/
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a mime type parameter value.
|
||||
* @param name The parameter whose value is to be returned.
|
||||
* @return The parameter value, or <b>null</b> if not found.
|
||||
*/
|
||||
public String getParameterValue (String name) {
|
||||
if ( pnames != null ) {
|
||||
for (int i = 0 ; i < pnames.length ; i++) {
|
||||
if ( pnames[i].equals(name) )
|
||||
return pvalues[i];
|
||||
}
|
||||
}
|
||||
return null ;
|
||||
}
|
||||
|
||||
/**
|
||||
* adds some parameters to a MimeType
|
||||
* @param param a String array of parameter names
|
||||
* @param values the corresponding String array of values
|
||||
*/
|
||||
public void addParameters(String[] param, String[] values) {
|
||||
// sanity check
|
||||
if ((param == null) || (values == null) ||
|
||||
(values.length != param.length))
|
||||
return;
|
||||
if (pnames == null) {
|
||||
pnames = param;
|
||||
pvalues = values;
|
||||
} else {
|
||||
String[] nparam = new String[pnames.length+param.length];
|
||||
String[] nvalues = new String[pvalues.length+values.length];
|
||||
System.arraycopy(pnames, 0, nparam, 0, pnames.length);
|
||||
System.arraycopy(param, 0, nparam, pnames.length, param.length);
|
||||
System.arraycopy(pvalues, 0, nvalues, 0, pvalues.length);
|
||||
System.arraycopy(values,0, nvalues, pvalues.length, values.length);
|
||||
pnames = nparam;
|
||||
pvalues = nvalues;
|
||||
}
|
||||
external = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a clone of this object
|
||||
* @return another cloned instance of MimeType
|
||||
*/
|
||||
public MimeType getClone() {
|
||||
try {
|
||||
return (MimeType) clone();
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
// should never happen as we are Cloneable!
|
||||
}
|
||||
// never reached
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a parameterto a MimeType
|
||||
* @param param the parameter name, as a String
|
||||
* @param value the parameter value, as a Sting
|
||||
*/
|
||||
public void addParameter(String param, String value) {
|
||||
String[] p = new String[1];
|
||||
String[] v = new String[1];
|
||||
p[0] = param;
|
||||
v[0] = value;
|
||||
addParameters(p, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct MimeType object for the given string.
|
||||
* The string should be the representation of the type. This methods
|
||||
* tries to be compliant with HTTP1.1, p 15, although it is not
|
||||
* (because of quoted-text not being accepted).
|
||||
* FIXME
|
||||
* @parameter spec A string representing a MimeType
|
||||
* @return A MimeType object
|
||||
* @exception MimeTypeFormatException if the string couldn't be parsed.
|
||||
*/
|
||||
public MimeType (String spec)
|
||||
throws MimeTypeFormatException
|
||||
{
|
||||
int strl = spec.length() ;
|
||||
int start = 0, look = -1 ;
|
||||
// skip leading/trailing blanks:
|
||||
while ((start < strl) && (spec.charAt (start)) <= ' ')
|
||||
start++ ;
|
||||
while ((strl > start) && (spec.charAt (strl-1) <= ' '))
|
||||
strl-- ;
|
||||
// get the type:
|
||||
StringBuffer sb = new StringBuffer () ;
|
||||
while ((start < strl) && ((look = spec.charAt(start)) != '/')) {
|
||||
sb.append ((char) look) ;
|
||||
start++ ;
|
||||
}
|
||||
if ( look != '/' )
|
||||
throw new MimeTypeFormatException (spec) ;
|
||||
this.type = sb.toString() ;
|
||||
// get the subtype:
|
||||
start++ ;
|
||||
sb.setLength(0) ;
|
||||
while ((start < strl)
|
||||
&& ((look = spec.charAt(start)) > ' ') && (look != ';')) {
|
||||
sb.append ((char) look) ;
|
||||
start++ ;
|
||||
}
|
||||
this.subtype = sb.toString() ;
|
||||
// get parameters, if any:
|
||||
while ((start < strl) && ((look = spec.charAt(start)) <= ' '))
|
||||
start++ ;
|
||||
if ( start < strl ) {
|
||||
if (spec.charAt(start) != ';')
|
||||
throw new MimeTypeFormatException (spec) ;
|
||||
start++ ;
|
||||
Vector vp = new Vector(4) ;
|
||||
Vector vv = new Vector(4) ;
|
||||
while ( start < strl ) {
|
||||
while ((start < strl) && (spec.charAt(start) <= ' ')) start++ ;
|
||||
// get parameter name:
|
||||
sb.setLength (0) ;
|
||||
while ((start < strl)
|
||||
&& ((look=spec.charAt(start)) > ' ') && (look != '=')) {
|
||||
sb.append (Character.toLowerCase((char) look)) ;
|
||||
start++ ;
|
||||
}
|
||||
String name = sb.toString() ;
|
||||
// get the value:
|
||||
while ((start < strl) && (spec.charAt(start) <= ' ')) start++ ;
|
||||
if (spec.charAt(start) != '=')
|
||||
throw new MimeTypeFormatException (spec) ;
|
||||
start++ ;
|
||||
while ((start < strl) &&
|
||||
((spec.charAt(start) == '"') ||
|
||||
(spec.charAt(start) <= ' '))) start++ ;
|
||||
sb.setLength(0) ;
|
||||
while ((start < strl)
|
||||
&& ((look=spec.charAt(start)) > ' ')
|
||||
&& (look != ';')
|
||||
&& (look != '"')) {
|
||||
sb.append ((char) look) ;
|
||||
start++ ;
|
||||
}
|
||||
while ((start < strl) && (spec.charAt(start) != ';')) start++ ;
|
||||
start++ ;
|
||||
String value = sb.toString() ;
|
||||
vp.addElement(name);
|
||||
vv.addElement(value);
|
||||
}
|
||||
this.pnames = new String[vp.size()];
|
||||
vp.copyInto(pnames);
|
||||
this.pvalues = new String[vv.size()];
|
||||
vv.copyInto(pvalues);
|
||||
}
|
||||
}
|
||||
|
||||
public MimeType (String type, String subtype
|
||||
, String pnames[], String pvalues[]) {
|
||||
this.type = type ;
|
||||
this.subtype = subtype ;
|
||||
this.pnames = pnames;
|
||||
this.pvalues = pvalues;
|
||||
}
|
||||
|
||||
public MimeType (String type, String subtype) {
|
||||
this.type = type;
|
||||
this.subtype = subtype;
|
||||
}
|
||||
|
||||
public static void main (String args[]) {
|
||||
if ( args.length == 1) {
|
||||
MimeType type = null ;
|
||||
try {
|
||||
type = new MimeType (args[0]) ;
|
||||
} catch (MimeTypeFormatException e) {
|
||||
}
|
||||
if ( type != null )
|
||||
System.out.println (type) ;
|
||||
else
|
||||
System.out.println ("Invalid mime type specification.") ;
|
||||
} else {
|
||||
System.out.println ("Usage: java MimeType <type-to-parse>") ;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
14
src/helma/mime/MimeTypeFormatException.java
Normal file
14
src/helma/mime/MimeTypeFormatException.java
Normal file
|
@ -0,0 +1,14 @@
|
|||
// MimeType.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.mime;
|
||||
|
||||
public class MimeTypeFormatException extends Exception {
|
||||
|
||||
public MimeTypeFormatException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
214
src/helma/mime/MultipartInputStream.java
Normal file
214
src/helma/mime/MultipartInputStream.java
Normal file
|
@ -0,0 +1,214 @@
|
|||
// MultipartInputStream.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.mime;
|
||||
|
||||
import java.io.* ;
|
||||
|
||||
/**
|
||||
* A class to handle multipart MIME input streams. See RC 1521.
|
||||
* This class handles multipart input streams, as defined by the RFC 1521.
|
||||
* It prvides a sequential interface to all MIME parts, and for each part
|
||||
* it delivers a suitable InputStream for getting its body.
|
||||
*/
|
||||
|
||||
public class MultipartInputStream extends InputStream {
|
||||
InputStream in = null;
|
||||
byte boundary[] = null ;
|
||||
byte buffer[] = null ;
|
||||
boolean partEnd = false ;
|
||||
boolean fileEnd = false ;
|
||||
|
||||
// Read boundary bytes of input in buffer
|
||||
// Return true if enough bytes available, false otherwise.
|
||||
|
||||
private final boolean readBoundaryBytes()
|
||||
throws IOException
|
||||
{
|
||||
int pos = 0;
|
||||
while ( pos < buffer.length ) {
|
||||
int got = in.read(buffer, pos, buffer.length-pos);
|
||||
if ( got < 0 )
|
||||
return false;
|
||||
pos += got;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Skip to next input boundary, set stream at begining of content:
|
||||
// Returns true if boundary was found, false otherwise.
|
||||
|
||||
protected boolean skipToBoundary()
|
||||
throws IOException
|
||||
{
|
||||
int ch = in.read() ;
|
||||
skip:
|
||||
while ( ch != -1 ) {
|
||||
if ( ch != '-' ) {
|
||||
ch = in.read() ;
|
||||
continue ;
|
||||
}
|
||||
if ((ch = in.read()) != '-')
|
||||
continue ;
|
||||
in.mark(boundary.length) ;
|
||||
if ( ! readBoundaryBytes() ) {
|
||||
in.reset();
|
||||
ch = in.read();
|
||||
continue skip;
|
||||
}
|
||||
for (int i = 0 ; i < boundary.length ; i++) {
|
||||
if ( buffer[i] != boundary[i] ) {
|
||||
in.reset() ;
|
||||
ch = in.read() ;
|
||||
continue skip ;
|
||||
}
|
||||
}
|
||||
// FIXME: should we check for a properly syntaxed part, which
|
||||
// means that we should expect '\r\n'. For now, we just skip
|
||||
// as much as we can.
|
||||
if ( (ch = in.read()) == '\r' ) {
|
||||
ch = in.read() ;
|
||||
}
|
||||
in.mark(3);
|
||||
if( in.read() == '-' ) { // check fileEnd!
|
||||
if( in.read() == '\r' && in.read() == '\n' ) {
|
||||
fileEnd = true ;
|
||||
return false ;
|
||||
}
|
||||
}
|
||||
in.reset();
|
||||
return true ;
|
||||
}
|
||||
fileEnd = true ;
|
||||
return false ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one byte of data from the current part.
|
||||
* @return A byte of data, or <strong>-1</strong> if end of file.
|
||||
* @exception IOException If some IO error occured.
|
||||
*/
|
||||
|
||||
public int read()
|
||||
throws IOException
|
||||
{
|
||||
int ch ;
|
||||
if ( partEnd )
|
||||
return -1 ;
|
||||
switch (ch = in.read()) {
|
||||
case '\r':
|
||||
// check for a boundary
|
||||
in.mark(boundary.length+3) ;
|
||||
int c1 = in.read() ;
|
||||
int c2 = in.read() ;
|
||||
int c3 = in.read() ;
|
||||
if ((c1 == '\n') && (c2 == '-') && (c3 == '-')) {
|
||||
if ( ! readBoundaryBytes() ) {
|
||||
in.reset();
|
||||
return ch;
|
||||
}
|
||||
for (int i = 0 ; i < boundary.length ; i++) {
|
||||
if ( buffer[i] != boundary[i] ) {
|
||||
in.reset() ;
|
||||
return ch ;
|
||||
}
|
||||
}
|
||||
partEnd = true ;
|
||||
if ( (ch = in.read()) == '\r' ) {
|
||||
in.read() ;
|
||||
} else if (ch == '-') {
|
||||
// FIXME, check the second hyphen
|
||||
if (in.read() == '-')
|
||||
fileEnd = true ;
|
||||
} else {
|
||||
fileEnd = (ch == -1);
|
||||
}
|
||||
return -1 ;
|
||||
} else {
|
||||
in.reset () ;
|
||||
return ch ;
|
||||
}
|
||||
// not reached
|
||||
case -1:
|
||||
fileEnd = true ;
|
||||
return -1 ;
|
||||
default:
|
||||
return ch ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read n bytes of data from the current part.
|
||||
* @return the number of bytes data, read or <strong>-1</strong>
|
||||
* if end of file.
|
||||
* @exception IOException If some IO error occured.
|
||||
*/
|
||||
public int read (byte b[], int off, int len)
|
||||
throws IOException
|
||||
{
|
||||
int got = 0 ;
|
||||
int ch ;
|
||||
|
||||
while ( got < len ) {
|
||||
if ((ch = read()) == -1)
|
||||
return (got == 0) ? -1 : got ;
|
||||
b[off+(got++)] = (byte) (ch & 0xFF) ;
|
||||
}
|
||||
return got ;
|
||||
}
|
||||
|
||||
public long skip (long n)
|
||||
throws IOException
|
||||
{
|
||||
while ((--n >= 0) && (read() != -1))
|
||||
;
|
||||
return n ;
|
||||
}
|
||||
|
||||
public int available ()
|
||||
throws IOException
|
||||
{
|
||||
return in.available();
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to the next available part of data.
|
||||
* One can interrupt the current part, and use this method to switch
|
||||
* to next part before current part was totally read.
|
||||
* @return A boolean <strong>true</strong> if there next partis ready,
|
||||
* or <strong>false</strong> if this was the last part.
|
||||
*/
|
||||
|
||||
public boolean nextInputStream()
|
||||
throws IOException
|
||||
{
|
||||
if ( fileEnd ) {
|
||||
return false ;
|
||||
}
|
||||
if ( ! partEnd ) {
|
||||
return skipToBoundary() ;
|
||||
} else {
|
||||
partEnd = false ;
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new multipart input stream.
|
||||
* @param in The initial (multipart) input stream.
|
||||
* @param boundary The input stream MIME boundary.
|
||||
*/
|
||||
|
||||
public MultipartInputStream (InputStream in, byte boundary[]) {
|
||||
this.in = (in.markSupported()
|
||||
? in
|
||||
: new BufferedInputStream(in, boundary.length+4));
|
||||
this.boundary = boundary ;
|
||||
this.buffer = new byte[boundary.length] ;
|
||||
this.partEnd = false ;
|
||||
this.fileEnd = false ;
|
||||
}
|
||||
|
||||
}
|
143
src/helma/mime/Utils.java
Normal file
143
src/helma/mime/Utils.java
Normal file
|
@ -0,0 +1,143 @@
|
|||
// Utils.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1998.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.mime;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* @version $Revision$
|
||||
* @author Benoît Mahé (bmahe@w3.org)
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
private static Hashtable extension_map = new Hashtable();
|
||||
|
||||
private static void setSuffix(String ext, String ct) {
|
||||
extension_map.put(ext, ct);
|
||||
}
|
||||
|
||||
static {
|
||||
setSuffix("", "content/unknown");
|
||||
setSuffix(".uu", "application/octet-stream");
|
||||
setSuffix(".saveme", "application/octet-stream");
|
||||
setSuffix(".dump", "application/octet-stream");
|
||||
setSuffix(".hqx", "application/octet-stream");
|
||||
setSuffix(".arc", "application/octet-stream");
|
||||
setSuffix(".o", "application/octet-stream");
|
||||
setSuffix(".a", "application/octet-stream");
|
||||
setSuffix(".bin", "application/octet-stream");
|
||||
setSuffix(".exe", "application/octet-stream");
|
||||
setSuffix(".z", "application/octet-stream");
|
||||
setSuffix(".gz", "application/octet-stream");
|
||||
setSuffix(".oda", "application/oda");
|
||||
setSuffix(".pdf", "application/pdf");
|
||||
setSuffix(".eps", "application/postscript");
|
||||
setSuffix(".ai", "application/postscript");
|
||||
setSuffix(".ps", "application/postscript");
|
||||
setSuffix(".rtf", "application/rtf");
|
||||
setSuffix(".dvi", "application/x-dvi");
|
||||
setSuffix(".hdf", "application/x-hdf");
|
||||
setSuffix(".latex", "application/x-latex");
|
||||
setSuffix(".cdf", "application/x-netcdf");
|
||||
setSuffix(".nc", "application/x-netcdf");
|
||||
setSuffix(".tex", "application/x-tex");
|
||||
setSuffix(".texinfo", "application/x-texinfo");
|
||||
setSuffix(".texi", "application/x-texinfo");
|
||||
setSuffix(".t", "application/x-troff");
|
||||
setSuffix(".tr", "application/x-troff");
|
||||
setSuffix(".roff", "application/x-troff");
|
||||
setSuffix(".man", "application/x-troff-man");
|
||||
setSuffix(".me", "application/x-troff-me");
|
||||
setSuffix(".ms", "application/x-troff-ms");
|
||||
setSuffix(".src", "application/x-wais-source");
|
||||
setSuffix(".wsrc", "application/x-wais-source");
|
||||
setSuffix(".zip", "application/zip");
|
||||
setSuffix(".bcpio", "application/x-bcpio");
|
||||
setSuffix(".cpio", "application/x-cpio");
|
||||
setSuffix(".gtar", "application/x-gtar");
|
||||
setSuffix(".shar", "application/x-shar");
|
||||
setSuffix(".sh", "application/x-shar");
|
||||
setSuffix(".sv4cpio", "application/x-sv4cpio");
|
||||
setSuffix(".sv4crc", "application/x-sv4crc");
|
||||
setSuffix(".tar", "application/x-tar");
|
||||
setSuffix(".ustar", "application/x-ustar");
|
||||
setSuffix(".snd", "audio/basic");
|
||||
setSuffix(".au", "audio/basic");
|
||||
setSuffix(".aifc", "audio/x-aiff");
|
||||
setSuffix(".aif", "audio/x-aiff");
|
||||
setSuffix(".aiff", "audio/x-aiff");
|
||||
setSuffix(".wav", "audio/x-wav");
|
||||
setSuffix(".gif", "image/gif");
|
||||
setSuffix(".ief", "image/ief");
|
||||
setSuffix(".jfif", "image/jpeg");
|
||||
setSuffix(".jfif-tbnl", "image/jpeg");
|
||||
setSuffix(".jpe", "image/jpeg");
|
||||
setSuffix(".jpg", "image/jpeg");
|
||||
setSuffix(".jpeg", "image/jpeg");
|
||||
setSuffix(".tif", "image/tiff");
|
||||
setSuffix(".tiff", "image/tiff");
|
||||
setSuffix(".ras", "image/x-cmu-rast");
|
||||
setSuffix(".pnm", "image/x-portable-anymap");
|
||||
setSuffix(".pbm", "image/x-portable-bitmap");
|
||||
setSuffix(".pgm", "image/x-portable-graymap");
|
||||
setSuffix(".ppm", "image/x-portable-pixmap");
|
||||
setSuffix(".rgb", "image/x-rgb");
|
||||
setSuffix(".xbm", "image/x-xbitmap");
|
||||
setSuffix(".xpm", "image/x-xpixmap");
|
||||
setSuffix(".xwd", "image/x-xwindowdump");
|
||||
setSuffix(".htm", "text/html");
|
||||
setSuffix(".html", "text/html");
|
||||
setSuffix(".text", "text/plain");
|
||||
setSuffix(".c", "text/plain");
|
||||
setSuffix(".cc", "text/plain");
|
||||
setSuffix(".c++", "text/plain");
|
||||
setSuffix(".h", "text/plain");
|
||||
setSuffix(".pl", "text/plain");
|
||||
setSuffix(".txt", "text/plain");
|
||||
setSuffix(".java", "text/plain");
|
||||
setSuffix(".rtx", "application/rtf");
|
||||
setSuffix(".tsv", "texyt/tab-separated-values");
|
||||
setSuffix(".etx", "text/x-setext");
|
||||
setSuffix(".mpg", "video/mpeg");
|
||||
setSuffix(".mpe", "video/mpeg");
|
||||
setSuffix(".mpeg", "video/mpeg");
|
||||
setSuffix(".mov", "video/quicktime");
|
||||
setSuffix(".qt", "video/quicktime");
|
||||
setSuffix(".avi", "application/x-troff-msvideo");
|
||||
setSuffix(".movie", "video/x-sgi-movie");
|
||||
setSuffix(".mv", "video/x-sgi-movie");
|
||||
setSuffix(".mime", "message/rfc822");
|
||||
}
|
||||
|
||||
/**
|
||||
* A useful utility routine that tries to guess the content-type
|
||||
* of an object based upon its extension.
|
||||
*/
|
||||
public static String guessContentTypeFromName(String fname) {
|
||||
String ext = "";
|
||||
int i = fname.lastIndexOf('#');
|
||||
|
||||
if (i != -1)
|
||||
fname = fname.substring(0, i - 1);
|
||||
i = fname.lastIndexOf('.');
|
||||
i = Math.max(i, fname.lastIndexOf('/'));
|
||||
i = Math.max(i, fname.lastIndexOf('?'));
|
||||
|
||||
if (i != -1 && fname.charAt(i) == '.') {
|
||||
ext = fname.substring(i).toLowerCase();
|
||||
}
|
||||
return (String) extension_map.get(ext);
|
||||
}
|
||||
|
||||
public static MimeType getMimeType(String filename) {
|
||||
try {
|
||||
return new MimeType(guessContentTypeFromName(filename));
|
||||
} catch (MimeTypeFormatException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
51
src/helma/objectmodel/ConcurrencyException.java
Normal file
51
src/helma/objectmodel/ConcurrencyException.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
// ConcurrencyException.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
|
||||
/**
|
||||
* Thrown when more than one thrad tries to modify a Node. The evaluator
|
||||
* will normally catch this and try again after a period of time.
|
||||
*/
|
||||
|
||||
public class ConcurrencyException extends RuntimeException {
|
||||
|
||||
public ConcurrencyException (String msg) {
|
||||
super (msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
457
src/helma/objectmodel/DbMapping.java
Normal file
457
src/helma/objectmodel/DbMapping.java
Normal file
|
@ -0,0 +1,457 @@
|
|||
// DbMapping.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
import helma.framework.core.Application;
|
||||
import helma.objectmodel.db.WrappedNodeManager;
|
||||
import java.util.*;
|
||||
import java.sql.*;
|
||||
import com.workingdogs.village.*;
|
||||
|
||||
/**
|
||||
* A DbMapping describes how a certain type of Nodes is to mapped to a
|
||||
* 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.
|
||||
*/
|
||||
|
||||
public class DbMapping {
|
||||
|
||||
Application app;
|
||||
String typename;
|
||||
|
||||
SystemProperties props;
|
||||
|
||||
DbSource source;
|
||||
String table;
|
||||
|
||||
DbMapping parent;
|
||||
DbMapping subnodes;
|
||||
DbMapping properties;
|
||||
private Relation parentRel;
|
||||
private Relation subnodesRel;
|
||||
private Relation propertiesRel;
|
||||
|
||||
// Map of property names to Relations objects
|
||||
public Hashtable prop2db;
|
||||
// Map of db columns to Relations objects
|
||||
public Hashtable db2prop;
|
||||
|
||||
String idField;
|
||||
String nameField;
|
||||
private String idgen;
|
||||
|
||||
Schema schema = null;
|
||||
KeyDef keydef = null;
|
||||
|
||||
private long lastTypeChange;
|
||||
public long lastDataChange;
|
||||
|
||||
public DbMapping () {
|
||||
|
||||
prop2db = new Hashtable ();
|
||||
db2prop = new Hashtable ();
|
||||
|
||||
parent = null;
|
||||
subnodes = null;
|
||||
properties = null;
|
||||
idField = "id";
|
||||
}
|
||||
|
||||
public DbMapping (Application app, String typename, SystemProperties props) {
|
||||
|
||||
this.app = app;
|
||||
this.typename = typename;
|
||||
|
||||
prop2db = new Hashtable ();
|
||||
db2prop = new Hashtable ();
|
||||
|
||||
parent = null;
|
||||
subnodes = null;
|
||||
properties = null;
|
||||
idField = "id";
|
||||
|
||||
this.props = props;
|
||||
read ();
|
||||
|
||||
app.putDbMapping (typename, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the mapping from the Properties. Return true if the properties were changed.
|
||||
*/
|
||||
public boolean read () {
|
||||
|
||||
long lastmod = props.lastModified ();
|
||||
if (lastmod == lastTypeChange)
|
||||
return false;
|
||||
|
||||
this.table = props.getProperty ("_tablename");
|
||||
this.idgen = props.getProperty ("_idgen");
|
||||
String sourceName = props.getProperty ("_datasource");
|
||||
if (sourceName != null)
|
||||
source = (DbSource) IServer.dbSources.get (sourceName.toLowerCase ());
|
||||
lastTypeChange = lastmod;
|
||||
// set the cached schema & keydef to null so it's rebuilt the next time around
|
||||
schema = null;
|
||||
keydef = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void rewire () {
|
||||
|
||||
// if (table != null && source != null) {
|
||||
// IServer.getLogger().log ("set data source for "+typename+" to "+source);
|
||||
Hashtable p2d = new Hashtable ();
|
||||
Hashtable d2p = new Hashtable ();
|
||||
|
||||
for (Enumeration e=props.keys(); e.hasMoreElements(); ) {
|
||||
String propName = (String) e.nextElement ();
|
||||
|
||||
if (!propName.startsWith ("_") && propName.indexOf (".") < 0) {
|
||||
String dbField = props.getProperty (propName);
|
||||
Relation rel = new Relation (dbField, propName, this, props);
|
||||
p2d.put (propName, rel);
|
||||
if (rel.localField != null)
|
||||
d2p.put (rel.localField, rel);
|
||||
// IServer.getLogger().log ("Mapping "+propName+" -> "+dbField);
|
||||
|
||||
} else if ("_id".equalsIgnoreCase (propName)) {
|
||||
idField = props.getProperty (propName);
|
||||
|
||||
} else if ("_name".equalsIgnoreCase (propName)) {
|
||||
nameField = props.getProperty (propName);
|
||||
}
|
||||
}
|
||||
prop2db = p2d;
|
||||
db2prop = d2p;
|
||||
|
||||
String parentMapping = props.getProperty ("_parent");
|
||||
if (parentMapping != null) {
|
||||
parentRel = new Relation (parentMapping, "_parent", this, props);
|
||||
if (parentRel.isReference ())
|
||||
parent = parentRel.other;
|
||||
else
|
||||
parent = (DbMapping) app.getDbMapping (parentMapping);
|
||||
}
|
||||
|
||||
String subnodeMapping = props.getProperty ("_subnodes");
|
||||
if (subnodeMapping != null) {
|
||||
subnodesRel = new Relation (subnodeMapping, "_subnodes", this, props);
|
||||
if (subnodesRel.isReference ())
|
||||
subnodes = subnodesRel.other;
|
||||
else
|
||||
subnodes = (DbMapping) app.getDbMapping (subnodeMapping);
|
||||
}
|
||||
|
||||
String propertiesMapping = props.getProperty ("_properties");
|
||||
if (propertiesMapping != null) {
|
||||
propertiesRel = new Relation (propertiesMapping, "_properties", this, props);
|
||||
if (propertiesRel.isReference ())
|
||||
properties = propertiesRel.other;
|
||||
else
|
||||
properties = (DbMapping) app.getDbMapping (propertiesMapping);
|
||||
// take over groupby flag from subnodes, if properties are subnodes
|
||||
if (propertiesRel.subnodesAreProperties && subnodesRel != null)
|
||||
propertiesRel.groupby = subnodesRel.groupby;
|
||||
}
|
||||
|
||||
IServer.getLogger().log ("rewiring: "+parent+" -> "+this+" -> "+subnodes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Connection getConnection () throws ClassNotFoundException, SQLException {
|
||||
if (source == null)
|
||||
throw new SQLException ("Tried to get Connection from non-relational embedded data source.");
|
||||
return source.getConnection ();
|
||||
}
|
||||
|
||||
public DbSource getDbSource () {
|
||||
return source;
|
||||
}
|
||||
|
||||
public String getSourceID () {
|
||||
return source == null ? "" : source.url;
|
||||
}
|
||||
|
||||
public String getTableName () {
|
||||
return table;
|
||||
}
|
||||
|
||||
public Application getApplication () {
|
||||
return app;
|
||||
}
|
||||
|
||||
public String getAppName () {
|
||||
return app.getName();
|
||||
}
|
||||
|
||||
public String getTypeName () {
|
||||
return typename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the primary key column name for objects using this mapping.
|
||||
*/
|
||||
public String getIDField () {
|
||||
return idField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the column used for (internal) names of objects of this type.
|
||||
*/
|
||||
public String getNameField () {
|
||||
return nameField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a database column name to a JavaScript property name according to this mapping.
|
||||
*/
|
||||
public Relation columnNameToProperty (String columnName) {
|
||||
return (Relation) db2prop.get (columnName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a JavaScript property name to a database column name according to this mapping.
|
||||
*/
|
||||
public Relation propertyToColumnName (String propName) {
|
||||
return (Relation) prop2db.get (propName);
|
||||
}
|
||||
|
||||
public DbMapping getParentMapping () {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public DbMapping getSubnodeMapping () {
|
||||
return subnodes;
|
||||
}
|
||||
|
||||
public void setSubnodeMapping (DbMapping sm) {
|
||||
subnodes = sm;
|
||||
}
|
||||
|
||||
public DbMapping getExactPropertyMapping (String propname) {
|
||||
if (propname == null)
|
||||
return null;
|
||||
Relation rel = (Relation) prop2db.get (propname.toLowerCase());
|
||||
return rel != null ? rel.other : null;
|
||||
}
|
||||
|
||||
public DbMapping getPropertyMapping (String propname) {
|
||||
if (propname == null)
|
||||
return properties;
|
||||
Relation rel = (Relation) prop2db.get (propname.toLowerCase());
|
||||
return rel != null && !rel.virtual ? rel.other : properties;
|
||||
}
|
||||
|
||||
public void setPropertyMapping (DbMapping pm) {
|
||||
properties = pm;
|
||||
}
|
||||
|
||||
public void setSubnodeRelation (Relation rel) {
|
||||
subnodesRel = rel;
|
||||
}
|
||||
|
||||
public void setPropertyRelation (Relation rel) {
|
||||
propertiesRel = rel;
|
||||
}
|
||||
|
||||
public Relation getSubnodeRelation () {
|
||||
return subnodesRel;
|
||||
}
|
||||
|
||||
public Relation getPropertyRelation () {
|
||||
return propertiesRel;
|
||||
}
|
||||
|
||||
public Relation getPropertyRelation (String propname) {
|
||||
if (propname == null)
|
||||
return propertiesRel;
|
||||
Relation rel = (Relation) prop2db.get (propname.toLowerCase());
|
||||
return rel != null ? rel : propertiesRel;
|
||||
}
|
||||
|
||||
public String getIDgen () {
|
||||
return idgen;
|
||||
}
|
||||
|
||||
public WrappedNodeManager getWrappedNodeManager () {
|
||||
if (app == null)
|
||||
throw new RuntimeException ("Can't get node manager from internal db mapping");
|
||||
return app.getWrappedNodeManager ();
|
||||
}
|
||||
|
||||
public boolean isRelational () {
|
||||
return source != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Village Schema object for this DbMapping.
|
||||
*/
|
||||
public Schema getSchema () throws ClassNotFoundException, SQLException, DataSetException {
|
||||
if (!isRelational ())
|
||||
throw new SQLException ("Can't get Schema for non-relational data mapping");
|
||||
// Use local variable s to avoid synchronization (schema may be nulled elsewhere)
|
||||
Schema s = schema;
|
||||
if (s != null)
|
||||
return s;
|
||||
schema = new Schema ().schema (getConnection (), table, "*");
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Village Schema object for this DbMapping.
|
||||
*/
|
||||
public KeyDef getKeyDef () {
|
||||
if (!isRelational ())
|
||||
throw new RuntimeException ("Can't get KeyDef for non-relational data mapping");
|
||||
// Use local variable s to avoid synchronization (keydef may be nulled elsewhere)
|
||||
KeyDef k = keydef;
|
||||
if (k != null)
|
||||
return k;
|
||||
keydef = new KeyDef ().addAttrib (idField);
|
||||
return keydef;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
if (app == null)
|
||||
return "[unspecified internal DbMapping]";
|
||||
else
|
||||
return ("["+app.getName()+"."+typename+"]");
|
||||
}
|
||||
|
||||
public long getLastTypeChange () {
|
||||
return lastTypeChange;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
95
src/helma/objectmodel/DbSource.java
Normal file
95
src/helma/objectmodel/DbSource.java
Normal file
|
@ -0,0 +1,95 @@
|
|||
// DbSource.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.Hashtable;
|
||||
import helma.objectmodel.db.Transactor;
|
||||
|
||||
/**
|
||||
* This class describes a releational data source (URL, driver, user and password).
|
||||
*/
|
||||
|
||||
public class DbSource {
|
||||
|
||||
private String name;
|
||||
protected String url;
|
||||
private String driver;
|
||||
protected String user;
|
||||
private String password;
|
||||
|
||||
public DbSource (String name) throws ClassNotFoundException {
|
||||
this.name = name;
|
||||
url = IServer.dbProps.getProperty (name+".url");
|
||||
driver = IServer.dbProps.getProperty (name+".driver");
|
||||
Class.forName (driver);
|
||||
user = IServer.dbProps.getProperty (name+".user");
|
||||
password = IServer.dbProps.getProperty (name+".password");
|
||||
IServer.getLogger().log ("created db source ["+name+", "+url+", "+driver+", "+user+"]");
|
||||
IServer.dbSources.put (name.toLowerCase (), this);
|
||||
}
|
||||
|
||||
public Connection getConnection () throws ClassNotFoundException, SQLException {
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
Connection con = tx.getConnection (this);
|
||||
if (con == null || con.isClosed ()) {
|
||||
Class.forName (driver);
|
||||
con = DriverManager.getConnection (url, user, password);
|
||||
// If we wanted to use SQL transactions, we'd set autoCommit to
|
||||
// false here and make commit/rollback invocations in Transactor methods;
|
||||
IServer.getLogger().log ("Created new Connection to "+url);
|
||||
tx.registerConnection (this, con);
|
||||
}
|
||||
return con;
|
||||
}
|
||||
|
||||
public String getDriverName () {
|
||||
return driver;
|
||||
}
|
||||
|
||||
public String getName () {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
114
src/helma/objectmodel/INode.java
Normal file
114
src/helma/objectmodel/INode.java
Normal file
|
@ -0,0 +1,114 @@
|
|||
// INode.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Interface that all Nodes implement. Currently, there are two implementations:
|
||||
* Transient nodes which only exist in memory, and persistent Nodes, which are
|
||||
* stored in a database (either the internal Object DB or an external relational DB).
|
||||
*/
|
||||
|
||||
public interface INode {
|
||||
|
||||
public final static String webTypes = "image/jpeg, image/gif, image/png";
|
||||
|
||||
public final static int TRANSIENT = -3;
|
||||
public final static int VIRTUAL = -2;
|
||||
public final static int INVALID = -1;
|
||||
public final static int CLEAN = 0;
|
||||
public final static int NEW = 1;
|
||||
public final static int MODIFIED = 2;
|
||||
public final static int DELETED = 3;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* id-related methods
|
||||
*/
|
||||
|
||||
public String getID ();
|
||||
public String getName ();
|
||||
public String getNameOrID (); // get name or id depending if it's a named or an anonymous node.
|
||||
public void setDbMapping (DbMapping dbmap);
|
||||
public DbMapping getDbMapping ();
|
||||
public int getState ();
|
||||
public void setState (int s);
|
||||
public String getFullName ();
|
||||
public String getFullName (INode root);
|
||||
public INode[] getPath ();
|
||||
public void setName (String name);
|
||||
public long lastModified ();
|
||||
public long created ();
|
||||
public boolean isAnonymous (); // is this a named node, or an anonymous node in a collection?
|
||||
// public void setPrototype (String prototype);
|
||||
// public String getPrototype ();
|
||||
public INode getCacheNode ();
|
||||
|
||||
/**
|
||||
* node-related methods
|
||||
*/
|
||||
|
||||
public INode getParent ();
|
||||
public void setSubnodeRelation (String rel);
|
||||
public String getSubnodeRelation ();
|
||||
public int numberOfNodes ();
|
||||
public INode addNode (INode node);
|
||||
public INode addNode (INode node, int where);
|
||||
public INode createNode (String name);
|
||||
public INode createNode (String name, int where);
|
||||
public Enumeration getSubnodes ();
|
||||
public INode getSubnode (String name);
|
||||
public INode getSubnodeAt (int index);
|
||||
public int contains (INode node);
|
||||
public boolean remove ();
|
||||
public void removeNode (INode node);
|
||||
|
||||
/**
|
||||
* property-related methods
|
||||
*/
|
||||
|
||||
public Enumeration properties ();
|
||||
public IProperty get (String name, boolean inherit);
|
||||
public String getString (String name, boolean inherit);
|
||||
public String getString (String name, String defaultValue, boolean inherit);
|
||||
public boolean getBoolean (String name, boolean inherit);
|
||||
public Date getDate (String name, boolean inherit);
|
||||
public long getInteger (String name, boolean inherit);
|
||||
public double getFloat (String name, boolean inherit);
|
||||
public INode getNode (String name, boolean inherit);
|
||||
public Object getJavaObject (String name, boolean inherit);
|
||||
|
||||
public void setString (String name, String value);
|
||||
public void setBoolean (String name, boolean value);
|
||||
public void setDate (String name, Date value);
|
||||
public void setInteger (String name, long value);
|
||||
public void setFloat (String name, double value);
|
||||
public void setNode (String name, INode value);
|
||||
public void setJavaObject (String name, Object value);
|
||||
|
||||
public void unset (String name);
|
||||
|
||||
/**
|
||||
* content-related methods
|
||||
*/
|
||||
|
||||
public String getContentType ();
|
||||
public void setContentType (String type);
|
||||
public int getContentLength ();
|
||||
public void setContent (byte content[], String type);
|
||||
public void setContent (String content);
|
||||
public byte[] getContent ();
|
||||
public String getText ();
|
||||
public String getUrl (INode root, INode userroot, String tmpname);
|
||||
public String getHref (INode root, INode userroot, String tmpname, String prefix);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
15
src/helma/objectmodel/INodeListener.java
Normal file
15
src/helma/objectmodel/INodeListener.java
Normal file
|
@ -0,0 +1,15 @@
|
|||
// INodeListener.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
/**
|
||||
* An interface for objects that wish to be notified when certain nodes are
|
||||
* modified. This is not used in the HOP as much as it used to be.
|
||||
*/
|
||||
|
||||
public interface INodeListener {
|
||||
|
||||
public void nodeChanged (NodeEvent event);
|
||||
|
||||
}
|
21
src/helma/objectmodel/IPathElement.java
Normal file
21
src/helma/objectmodel/IPathElement.java
Normal file
|
@ -0,0 +1,21 @@
|
|||
// INode.java
|
||||
// Copyright (c) Hannes Wallnöfer 2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
|
||||
/**
|
||||
* Minimal Interface for Nodes that build a hierarchic tree
|
||||
*/
|
||||
|
||||
public interface IPathElement {
|
||||
|
||||
public INode getSubnode (String name);
|
||||
public INode getNode (String name, boolean inherit);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
34
src/helma/objectmodel/IProperty.java
Normal file
34
src/helma/objectmodel/IProperty.java
Normal file
|
@ -0,0 +1,34 @@
|
|||
// IProperty.java
|
||||
// Copyright (c) Hannes Wallnöfer 1997-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Interface that is implemented by node properties.
|
||||
*/
|
||||
|
||||
public interface IProperty {
|
||||
|
||||
public static final int STRING = 1;
|
||||
public static final int BOOLEAN = 2;
|
||||
public static final int DATE = 3;
|
||||
public static final int INTEGER = 4;
|
||||
public static final int FLOAT = 5;
|
||||
public static final int NODE = 6;
|
||||
public static final int JAVAOBJECT = 7;
|
||||
|
||||
public String getName ();
|
||||
public int getType ();
|
||||
public Object getValue ();
|
||||
|
||||
public INode getNodeValue ();
|
||||
public String getStringValue ();
|
||||
public boolean getBooleanValue ();
|
||||
public long getIntegerValue ();
|
||||
public double getFloatValue ();
|
||||
public Date getDateValue ();
|
||||
public Object getJavaObjectValue ();
|
||||
|
||||
}
|
79
src/helma/objectmodel/IServer.java
Normal file
79
src/helma/objectmodel/IServer.java
Normal file
|
@ -0,0 +1,79 @@
|
|||
// IServer.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
import helma.util.*;
|
||||
import helma.xmlrpc.WebServer;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Abstract Server class. Defines the methods all servers have to implement.
|
||||
*/
|
||||
|
||||
public abstract class IServer {
|
||||
|
||||
// public static final Object sync = new Object ();
|
||||
public static SystemProperties sysProps, dbProps;
|
||||
public static Hashtable dbSources;
|
||||
|
||||
protected static String hopHome = null;
|
||||
|
||||
private static Logger logger;
|
||||
|
||||
protected static WebServer xmlrpc;
|
||||
|
||||
|
||||
/* public abstract INode getAppRoot (String appID);
|
||||
|
||||
public abstract INode getAppNode (String appID, Vector path, String name);
|
||||
|
||||
public abstract INode getSubnode (String path); */
|
||||
|
||||
public static void throwNodeEvent (NodeEvent evt) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public static void addNodeListener (String id, INodeListener listener) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public static void removeNodeListener (String node, INodeListener listener) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public static Logger getLogger () {
|
||||
if (logger == null) {
|
||||
String logDir = sysProps.getProperty ("logdir");
|
||||
if (logDir == null) {
|
||||
logger = new Logger (System.out);
|
||||
} else {
|
||||
try {
|
||||
File helper = new File (logDir);
|
||||
if (hopHome != null && !helper.isAbsolute ())
|
||||
helper = new File (hopHome, logDir);
|
||||
logDir = helper.getAbsolutePath ();
|
||||
logger = new Logger (logDir, "hop");
|
||||
} catch (IOException iox) {
|
||||
System.err.println ("Could not create Logger for log/hop: "+iox);
|
||||
// fallback to System.out
|
||||
logger = new Logger (System.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
return logger;
|
||||
}
|
||||
|
||||
public static String getHopHome () {
|
||||
return hopHome;
|
||||
}
|
||||
|
||||
public static WebServer getXmlRpcServer() {
|
||||
return xmlrpc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
140
src/helma/objectmodel/Key.java
Normal file
140
src/helma/objectmodel/Key.java
Normal file
|
@ -0,0 +1,140 @@
|
|||
// Key.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
|
||||
import Acme.LruHashtable;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* This is the internal representation of a database key. It is constructed
|
||||
* out of the database URL, the table name, the user name and the database
|
||||
* key of the node and unique within each HOP application. Currently only
|
||||
* single keys are supported.
|
||||
*/
|
||||
public class Key implements Serializable {
|
||||
|
||||
protected String type;
|
||||
protected String id;
|
||||
private int hash;
|
||||
private static LruHashtable keycache;
|
||||
|
||||
|
||||
public synchronized static Key makeKey (DbMapping dbmap, String id) {
|
||||
String _type = dbmap == null ? "" : dbmap.typename;
|
||||
String _id = id.trim (); // removed .toLowerCase() - hw
|
||||
return makeKey (_type, _id);
|
||||
}
|
||||
|
||||
private synchronized static Key makeKey (String _type, String _id) {
|
||||
if (keycache == null)
|
||||
keycache = new LruHashtable (1000, 0.9f);
|
||||
Key k = (Key) keycache.get (_type+"#"+_id);
|
||||
if (k == null) {
|
||||
k = new Key (_type, _id);
|
||||
keycache.put (_type+"#"+_id, k);
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
private Key (String type, String id) {
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
hash = this.id.hashCode ();
|
||||
}
|
||||
|
||||
public boolean equals (Object what) {
|
||||
if (what == this)
|
||||
return true;
|
||||
if (what == null || !(what instanceof Key))
|
||||
return false;
|
||||
Key other = (Key) what;
|
||||
if (type == null)
|
||||
return (id.equals (other.id) && other.type == null);
|
||||
else
|
||||
return (id.equals (other.id) && type.equals (other.type));
|
||||
}
|
||||
|
||||
public int hashCode () {
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Key for a virtual node contained by this node, that is, a node that does
|
||||
* not represent a record in the database. The main objective here is to generate
|
||||
* a key that can't be mistaken for a relational db key.
|
||||
*/
|
||||
public Key getVirtualKey (String sid) {
|
||||
Key virtkey = makeKey ("", getVirtualID (type, id, sid));
|
||||
return virtkey;
|
||||
}
|
||||
|
||||
public static String getVirtualID (DbMapping pmap, String pid, String sid) {
|
||||
String ptype = pmap == null ? "" : pmap.typename;
|
||||
return ptype+"/"+pid + "*h~v*" + sid;
|
||||
}
|
||||
|
||||
public static String getVirtualID (String ptype, String pid, String sid) {
|
||||
return ptype+"/"+pid + "*h~v*" + sid;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return type+"["+id+"]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
765
src/helma/objectmodel/Node.java
Normal file
765
src/helma/objectmodel/Node.java
Normal file
|
@ -0,0 +1,765 @@
|
|||
// Node.java
|
||||
// Copyright (c) Hannes Wallnöfer 1997-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import helma.util.*;
|
||||
|
||||
/**
|
||||
* A transient implementation of INode. If a transient node is stored in a
|
||||
* database, it is automatically (along with all reachable subnodes) rebuilt
|
||||
* as a persistent node.
|
||||
*/
|
||||
|
||||
public class Node implements INode, Serializable {
|
||||
|
||||
|
||||
protected Hashtable propMap, nodeMap;
|
||||
protected Vector nodes;
|
||||
protected Node parent;
|
||||
protected Vector links; // links to this node
|
||||
protected Vector proplinks; // nodes using this node as property
|
||||
|
||||
protected String contentType;
|
||||
protected byte content[];
|
||||
|
||||
protected long created;
|
||||
protected long lastmodified;
|
||||
|
||||
protected String id, name;
|
||||
// is the main identity a named property or an anonymous node in a collection?
|
||||
protected boolean anonymous = false;
|
||||
|
||||
transient DbMapping dbmap;
|
||||
|
||||
transient boolean adoptName = true; // little helper to know if this node is being converted
|
||||
|
||||
private static long idgen = 0;
|
||||
|
||||
public String generateID () {
|
||||
return "t"+idgen++; // make transient ids differ from persistent ones
|
||||
}
|
||||
|
||||
public Node () {
|
||||
id = generateID ();
|
||||
name = id;
|
||||
created = lastmodified = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt einen neuen Node.
|
||||
*/
|
||||
public Node (String n) {
|
||||
id = generateID ();
|
||||
name = n == null || "".equals (n) ? id : n;
|
||||
created = lastmodified = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt einen Clone eines Nodes in der "lokalen" Implementierung von INode.
|
||||
*/
|
||||
public Node (INode node, Hashtable ntable, boolean conversionRoot) {
|
||||
this.id = generateID ();
|
||||
this.name = node.getName ();
|
||||
created = lastmodified = System.currentTimeMillis ();
|
||||
setContent (node.getContent (), node.getContentType ());
|
||||
created = lastmodified = System.currentTimeMillis ();
|
||||
ntable.put (node, this);
|
||||
adoptName = !conversionRoot; // only take over name from property if this is not the transient root
|
||||
for (Enumeration e = node.getSubnodes (); e.hasMoreElements (); ) {
|
||||
INode next = (INode) e.nextElement ();
|
||||
Node nextc = (Node) ntable.get (next);
|
||||
if (nextc == null)
|
||||
nextc = new Node (next, ntable, true);
|
||||
addNode (nextc);
|
||||
}
|
||||
for (Enumeration e = node.properties (); e.hasMoreElements (); ) {
|
||||
IProperty next = (IProperty) e.nextElement ();
|
||||
int t = next.getType ();
|
||||
if (t == IProperty.NODE) {
|
||||
INode n = next.getNodeValue ();
|
||||
Node nextc = (Node) ntable.get (n);
|
||||
if (nextc == null)
|
||||
nextc = new Node (n, ntable, true);
|
||||
setNode (next.getName (), nextc);
|
||||
} else if (t == IProperty.STRING) {
|
||||
setString (next.getName (), next.getStringValue ());
|
||||
} else if (t == IProperty.INTEGER) {
|
||||
setInteger (next.getName (), next.getIntegerValue ());
|
||||
} else if (t == IProperty.FLOAT) {
|
||||
setFloat (next.getName (), next.getFloatValue ());
|
||||
} else if (t == IProperty.BOOLEAN) {
|
||||
setBoolean (next.getName (), next.getBooleanValue ());
|
||||
} else if (t == IProperty.DATE) {
|
||||
setDate (next.getName (), next.getDateValue ());
|
||||
}
|
||||
}
|
||||
adoptName = true; // switch back to normal name adoption behaviour
|
||||
}
|
||||
|
||||
|
||||
public void setDbMapping (DbMapping dbmap) {
|
||||
this.dbmap = dbmap;
|
||||
}
|
||||
|
||||
public DbMapping getDbMapping () {
|
||||
return dbmap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* navigation-related
|
||||
*/
|
||||
|
||||
public String getID () {
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean isAnonymous () {
|
||||
return anonymous;
|
||||
}
|
||||
|
||||
|
||||
public String getName () {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getNameOrID () {
|
||||
return anonymous ? id : name;
|
||||
}
|
||||
|
||||
public int getState () {
|
||||
return TRANSIENT;
|
||||
}
|
||||
|
||||
public void setState (int s) {
|
||||
// state always is TRANSIENT on this kind of node
|
||||
}
|
||||
|
||||
public String getFullName () {
|
||||
return getFullName (null);
|
||||
}
|
||||
|
||||
public String getFullName (INode root) {
|
||||
String fullname = "";
|
||||
String divider = null;
|
||||
StringBuffer b = new StringBuffer ();
|
||||
Node p = this;
|
||||
while (p != null && p.parent != null && p != root) {
|
||||
if (divider != null)
|
||||
b.insert (0, divider);
|
||||
else
|
||||
divider = "/";
|
||||
b.insert (0, p.getNameOrID ());
|
||||
p = p.parent;
|
||||
}
|
||||
return b.toString ();
|
||||
}
|
||||
|
||||
public INode[] getPath () {
|
||||
int pathSize = 1;
|
||||
INode p = getParent ();
|
||||
while (p != null) {
|
||||
pathSize +=1;
|
||||
p = p.getParent ();
|
||||
}
|
||||
INode path[] = new INode[pathSize];
|
||||
p = this;
|
||||
for (int i = pathSize-1; i>=0; i--) {
|
||||
path[i] = p;
|
||||
p = p.getParent ();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setName (String name) {
|
||||
if (name.indexOf('/') > -1)
|
||||
throw new RuntimeException ("The name of the node must not contain \"/\".");
|
||||
if (name == null || name.trim().length() == 0)
|
||||
this.name = id;
|
||||
else
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public INode getParent () {
|
||||
return parent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* INode-related
|
||||
*/
|
||||
|
||||
public void setSubnodeRelation (String rel) {
|
||||
throw new RuntimeException ("Can't set subnode relation for non-persistent Node.");
|
||||
}
|
||||
|
||||
public String getSubnodeRelation () {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int numberOfNodes () {
|
||||
return nodes == null ? 0 : nodes.size ();
|
||||
}
|
||||
|
||||
public INode addNode (INode elem) {
|
||||
return addNode (elem, numberOfNodes ());
|
||||
}
|
||||
|
||||
public INode addNode (INode elem, int where) {
|
||||
|
||||
if (where < 0 || where > numberOfNodes ())
|
||||
where = numberOfNodes ();
|
||||
|
||||
String n = elem.getName();
|
||||
if (n.indexOf('/') > -1)
|
||||
throw new RuntimeException ("The name of a node must not contain \"/\" (slash).");
|
||||
|
||||
// IServer.getLogger().log ("adding: "+node+" -- "+node.getContentLength ());
|
||||
if (nodeMap != null && nodeMap.get (elem.getID ()) != null) {
|
||||
nodes.removeElement (elem);
|
||||
where = Math.min (where, numberOfNodes ());
|
||||
nodes.insertElementAt (elem, where);
|
||||
return elem;
|
||||
}
|
||||
|
||||
if (nodeMap == null) nodeMap = new Hashtable ();
|
||||
if (nodes == null) nodes = new Vector ();
|
||||
|
||||
nodeMap.put (elem.getID (), elem);
|
||||
nodes.insertElementAt (elem, where);
|
||||
|
||||
if (elem instanceof Node) {
|
||||
Node node = (Node) elem;
|
||||
if (node.parent == null) {
|
||||
node.parent = this;
|
||||
node.anonymous = true;
|
||||
}
|
||||
if (node.parent != null && (node.parent != this || !node.anonymous)) {
|
||||
node.registerLink (this);
|
||||
}
|
||||
}
|
||||
|
||||
lastmodified = System.currentTimeMillis ();
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_ADDED, node));
|
||||
return elem;
|
||||
}
|
||||
|
||||
public INode createNode () {
|
||||
return createNode (null, 0); // where is ignored since this is an anonymous node
|
||||
}
|
||||
|
||||
public INode createNode (int where) {
|
||||
return createNode (null, where);
|
||||
}
|
||||
|
||||
public INode createNode (String nm) {
|
||||
return createNode (nm, numberOfNodes ()); // where is usually ignored (if nm != null)
|
||||
}
|
||||
|
||||
public INode createNode (String nm, int where) {
|
||||
boolean anon = false;
|
||||
if (nm == null || "".equals (nm.trim ()))
|
||||
anon = true;
|
||||
INode n = new Node (nm);
|
||||
if (anon)
|
||||
addNode (n, where);
|
||||
else
|
||||
setNode (nm, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* register a node that links to this node.
|
||||
*/
|
||||
protected void registerLink (Node from) {
|
||||
if (links == null)
|
||||
links = new Vector ();
|
||||
if (!links.contains (from))
|
||||
links.addElement (from);
|
||||
}
|
||||
|
||||
public INode getSubnode (String name) {
|
||||
return getSubnode (name, false);
|
||||
}
|
||||
|
||||
public INode getSubnode (String name, boolean inherit) {
|
||||
StringTokenizer st = new StringTokenizer (name, "/");
|
||||
Node retval = this, runner;
|
||||
while (st.hasMoreTokens () && retval != null) {
|
||||
runner = retval;
|
||||
String next = st.nextToken().trim().toLowerCase ();
|
||||
if ("".equals (next))
|
||||
retval = this;
|
||||
else
|
||||
retval = runner.nodeMap == null ? null : (Node) runner.nodeMap.get (next);
|
||||
if (retval == null)
|
||||
retval = (Node) runner.getNode (next, inherit);
|
||||
if (retval == null && inherit && runner == this && parent != null)
|
||||
retval = (Node) parent.getSubnode (next, inherit);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
public INode getSubnodeAt (int index) {
|
||||
return nodes == null ? null : (INode) nodes.elementAt (index);
|
||||
}
|
||||
|
||||
public int contains (INode n) {
|
||||
if (n == null || nodes == null)
|
||||
return -1;
|
||||
return nodes.indexOf (n);
|
||||
}
|
||||
|
||||
public boolean remove () {
|
||||
if (anonymous)
|
||||
parent.unset (name);
|
||||
else
|
||||
parent.removeNode (this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void removeNode (INode node) {
|
||||
// IServer.getLogger().log ("removing: "+ node);
|
||||
releaseNode (node);
|
||||
Node n = (Node) node;
|
||||
if (n.getParent () == this && n.anonymous) {
|
||||
int l = n.links == null ? 0 : n.links.size (); // notify nodes that link to n that n is going down.
|
||||
for (int i = 0; i < l; i++) {
|
||||
Node link = (Node) n.links.elementAt (i);
|
||||
link.releaseNode (n);
|
||||
}
|
||||
if (n.proplinks != null) {
|
||||
// clean up all nodes that use n as a property
|
||||
for (Enumeration e1 = n.proplinks.elements (); e1.hasMoreElements (); ) try {
|
||||
Property p = (Property) e1.nextElement ();
|
||||
p.node.propMap.remove (p.propname.toLowerCase ());
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
for (Enumeration e2 = n.properties (); e2.hasMoreElements (); ) {
|
||||
// tell all nodes that are properties of n that they are no longer used as such
|
||||
Property p = (Property) e2.nextElement ();
|
||||
if (p != null && p.type == Property.NODE)
|
||||
p.unregisterNode ();
|
||||
}
|
||||
// remove all subnodes, giving them a chance to destroy themselves.
|
||||
Vector v = new Vector (); // removeElement modifies the Vector we are enumerating, so we are extra careful.
|
||||
for (Enumeration e3 = n.getSubnodes (); e3.hasMoreElements (); ) {
|
||||
v.addElement (e3.nextElement ());
|
||||
}
|
||||
int m = v.size ();
|
||||
for (int i=0; i<m; i++) {
|
||||
n.removeNode ((Node) v.elementAt (i));
|
||||
}
|
||||
if (n.content != null) {
|
||||
// IServer.getLogger().log ("destroying content of node "+n.getName ());
|
||||
// ObjectStore.destroy (n.content);
|
||||
}
|
||||
} else {
|
||||
//
|
||||
n.links.removeElement (this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "Physically" remove a subnode from the subnodes table.
|
||||
* the logical stuff necessary for keeping data consistent is done elsewhere (in removeNode).
|
||||
*/
|
||||
protected void releaseNode (INode node) {
|
||||
if (nodes == null || nodeMap == null)
|
||||
|
||||
return;
|
||||
int runner = nodes.indexOf (node);
|
||||
// this is due to difference between .equals() and ==
|
||||
while (runner > -1 && nodes.elementAt (runner) != node)
|
||||
runner = nodes.indexOf (node, Math.min (nodes.size()-1, runner+1));
|
||||
if (runner > -1)
|
||||
nodes.removeElementAt (runner);
|
||||
// nodes.remove (node);
|
||||
Object what = nodeMap.remove (node.getName ().toLowerCase ());
|
||||
// Server.throwNodeEvent (new NodeEvent (node, NodeEvent.NODE_REMOVED));
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_REMOVED, node));
|
||||
lastmodified = System.currentTimeMillis ();
|
||||
// IServer.getLogger().log ("released node "+node +" from "+this+" oldobj = "+what);
|
||||
}
|
||||
|
||||
public Enumeration getSubnodes () {
|
||||
return nodes == null ? new Vector ().elements () : nodes.elements ();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* property-related
|
||||
*/
|
||||
|
||||
public Enumeration properties () {
|
||||
return propMap == null ? new Vector ().elements () : propMap.elements ();
|
||||
}
|
||||
|
||||
|
||||
private Property getProperty (String propname, boolean inherit) {
|
||||
Property prop = propMap == null ? null : (Property) propMap.get (propname);
|
||||
if (prop == null && inherit && parent != null) {
|
||||
prop = parent.getProperty (propname, inherit);
|
||||
}
|
||||
// check if we have to create a virtual node
|
||||
if (prop == null && dbmap != null) {
|
||||
Relation rel = dbmap.getPropertyRelation (propname);
|
||||
if (rel != null && rel.virtual) {
|
||||
prop = makeVirtualNode (propname, rel);
|
||||
}
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
private Property makeVirtualNode (String propname, Relation rel) {
|
||||
INode node = new helma.objectmodel.db.Node (rel.propname, dbmap.getWrappedNodeManager());
|
||||
// node.setState (TRANSIENT);
|
||||
// make a db mapping good enough that the virtual node finds its subnodes
|
||||
DbMapping dbm = new DbMapping ();
|
||||
dbm.setSubnodeMapping (rel.other);
|
||||
dbm.setSubnodeRelation (rel);
|
||||
dbm.setPropertyMapping (rel.other);
|
||||
dbm.setPropertyRelation (rel);
|
||||
node.setDbMapping (dbm);
|
||||
setNode (propname, node);
|
||||
return (Property) propMap.get (propname);
|
||||
}
|
||||
|
||||
|
||||
public IProperty get (String propname, boolean inherit) {
|
||||
propname = propname.toLowerCase ();
|
||||
return getProperty (propname, inherit);
|
||||
}
|
||||
|
||||
public String getString (String propname, String defaultValue, boolean inherit) {
|
||||
String propValue = getString (propname, inherit);
|
||||
return propValue == null ? defaultValue : propValue;
|
||||
}
|
||||
|
||||
public String getString (String propname, boolean inherit) {
|
||||
propname = propname.toLowerCase ();
|
||||
Property prop = getProperty (propname, inherit);
|
||||
try {
|
||||
return prop.getStringValue ();
|
||||
} catch (Exception ignore) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
public long getInteger (String propname, boolean inherit) {
|
||||
propname = propname.toLowerCase ();
|
||||
Property prop = getProperty (propname, inherit);
|
||||
try {
|
||||
return prop.getIntegerValue ();
|
||||
} catch (Exception ignore) {}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public double getFloat (String propname, boolean inherit) {
|
||||
propname = propname.toLowerCase ();
|
||||
Property prop = getProperty (propname, inherit);
|
||||
try {
|
||||
return prop.getFloatValue ();
|
||||
} catch (Exception ignore) {}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public Date getDate (String propname, boolean inherit) {
|
||||
propname = propname.toLowerCase ();
|
||||
Property prop = getProperty (propname, inherit);
|
||||
try {
|
||||
return prop.getDateValue ();
|
||||
} catch (Exception ignore) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public boolean getBoolean (String propname, boolean inherit) {
|
||||
propname = propname.toLowerCase ();
|
||||
Property prop = getProperty (propname, inherit);
|
||||
try {
|
||||
return prop.getBooleanValue ();
|
||||
} catch (Exception ignore) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
public INode getNode (String propname, boolean inherit) {
|
||||
propname = propname.toLowerCase ();
|
||||
Property prop = getProperty (propname, inherit);
|
||||
try {
|
||||
return prop.getNodeValue ();
|
||||
} catch (Exception ignore) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getJavaObject (String propname, boolean inherit) {
|
||||
propname = propname.toLowerCase ();
|
||||
Property prop = getProperty (propname, inherit);
|
||||
try {
|
||||
return prop.getJavaObjectValue ();
|
||||
} catch (Exception ignore) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
// create a property if it doesn't exist for this name
|
||||
private Property initProperty (String propname) {
|
||||
if (propMap == null)
|
||||
propMap = new Hashtable ();
|
||||
propname = propname.trim ();
|
||||
String p2 = propname.toLowerCase ();
|
||||
Property prop = (Property) propMap.get (p2);
|
||||
if (prop == null) {
|
||||
prop = new Property (propname, this);
|
||||
propMap.put (p2, prop);
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setString (String propname, String value) {
|
||||
// IServer.getLogger().log ("setting String prop");
|
||||
Property prop = initProperty (propname);
|
||||
try {
|
||||
prop.setStringValue (value);
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
||||
} catch (java.text.ParseException x) {
|
||||
throw new RuntimeException ("Fehler beim Parsen des Datum-Strings");
|
||||
}
|
||||
lastmodified = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
public void setInteger (String propname, long value) {
|
||||
// IServer.getLogger().log ("setting bool prop");
|
||||
Property prop = initProperty (propname);
|
||||
prop.setIntegerValue (value);
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
||||
lastmodified = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
public void setFloat (String propname, double value) {
|
||||
// IServer.getLogger().log ("setting bool prop");
|
||||
Property prop = initProperty (propname);
|
||||
prop.setFloatValue (value);
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
||||
lastmodified = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
public void setBoolean (String propname, boolean value) {
|
||||
// IServer.getLogger().log ("setting bool prop");
|
||||
Property prop = initProperty (propname);
|
||||
prop.setBooleanValue (value);
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
||||
lastmodified = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
|
||||
public void setDate (String propname, Date value) {
|
||||
// IServer.getLogger().log ("setting date prop");
|
||||
Property prop = initProperty (propname);
|
||||
prop.setDateValue (value);
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
||||
lastmodified = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
public void setJavaObject (String propname, Object value) {
|
||||
// IServer.getLogger().log ("setting date prop");
|
||||
Property prop = initProperty (propname);
|
||||
prop.setJavaObjectValue (value);
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
||||
lastmodified = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
public void setNode (String propname, INode value) {
|
||||
// IServer.getLogger().log ("setting date prop");
|
||||
Property prop = initProperty (propname);
|
||||
prop.setNodeValue (value);
|
||||
|
||||
// check if the main identity of this node is as a named property
|
||||
// or as an anonymous node in a collection
|
||||
if (value instanceof Node) {
|
||||
Node n = (Node) value;
|
||||
if (n.parent == null && n.adoptName) {
|
||||
n.name = propname;
|
||||
n.parent = this;
|
||||
n.anonymous = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_ADDED, n));
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
||||
lastmodified = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
public void unset (String propname) {
|
||||
if (propMap == null)
|
||||
return;
|
||||
try {
|
||||
Property p = (Property) propMap.remove (propname.toLowerCase ());
|
||||
if (p != null && p.type == Property.NODE)
|
||||
p.unregisterNode ();
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
||||
lastmodified = System.currentTimeMillis ();
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
|
||||
protected void registerPropLink (Property p) {
|
||||
if (proplinks == null)
|
||||
proplinks = new Vector ();
|
||||
proplinks.addElement (p);
|
||||
// IServer.getLogger().log ("registered proplink from "+p.node.getFullName ());
|
||||
// the NodeEvent is thrown later, since the node is not yet in the prop table
|
||||
}
|
||||
|
||||
protected void unregisterPropLink (Property p) {
|
||||
if (proplinks != null)
|
||||
proplinks.removeElement (p);
|
||||
// IServer.getLogger().log ("unregistered proplink from "+p.node.getFullName ());
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.NODE_REMOVED));
|
||||
// Server.throwNodeEvent (new NodeEvent (p.node, NodeEvent.SUBNODE_REMOVED, this));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* content-related
|
||||
*/
|
||||
|
||||
public boolean isText () throws IOException {
|
||||
return getContentType().indexOf ("text/") == 0;
|
||||
}
|
||||
|
||||
public boolean isBinary () throws IOException {
|
||||
return getContentType().indexOf ("text/") != 0;
|
||||
}
|
||||
|
||||
public String getContentType () {
|
||||
if (contentType == null)
|
||||
return "text/plain";
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public void setContentType (String type) {
|
||||
contentType = type;
|
||||
lastmodified = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
public int getContentLength () {
|
||||
if (content == null)
|
||||
return 0;
|
||||
return content.length;
|
||||
}
|
||||
|
||||
public void setContent (byte cnt[], String type) {
|
||||
if (type != null)
|
||||
contentType = type;
|
||||
content = cnt;
|
||||
lastmodified = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
public void setContent (String cstr) {
|
||||
content = cstr == null ? null : cstr.getBytes ();
|
||||
lastmodified = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
public byte[] getContent () {
|
||||
if (content == null || content.length == 0)
|
||||
return "".getBytes ();
|
||||
byte retval[] = new byte[content.length];
|
||||
System.arraycopy (content, 0, retval, 0, content.length);
|
||||
return retval;
|
||||
}
|
||||
|
||||
public String writeToFile (String dir) {
|
||||
return writeToFile (dir, null);
|
||||
}
|
||||
|
||||
public String writeToFile (String dir, String fname) {
|
||||
try {
|
||||
File base = new File (dir);
|
||||
// make directories if they don't exist
|
||||
if (!base.exists ())
|
||||
base.mkdirs ();
|
||||
|
||||
String filename = name;
|
||||
if (fname != null) {
|
||||
if (fname.indexOf (".") < 0) {
|
||||
// check if we can use extension from name
|
||||
int ndot = name == null ? -1 : name.lastIndexOf (".");
|
||||
if (ndot > -1)
|
||||
filename = fname + name.substring (ndot);
|
||||
else
|
||||
filename = fname;
|
||||
} else {
|
||||
filename = fname;
|
||||
}
|
||||
}
|
||||
File file = new File (base, filename);
|
||||
FileOutputStream fout = new FileOutputStream (file);
|
||||
fout.write (getContent ());
|
||||
fout.close ();
|
||||
return filename;
|
||||
} catch (Exception x) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getText () {
|
||||
if (content != null) {
|
||||
if (getContentType ().indexOf ("text/") == 0) {
|
||||
return new String (content);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public String getHref (INode root, INode userroot, String tmpname, String prefix) {
|
||||
return prefix + getUrl (root, userroot, tmpname);
|
||||
}
|
||||
|
||||
public String getUrl (INode root, INode userroot, String tmpname) {
|
||||
throw new RuntimeException ("HREFs on transient (non-db based) Nodes not supported");
|
||||
}
|
||||
|
||||
|
||||
public long lastModified () {
|
||||
return lastmodified;
|
||||
}
|
||||
|
||||
public long created () {
|
||||
return created;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return "Node " + name;
|
||||
}
|
||||
|
||||
|
||||
protected Node convert (INode n) {
|
||||
Hashtable ntable = new Hashtable ();
|
||||
Node converted = new Node (n, ntable, false);
|
||||
return converted;
|
||||
}
|
||||
|
||||
Node cacheNode;
|
||||
/**
|
||||
* Get the cache node for this node. This can be used to store transient cache data per node from Javascript.
|
||||
*/
|
||||
public synchronized INode getCacheNode () {
|
||||
if (cacheNode == null)
|
||||
cacheNode = new Node();
|
||||
return cacheNode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
46
src/helma/objectmodel/NodeDataSource.java
Normal file
46
src/helma/objectmodel/NodeDataSource.java
Normal file
|
@ -0,0 +1,46 @@
|
|||
// NodeDataSource.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
import javax.activation.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Makes Nodes usable as Datasources in the Java Activation Framework (JAF)
|
||||
*/
|
||||
|
||||
public class NodeDataSource implements DataSource {
|
||||
|
||||
private INode node;
|
||||
private String name;
|
||||
|
||||
public NodeDataSource (INode node) {
|
||||
this.node = node;
|
||||
this.name = node.getName ();
|
||||
}
|
||||
|
||||
public NodeDataSource (INode node, String name) {
|
||||
this.node = node;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new ByteArrayInputStream(node.getContent ());
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream () throws IOException {
|
||||
throw new IOException ("Can't write to Node object.");
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return node.getContentType ();
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
59
src/helma/objectmodel/NodeEvent.java
Normal file
59
src/helma/objectmodel/NodeEvent.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
// NodeEvent.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* This is passed to NodeListeners when a node is modified.
|
||||
*/
|
||||
|
||||
public class NodeEvent implements Serializable {
|
||||
|
||||
public static final int CONTENT_CHANGED = 0;
|
||||
public static final int PROPERTIES_CHANGED = 1;
|
||||
public static final int NODE_REMOVED = 2;
|
||||
public static final int NODE_RENAMED = 3;
|
||||
public static final int SUBNODE_ADDED = 4;
|
||||
public static final int SUBNODE_REMOVED = 5;
|
||||
|
||||
public int type;
|
||||
public String id;
|
||||
public transient INode node;
|
||||
public transient Object arg;
|
||||
|
||||
public NodeEvent (INode node, int type) {
|
||||
super ();
|
||||
this.node = node;
|
||||
this.id = node.getID ();
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public NodeEvent (INode node, int type, Object arg) {
|
||||
super ();
|
||||
this.node = node;
|
||||
this.id = node.getID ();
|
||||
this.type = type;
|
||||
this.arg = arg;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
switch (type) {
|
||||
case CONTENT_CHANGED:
|
||||
return "NodeEvent: content changed";
|
||||
case PROPERTIES_CHANGED:
|
||||
return "NodeEvent: properties changed";
|
||||
case NODE_REMOVED:
|
||||
return "NodeEvent: node removed";
|
||||
case NODE_RENAMED:
|
||||
return "NodeEvent: node moved";
|
||||
case SUBNODE_ADDED:
|
||||
return "NodeEvent: subnode added";
|
||||
case SUBNODE_REMOVED:
|
||||
return "NodeEvent: subnode removed";
|
||||
}
|
||||
return "NodeEvent: invalid type";
|
||||
}
|
||||
|
||||
}
|
49
src/helma/objectmodel/ObjectNotFoundException.java
Normal file
49
src/helma/objectmodel/ObjectNotFoundException.java
Normal file
|
@ -0,0 +1,49 @@
|
|||
// ObjectNotFoundException.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
|
||||
/**
|
||||
* Thrown when an object could not found in the database where
|
||||
* it was expected.
|
||||
*/
|
||||
|
||||
public class ObjectNotFoundException extends Exception {
|
||||
|
||||
public ObjectNotFoundException (String msg) {
|
||||
super (msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
332
src/helma/objectmodel/Property.java
Normal file
332
src/helma/objectmodel/Property.java
Normal file
|
@ -0,0 +1,332 @@
|
|||
// Property.java
|
||||
// Copyright (c) Hannes Wallnöfer 1997-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
import helma.util.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
|
||||
/**
|
||||
* A property implementation for Nodes stored inside a database.
|
||||
*/
|
||||
|
||||
public final class Property implements IProperty, Serializable, Cloneable {
|
||||
|
||||
|
||||
protected String propname;
|
||||
protected Node node;
|
||||
|
||||
public String svalue;
|
||||
public boolean bvalue;
|
||||
public long lvalue;
|
||||
public double dvalue;
|
||||
public INode nvalue;
|
||||
public Object jvalue;
|
||||
|
||||
public int type;
|
||||
|
||||
|
||||
public Property (Node node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public Property (String propname, Node node) {
|
||||
this.propname = propname;
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public String getName () {
|
||||
return propname;
|
||||
}
|
||||
|
||||
public Object getValue () {
|
||||
switch (type) {
|
||||
case STRING:
|
||||
return svalue;
|
||||
case BOOLEAN:
|
||||
return new Boolean (bvalue);
|
||||
case INTEGER:
|
||||
return new Long (lvalue);
|
||||
case FLOAT:
|
||||
return new Double (dvalue);
|
||||
case DATE:
|
||||
return new Date (lvalue);
|
||||
case NODE:
|
||||
return nvalue;
|
||||
case JAVAOBJECT:
|
||||
return jvalue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setStringValue (String value) throws ParseException {
|
||||
if (type == NODE)
|
||||
unregisterNode ();
|
||||
// IServer.getLogger().log ("setting string value of property "+propname + " to "+value);
|
||||
if (type == DATE) {
|
||||
SimpleDateFormat dateformat = new SimpleDateFormat ();
|
||||
dateformat.setLenient (true);
|
||||
Date date = dateformat.parse (value);
|
||||
this.lvalue = date.getTime ();
|
||||
return;
|
||||
}
|
||||
if (type == BOOLEAN) {
|
||||
if ("true".equalsIgnoreCase (value))
|
||||
this.bvalue = true;
|
||||
else if ("false".equalsIgnoreCase (value))
|
||||
this.bvalue = false;
|
||||
return;
|
||||
}
|
||||
if (type == INTEGER) {
|
||||
this.lvalue = Long.parseLong (value);
|
||||
return;
|
||||
}
|
||||
if (type == FLOAT) {
|
||||
this.dvalue = new Double (value).doubleValue ();
|
||||
return;
|
||||
}
|
||||
if (type == JAVAOBJECT)
|
||||
this.jvalue = null;
|
||||
this.svalue = value;
|
||||
type = STRING;
|
||||
}
|
||||
|
||||
public void setIntegerValue (long value) {
|
||||
if (type == NODE)
|
||||
unregisterNode ();
|
||||
if (type == JAVAOBJECT)
|
||||
this.jvalue = null;
|
||||
type = INTEGER;
|
||||
this.lvalue = value;
|
||||
}
|
||||
|
||||
public void setFloatValue (double value) {
|
||||
if (type == NODE)
|
||||
unregisterNode ();
|
||||
if (type == JAVAOBJECT)
|
||||
this.jvalue = null;
|
||||
type = FLOAT;
|
||||
this.dvalue = value;
|
||||
}
|
||||
|
||||
public void setDateValue (Date value) {
|
||||
if (type == NODE)
|
||||
unregisterNode ();
|
||||
if (type == JAVAOBJECT)
|
||||
this.jvalue = null;
|
||||
type = DATE;
|
||||
this.lvalue = value.getTime();
|
||||
}
|
||||
|
||||
public void setBooleanValue (boolean value) {
|
||||
if (type == NODE)
|
||||
unregisterNode ();
|
||||
if (type == JAVAOBJECT)
|
||||
this.jvalue = null;
|
||||
type = BOOLEAN;
|
||||
this.bvalue = value;
|
||||
}
|
||||
|
||||
public void setNodeValue (INode value) {
|
||||
if (type == JAVAOBJECT)
|
||||
this.jvalue = null;
|
||||
if (type == NODE && nvalue != value) {
|
||||
unregisterNode ();
|
||||
registerNode (value);
|
||||
} else if (type != NODE)
|
||||
registerNode (value);
|
||||
type = NODE;
|
||||
this.nvalue = value;
|
||||
}
|
||||
|
||||
public void setJavaObjectValue (Object value) {
|
||||
if (type == NODE)
|
||||
unregisterNode ();
|
||||
type = JAVAOBJECT;
|
||||
this.jvalue = value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tell a the value node that it is no longer used as a property.
|
||||
*/
|
||||
protected void unregisterNode () {
|
||||
if (nvalue != null && nvalue instanceof Node) {
|
||||
Node n = (Node) nvalue;
|
||||
if (!n.anonymous && propname.equals (n.getName()) && this.node == n.getParent()) {
|
||||
// this is the "main" property of a named node, so handle this as a total delete.
|
||||
IServer.getLogger().log ("deleting named property");
|
||||
if (n.proplinks != null) {
|
||||
for (Enumeration e = n.proplinks.elements (); e.hasMoreElements (); ) {
|
||||
Property p = (Property) e.nextElement ();
|
||||
p.node.propMap.remove (p.propname.toLowerCase ());
|
||||
}
|
||||
}
|
||||
if (n.links != null) {
|
||||
for (Enumeration e = n.links.elements (); e.hasMoreElements (); ) {
|
||||
Node n2 = (Node) e.nextElement ();
|
||||
n2.releaseNode (n);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
n.unregisterPropLink (this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tell the value node that it is being used as a property value.
|
||||
*/
|
||||
protected void registerNode (INode n) {
|
||||
if (n != null && n instanceof Node)
|
||||
((Node) n).registerPropLink (this);
|
||||
}
|
||||
|
||||
public String getStringValue () {
|
||||
switch (type) {
|
||||
case STRING:
|
||||
return svalue;
|
||||
case BOOLEAN:
|
||||
return "" + bvalue;
|
||||
case DATE:
|
||||
SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy hh:mm");
|
||||
return format.format (new Date (lvalue));
|
||||
case INTEGER:
|
||||
return Long.toString (lvalue);
|
||||
case FLOAT:
|
||||
return Double.toString (dvalue);
|
||||
case NODE:
|
||||
return nvalue.getName ();
|
||||
case JAVAOBJECT:
|
||||
return jvalue.toString ();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return getStringValue ();
|
||||
}
|
||||
|
||||
public long getIntegerValue () {
|
||||
if (type == INTEGER)
|
||||
return lvalue;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public double getFloatValue () {
|
||||
if (type == FLOAT)
|
||||
return dvalue;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
public Date getDateValue () {
|
||||
if (type == DATE)
|
||||
return new Date (lvalue);
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean getBooleanValue () {
|
||||
if (type == BOOLEAN)
|
||||
return bvalue;
|
||||
return false;
|
||||
}
|
||||
|
||||
public INode getNodeValue () {
|
||||
if (type == NODE)
|
||||
return nvalue;
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getJavaObjectValue () {
|
||||
if (type == JAVAOBJECT)
|
||||
return jvalue;
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getEditor () {
|
||||
switch (type) {
|
||||
case STRING:
|
||||
return "password".equalsIgnoreCase (propname) ?
|
||||
"<input type=password name=\""+propname+"\" value='"+ svalue.replace ('\'', '"') +"'>" :
|
||||
"<input type=text name=\""+propname+"\" value='"+ svalue.replace ('\'', '"') +"'>" ;
|
||||
case BOOLEAN:
|
||||
return "<select name=\""+propname+"\"><option selected value="+bvalue+">"+bvalue+"</option><option value="+!bvalue+">"+!bvalue+"</option></select>";
|
||||
case INTEGER:
|
||||
return "<input type=text name=\""+propname+"\" value=\""+lvalue+"\">" ;
|
||||
case FLOAT:
|
||||
return "<input type=text name=\""+propname+"\" value=\""+dvalue+"\">" ;
|
||||
case DATE:
|
||||
SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy hh:mm");
|
||||
String date = format.format (new Date (lvalue));
|
||||
return "<input type=text name=\""+propname+"\" value=\""+date+"\">";
|
||||
case NODE:
|
||||
return "<input type=text size=25 name="+propname+" value='"+ nvalue.getFullName () +"'>";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private String escape (String s) {
|
||||
char c[] = new char[s.length()];
|
||||
s.getChars (0, c.length, c, 0);
|
||||
StringBuffer b = new StringBuffer ();
|
||||
int copyfrom = 0;
|
||||
for (int i = 0; i < c.length; i++) {
|
||||
switch (c[i]) {
|
||||
case '\\':
|
||||
case '"':
|
||||
if (i-copyfrom > 0)
|
||||
b.append (c, copyfrom, i-copyfrom);
|
||||
b.append ('\\');
|
||||
b.append (c[i]);
|
||||
copyfrom = i+1;
|
||||
}
|
||||
}
|
||||
if (c.length-copyfrom > 0)
|
||||
b.append (c, copyfrom, c.length-copyfrom);
|
||||
return b.toString ();
|
||||
}
|
||||
|
||||
public int getType () {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getTypeString () {
|
||||
switch (type) {
|
||||
case STRING:
|
||||
return "string";
|
||||
case BOOLEAN:
|
||||
return "boolean";
|
||||
case DATE:
|
||||
return "date";
|
||||
case INTEGER:
|
||||
return "integer";
|
||||
case FLOAT:
|
||||
return "float";
|
||||
case NODE:
|
||||
return "node";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
public Object clone () {
|
||||
try {
|
||||
Property c = (Property) super.clone();
|
||||
c.propname = this.propname;
|
||||
c.svalue = this.svalue;
|
||||
c.bvalue = this.bvalue;
|
||||
c.lvalue = this.lvalue;
|
||||
c.dvalue = this.dvalue;
|
||||
c.type = this.type;
|
||||
return c;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// this shouldn't happen, since we are Cloneable
|
||||
throw new InternalError ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
308
src/helma/objectmodel/Relation.java
Normal file
308
src/helma/objectmodel/Relation.java
Normal file
|
@ -0,0 +1,308 @@
|
|||
// Relation.java
|
||||
// Copyright (c) Hannes Wallnöfer 1997-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
import helma.framework.core.Application;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* This describes how a property of a persistent Object is stored in a
|
||||
* relational database table. This can be either a scalar property (string, date, number etc.)
|
||||
* or a reference to one or more other objects.
|
||||
*/
|
||||
public class Relation {
|
||||
|
||||
// TODO: explain hop mapping types
|
||||
public final static int INVALID = -1;
|
||||
public final static int PRIMITIVE = 0;
|
||||
public final static int FORWARD = 1;
|
||||
public final static int BACKWARD = 2;
|
||||
public final static int DIRECT = 3;
|
||||
|
||||
public DbMapping home;
|
||||
public DbMapping other;
|
||||
public String propname;
|
||||
public String localField, remoteField;
|
||||
public int direction;
|
||||
|
||||
public boolean virtual;
|
||||
public boolean readonly;
|
||||
public boolean aggressiveLoading;
|
||||
public boolean aggressiveCaching;
|
||||
public boolean subnodesAreProperties;
|
||||
public String order;
|
||||
public String groupby;
|
||||
public String prototype;
|
||||
|
||||
Relation filter = null; // additional relation used to filter subnodes
|
||||
|
||||
/**
|
||||
* This constructor is used to directly construct a Relation, as opposed to reading it from a proerty file
|
||||
*/
|
||||
public Relation (DbMapping other, String localField, String remoteField, int direction, boolean subnodesAreProperties) {
|
||||
this.other = other;
|
||||
this.localField = localField;
|
||||
this.remoteField = remoteField;
|
||||
this.direction = direction;
|
||||
this.subnodesAreProperties = subnodesAreProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a relation entry from a line in a properties file.
|
||||
*/
|
||||
public Relation (String desc, String propname, DbMapping home, Properties props) {
|
||||
|
||||
this.home = home;
|
||||
this.propname = propname;
|
||||
other = null;
|
||||
Application app = home.getApplication ();
|
||||
boolean mountpoint = false;
|
||||
|
||||
if (desc == null || "".equals (desc.trim ())) {
|
||||
if (propname != null) {
|
||||
direction = PRIMITIVE;
|
||||
localField = propname;
|
||||
} else {
|
||||
direction = INVALID;
|
||||
localField = propname;
|
||||
}
|
||||
} else {
|
||||
desc = desc.trim ();
|
||||
String descLower = desc.toLowerCase ();
|
||||
if (descLower.startsWith ("[virtual]")) {
|
||||
desc = desc.substring (9).trim ();
|
||||
virtual = true;
|
||||
} else if (descLower.startsWith ("[collection]")) {
|
||||
desc = desc.substring (12).trim ();
|
||||
virtual = true;
|
||||
} else if (descLower.startsWith ("[mountpoint]")) {
|
||||
desc = desc.substring (12).trim ();
|
||||
virtual = true;
|
||||
mountpoint = true;
|
||||
} else {
|
||||
virtual = false;
|
||||
}
|
||||
if (descLower.startsWith ("[readonly]")) {
|
||||
desc = desc.substring (10).trim ();
|
||||
readonly = true;
|
||||
} else {
|
||||
readonly = false;
|
||||
}
|
||||
if (desc.indexOf ("<") > -1) {
|
||||
direction = BACKWARD;
|
||||
int lt = desc.indexOf ("<");
|
||||
int dot = desc.indexOf (".");
|
||||
String otherType = dot < 0 ? desc.substring (1).trim () : desc.substring (1, dot).trim ();
|
||||
other = app.getDbMapping (otherType);
|
||||
if (other == null)
|
||||
throw new RuntimeException ("DbMapping for "+otherType+" not found from "+home.typename);
|
||||
remoteField = dot < 0 ? null : desc.substring (dot+1).trim ();
|
||||
localField = lt < 0 ? null : desc.substring (0, lt).trim ();
|
||||
if (mountpoint) prototype = otherType;
|
||||
} else if (desc.indexOf (">") > -1) {
|
||||
direction = FORWARD;
|
||||
int bt = desc.indexOf (">");
|
||||
int dot = desc.indexOf (".");
|
||||
String otherType = dot > -1 ? desc.substring (bt+1, dot).trim () : desc.substring (bt+1).trim ();
|
||||
other = app.getDbMapping (otherType);
|
||||
if (other == null)
|
||||
throw new RuntimeException ("DbMapping for "+otherType+" not found from "+home.typename);
|
||||
localField = desc.substring (0, bt).trim ();
|
||||
remoteField = dot < 0 ? null : desc.substring (dot+1).trim ();
|
||||
if (mountpoint) prototype = otherType;
|
||||
} else if (desc.indexOf (".") > -1) {
|
||||
direction = DIRECT;
|
||||
int dot = desc.indexOf (".");
|
||||
String otherType = desc.substring (0, dot).trim ();
|
||||
other = app.getDbMapping (otherType);
|
||||
if (other == null)
|
||||
throw new RuntimeException ("DbMapping for "+otherType+" not found from "+home.typename);
|
||||
remoteField = desc.substring (dot+1).trim ();
|
||||
localField = null;
|
||||
if (mountpoint) prototype = otherType;
|
||||
} else {
|
||||
if (virtual) {
|
||||
direction = DIRECT;
|
||||
other = app.getDbMapping (desc);
|
||||
if (other == null)
|
||||
throw new RuntimeException ("DbMapping for "+desc+" not found from "+home.typename);
|
||||
remoteField = localField = null;
|
||||
if (mountpoint) prototype = desc;
|
||||
} else {
|
||||
direction = PRIMITIVE;
|
||||
localField = desc.trim ();
|
||||
}
|
||||
}
|
||||
}
|
||||
String loading = props.getProperty (propname+".loadmode");
|
||||
aggressiveLoading = loading != null && "aggressive".equalsIgnoreCase (loading.trim());
|
||||
String caching = props.getProperty (propname+".cachemode");
|
||||
aggressiveCaching = caching != null && "aggressive".equalsIgnoreCase (caching.trim());
|
||||
// get order property
|
||||
order = props.getProperty (propname+".order");
|
||||
if (order != null && order.trim().length() == 0) order = null;
|
||||
// get group by property
|
||||
groupby = props.getProperty (propname+".groupby");
|
||||
if (groupby != null && groupby.trim().length() == 0) groupby = null;
|
||||
// check if subnode condition should be applied for property relations
|
||||
if ("_properties".equalsIgnoreCase (propname) || virtual) {
|
||||
String subnodes2props = props.getProperty (propname+".aresubnodes");
|
||||
subnodesAreProperties = "true".equalsIgnoreCase (subnodes2props);
|
||||
if (virtual) {
|
||||
String subnodefilter = props.getProperty (propname+".subnoderelation");
|
||||
if (subnodefilter != null) {
|
||||
filter = new Relation (subnodefilter, propname+".subnoderelation", home, props);
|
||||
filter.groupby = groupby;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isReference () {
|
||||
return direction > PRIMITIVE;
|
||||
}
|
||||
|
||||
public boolean usesPrimaryKey () {
|
||||
if (remoteField == null || other == null)
|
||||
return false;
|
||||
return remoteField.equalsIgnoreCase (other.getIDField());
|
||||
}
|
||||
|
||||
public Relation getFilter () {
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a key string to cache a node with a specific value for this relation. If the
|
||||
* Relation uses the primary key return just the key value, otherwise include info on the
|
||||
* used column or even the base node to avoid collisions.
|
||||
*/
|
||||
public String getKeyID (INode home, String kval) {
|
||||
// if the column is not the primary key, we add the column name to the key
|
||||
if ((direction == DIRECT || direction == FORWARD) && !usesPrimaryKey ()) {
|
||||
// check if the subnode relation also has to be considered
|
||||
if (subnodesAreProperties)
|
||||
return "["+home.getID()+"]"+remoteField+"="+kval; // HACK
|
||||
else
|
||||
return remoteField+"="+kval;
|
||||
} else {
|
||||
return kval;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the local column name for this relation. Uses the home node's id as fallback if local field is not specified.
|
||||
*/
|
||||
public String getLocalField () {
|
||||
if (localField == null)
|
||||
return home.getIDField ();
|
||||
return localField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "remote" column name for this relation. Uses the remote node's id as fallback if the remote field is not specified.
|
||||
*/
|
||||
public String getRemoteField () {
|
||||
if (remoteField == null)
|
||||
return other.getIDField ();
|
||||
return remoteField;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a Relation that defines the subnodes of a virtual node.
|
||||
*/
|
||||
public Relation getVirtualSubnodeRelation () {
|
||||
if (!virtual)
|
||||
throw new RuntimeException ("getVirtualSubnodeRelation called on non-virtual relation");
|
||||
if (filter != null)
|
||||
return filter;
|
||||
return getVirtualPropertyRelation ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Relation that defines the properties of a virtual node.
|
||||
*/
|
||||
public Relation getVirtualPropertyRelation () {
|
||||
if (!virtual)
|
||||
throw new RuntimeException ("getVirtualPropertyRelation called on non-virtual relation");
|
||||
Relation vr = new Relation (other, localField, remoteField, direction, subnodesAreProperties);
|
||||
vr.groupby = groupby;
|
||||
return vr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Relation that defines the subnodes of a group-by node.
|
||||
*/
|
||||
public Relation getGroupbySubnodeRelation () {
|
||||
if (groupby == null)
|
||||
throw new RuntimeException ("getGroupbySubnodeRelation called on non-group-by relation");
|
||||
if (filter != null)
|
||||
return filter;
|
||||
return getGroupbyPropertyRelation ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Relation that defines the properties of a group-by node.
|
||||
*/
|
||||
public Relation getGroupbyPropertyRelation () {
|
||||
if (groupby == null)
|
||||
throw new RuntimeException ("getGroupbyPropertyRelation called on non-group-by relation");
|
||||
return new Relation (other, localField, remoteField, direction, subnodesAreProperties);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
133
src/helma/objectmodel/SystemProperties.java
Normal file
133
src/helma/objectmodel/SystemProperties.java
Normal file
|
@ -0,0 +1,133 @@
|
|||
// Property.java
|
||||
// Copyright (c) Hannes Wallnöfer 1997-2000
|
||||
|
||||
package helma.objectmodel;
|
||||
|
||||
import helma.util.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* A property dictionary that is updated from a property file each time the
|
||||
* file is modified. It is also case insensitive.
|
||||
*/
|
||||
|
||||
public final class SystemProperties extends Properties {
|
||||
|
||||
private Properties props; // wrapped properties
|
||||
private Properties newProps; // used while building up props
|
||||
private Properties defaultProps;
|
||||
private File file;
|
||||
private long lastread, lastcheck;
|
||||
|
||||
public SystemProperties (String filename) {
|
||||
this (filename, null);
|
||||
}
|
||||
|
||||
public SystemProperties (Properties defaultProps) {
|
||||
this (null, defaultProps);
|
||||
}
|
||||
|
||||
public SystemProperties (String filename, Properties defaultProps) {
|
||||
// IServer.getLogger().log ("building sysprops with file "+filename+" and node "+node);
|
||||
this.defaultProps = defaultProps;
|
||||
props = defaultProps == null ?
|
||||
new Properties () : new Properties (defaultProps);
|
||||
|
||||
if (filename != null) {
|
||||
file = new File (filename);
|
||||
checkFile ();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean wasModified () {
|
||||
return file != null && file.exists() && file.lastModified () > lastread;
|
||||
}
|
||||
|
||||
public long lastModified () {
|
||||
return file == null || !file.exists () ? 0 : file.lastModified ();
|
||||
}
|
||||
|
||||
|
||||
private synchronized void checkFile () {
|
||||
if (wasModified ()) {
|
||||
// IServer.getLogger().log ("Reading properties from file "+file);
|
||||
newProps = defaultProps == null ?
|
||||
new Properties () : new Properties (defaultProps);
|
||||
try {
|
||||
FileInputStream bpin = new FileInputStream (file);
|
||||
load (bpin);
|
||||
bpin.close ();
|
||||
} catch (Exception x) {
|
||||
IServer.getLogger().log ("Error reading properties from file "+file+": "+x);
|
||||
}
|
||||
lastread = System.currentTimeMillis ();
|
||||
props = newProps;
|
||||
newProps = null;
|
||||
}
|
||||
lastcheck = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
/*
|
||||
* This should not be used directly if properties are read from file,
|
||||
* otherwise changes will be lost whe the file is next modified.
|
||||
*/
|
||||
public Object put (Object key, Object value) {
|
||||
if (newProps == null)
|
||||
return props.put (key.toString().toLowerCase(), value);
|
||||
else
|
||||
return newProps.put (key.toString().toLowerCase(), value);
|
||||
}
|
||||
|
||||
public Object get (Object key) {
|
||||
return props.get (key);
|
||||
}
|
||||
|
||||
public Object remove (Object key) {
|
||||
return props.remove (key);
|
||||
}
|
||||
|
||||
public void clear () {
|
||||
props.clear ();
|
||||
}
|
||||
|
||||
public boolean contains (Object obj) {
|
||||
return props.contains (obj);
|
||||
}
|
||||
|
||||
public boolean containsKey (Object key) {
|
||||
return props.containsKey (key);
|
||||
}
|
||||
|
||||
public boolean isEmpty () {
|
||||
return props.isEmpty ();
|
||||
}
|
||||
|
||||
public String getProperty (String name) {
|
||||
if (System.currentTimeMillis () - lastcheck > 1500l)
|
||||
checkFile ();
|
||||
return props.getProperty (name.toLowerCase());
|
||||
}
|
||||
|
||||
public String getProperty (String name, String defaultValue) {
|
||||
if (System.currentTimeMillis () - lastcheck > 1500l)
|
||||
checkFile ();
|
||||
return props.getProperty (name.toLowerCase(), defaultValue);
|
||||
}
|
||||
|
||||
public Enumeration keys () {
|
||||
if (System.currentTimeMillis () - lastcheck > 1500l)
|
||||
checkFile ();
|
||||
return props.keys();
|
||||
}
|
||||
|
||||
public Enumeration elements () {
|
||||
if (System.currentTimeMillis () - lastcheck > 1500l)
|
||||
checkFile ();
|
||||
return props.elements();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
139
src/helma/objectmodel/db/ApplicationManager.java
Normal file
139
src/helma/objectmodel/db/ApplicationManager.java
Normal file
|
@ -0,0 +1,139 @@
|
|||
// ApplicationManager.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.rmi.*;
|
||||
import java.rmi.server.*;
|
||||
import helma.framework.*;
|
||||
import helma.framework.core.*;
|
||||
import helma.objectmodel.*;
|
||||
import helma.servlet.*;
|
||||
import Acme.Serve.*;
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
|
||||
/**
|
||||
* This class is responsible for starting and stopping HOP applications.
|
||||
*/
|
||||
|
||||
public class ApplicationManager {
|
||||
|
||||
private Hashtable applications;
|
||||
private int port;
|
||||
private File appHome, dbHome;
|
||||
private SystemProperties props;
|
||||
private Server server;
|
||||
private long lastModified;
|
||||
|
||||
public ApplicationManager (int port, File appHome, File dbHome, SystemProperties props, Server server) {
|
||||
this.port = port;
|
||||
this.appHome = appHome;
|
||||
this.dbHome = dbHome;
|
||||
this.props = props;
|
||||
this.server = server;
|
||||
applications = new Hashtable ();
|
||||
lastModified = 0;
|
||||
}
|
||||
|
||||
|
||||
// regularely check applications property file to create and start new applications
|
||||
protected void checkForChanges () {
|
||||
if (props.lastModified () > lastModified) {
|
||||
try {
|
||||
for (Enumeration e = props.keys(); e.hasMoreElements (); ) {
|
||||
String appName = (String) e.nextElement ();
|
||||
if (applications.get (appName) == null) {
|
||||
start (appName);
|
||||
register (appName);
|
||||
}
|
||||
}
|
||||
// then stop deleted ones
|
||||
for (Enumeration e = applications.keys(); e.hasMoreElements (); ) {
|
||||
String appName = (String) e.nextElement ();
|
||||
if (!props.containsKey (appName)) {
|
||||
stop (appName);
|
||||
}
|
||||
}
|
||||
} catch (Exception mx) {
|
||||
IServer.getLogger().log ("Error starting applications: "+mx);
|
||||
}
|
||||
|
||||
lastModified = System.currentTimeMillis ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void start (String appName) {
|
||||
IServer.getLogger().log ("Building application "+appName);
|
||||
try {
|
||||
Application app = new Application (appName, dbHome, appHome);
|
||||
applications.put (appName, app);
|
||||
app.start ();
|
||||
} catch (Exception x) {
|
||||
IServer.getLogger().log ("Error creating application "+appName+": "+x);
|
||||
}
|
||||
}
|
||||
|
||||
private void stop (String appName) {
|
||||
IServer.getLogger().log ("Stopping application "+appName);
|
||||
try {
|
||||
Application app = (Application) applications.get (appName);
|
||||
if (server.websrv == null) {
|
||||
Naming.unbind ("//:"+port+"/"+appName);
|
||||
} else {
|
||||
server.websrv.removeServlet ("/"+appName+"/");
|
||||
server.websrv.removeServlet ("/"+appName+"/*");
|
||||
}
|
||||
app.stop ();
|
||||
IServer.getLogger().log ("Unregistered application "+appName);
|
||||
} catch (Exception x) {
|
||||
IServer.getLogger().log ("Couldn't unregister app: "+x);
|
||||
}
|
||||
applications.remove (appName);
|
||||
}
|
||||
|
||||
private void register (String appName) {
|
||||
try {
|
||||
IServer.getLogger().log ("Binding application "+appName);
|
||||
Application app = (Application) applications.get (appName);
|
||||
if (server.websrv == null) {
|
||||
Naming.rebind ("//:"+port+"/"+appName, app);
|
||||
} else {
|
||||
AcmeServletClient servlet = new AcmeServletClient (app);
|
||||
server.websrv.addServlet ("/"+appName+"/", servlet);
|
||||
server.websrv.addServlet ("/"+appName+"/*", servlet);
|
||||
}
|
||||
} catch (Exception x) {
|
||||
IServer.getLogger().log ("Couldn't register app: "+x);
|
||||
}
|
||||
}
|
||||
|
||||
public void startAll () {
|
||||
try {
|
||||
for (Enumeration e = props.keys(); e.hasMoreElements (); ) {
|
||||
String appName = (String) e.nextElement ();
|
||||
start (appName);
|
||||
}
|
||||
for (Enumeration e = props.keys(); e.hasMoreElements (); ) {
|
||||
String appName = (String) e.nextElement ();
|
||||
register (appName);
|
||||
}
|
||||
if (server.websrv != null) {
|
||||
File staticContent = new File (server.getHopHome(), "static");
|
||||
IServer.getLogger().log("Serving static content from "+staticContent.getAbsolutePath());
|
||||
AcmeFileServlet fsrv = new AcmeFileServlet (staticContent);
|
||||
server.websrv.addServlet ("/static/", fsrv);
|
||||
server.websrv.addServlet ("/static/*", fsrv);
|
||||
}
|
||||
lastModified = System.currentTimeMillis ();
|
||||
} catch (Exception mx) {
|
||||
IServer.getLogger().log ("Error starting applications: "+mx);
|
||||
mx.printStackTrace ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
281
src/helma/objectmodel/db/DbWrapper.java
Normal file
281
src/helma/objectmodel/db/DbWrapper.java
Normal file
|
@ -0,0 +1,281 @@
|
|||
// DbWrapper.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import com.sleepycat.db.*;
|
||||
import helma.objectmodel.ObjectNotFoundException;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* A wrapper around a Berkeley embedded database. Used to gracefully handle the case
|
||||
* when the native library can not be loaded.
|
||||
*/
|
||||
|
||||
public class DbWrapper {
|
||||
|
||||
private boolean loaded, useTransactions;
|
||||
|
||||
private Db db;
|
||||
DbEnv dbenv;
|
||||
final int checkpointPause = 600000; // min. 10 minutes between checkpoints
|
||||
volatile long lastCheckpoint = 0;
|
||||
volatile long txncount=0;
|
||||
|
||||
private File dbBaseDir;
|
||||
|
||||
public DbWrapper (String dbHome, String dbFilename, boolean useTx) throws DbException {
|
||||
try {
|
||||
dbBaseDir = new File (dbHome);
|
||||
if (!dbBaseDir.exists())
|
||||
dbBaseDir.mkdirs();
|
||||
|
||||
useTransactions = useTx;
|
||||
|
||||
int dbInitFlags = Db.DB_CREATE | Db.DB_THREAD | Db.DB_INIT_MPOOL;
|
||||
if (useTransactions) {
|
||||
dbInitFlags = dbInitFlags | Db.DB_INIT_TXN;
|
||||
}
|
||||
|
||||
dbenv = new DbEnv (0);
|
||||
try {
|
||||
dbenv.open (dbHome, dbInitFlags, 0); // for berkeley 3.0, add second parameter (null)
|
||||
} catch (FileNotFoundException fnf) {
|
||||
// we just created the dirs, so this shouldn't happen
|
||||
}
|
||||
|
||||
try {
|
||||
dbenv.set_error_stream(System.err);
|
||||
dbenv.set_errpfx("HOP: ");
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error in DbWrapper: "+e.toString());
|
||||
}
|
||||
|
||||
db = new Db (dbenv, 0);
|
||||
try {
|
||||
db.upgrade (dbFilename, 0);
|
||||
} catch (Exception ignore) {
|
||||
// nothing to upgrade, db doesn't exist
|
||||
}
|
||||
|
||||
try {
|
||||
db.open (dbFilename, null, Db.DB_BTREE, Db.DB_CREATE, 0644);
|
||||
} catch (FileNotFoundException fnf) {
|
||||
// we just created the dirs, so this shouldn't happen
|
||||
}
|
||||
loaded = true;
|
||||
|
||||
} catch (NoClassDefFoundError noclass) {
|
||||
Server.getLogger().log ("Warning: Using file based db as fallback. Reason: "+noclass);
|
||||
loaded = false;
|
||||
} catch (UnsatisfiedLinkError nolib) {
|
||||
Server.getLogger().log ("Warning: Using file based db as fallback. Reason: "+nolib);
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void shutdown () throws DbException {
|
||||
if (loaded) {
|
||||
db.close (0);
|
||||
dbenv.close (0);
|
||||
}
|
||||
}
|
||||
|
||||
public DbTxn beginTransaction () throws DbException {
|
||||
if (loaded && useTransactions)
|
||||
return dbenv.txn_begin (null, 0);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public void commitTransaction (DbTxn txn) throws DbException {
|
||||
if (txn == null || !loaded || !useTransactions)
|
||||
return;
|
||||
txn.commit (0);
|
||||
if (++txncount%100 == 0 && System.currentTimeMillis()-checkpointPause > lastCheckpoint) {
|
||||
// checkpoint transaction logs in time interval specified by server.checkpointPause
|
||||
// if there are more then 100 transactions to checkpoint.
|
||||
checkpoint ();
|
||||
}
|
||||
}
|
||||
|
||||
public void abortTransaction (DbTxn txn) throws DbException {
|
||||
if (txn == null || !loaded || !useTransactions)
|
||||
return;
|
||||
txn.abort ();
|
||||
}
|
||||
|
||||
protected void checkpoint () throws DbException {
|
||||
if (!loaded || !useTransactions || txncount == 0)
|
||||
return;
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastCheckpoint < checkpointPause)
|
||||
return;
|
||||
dbenv.txn_checkpoint (0, 0, 0); // for berkeley 3.0, remove third 0 parameter
|
||||
txncount = 0;
|
||||
lastCheckpoint = now;
|
||||
Server.getLogger().log ("Spent "+(System.currentTimeMillis()-now)+" in checkpoint");
|
||||
}
|
||||
|
||||
public IDGenerator getIDGenerator (DbTxn txn, String kstr) throws Exception {
|
||||
if (loaded)
|
||||
return getIDGenFromDB (txn, kstr);
|
||||
else
|
||||
return getIDGenFromFile (kstr);
|
||||
}
|
||||
|
||||
public Node getNode (DbTxn txn, String kstr) throws Exception {
|
||||
if (loaded)
|
||||
return getNodeFromDB (txn, kstr);
|
||||
else
|
||||
return getNodeFromFile (kstr);
|
||||
}
|
||||
|
||||
public void save (DbTxn txn, String kstr, Object obj) throws Exception {
|
||||
if (loaded)
|
||||
saveToDB (txn, kstr, obj);
|
||||
else
|
||||
saveToFile (kstr, obj);
|
||||
}
|
||||
|
||||
public void delete (DbTxn txn, String kstr) throws Exception {
|
||||
if (loaded)
|
||||
deleteFromDB (txn, kstr);
|
||||
else
|
||||
deleteFromFile (kstr);
|
||||
}
|
||||
|
||||
|
||||
private IDGenerator getIDGenFromDB (DbTxn txn, String kstr) throws Exception {
|
||||
long now = System.currentTimeMillis ();
|
||||
byte[] kbuf = kstr.getBytes ();
|
||||
Dbt key = new Dbt (kbuf);
|
||||
key.set_size (kbuf.length);
|
||||
Dbt data = new Dbt ();
|
||||
data.set_flags (Db.DB_DBT_MALLOC);
|
||||
|
||||
db.get (txn, key, data, 0);
|
||||
|
||||
byte[] b = data.get_data ();
|
||||
if (b == null)
|
||||
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
|
||||
|
||||
IDGenerator idgen = null;
|
||||
ByteArrayInputStream bin = new ByteArrayInputStream (b);
|
||||
ObjectInputStream oin = new ObjectInputStream (bin);
|
||||
idgen = (IDGenerator) oin.readObject ();
|
||||
|
||||
oin.close ();
|
||||
return idgen;
|
||||
}
|
||||
|
||||
|
||||
private Node getNodeFromDB (DbTxn txn, String kstr) throws Exception {
|
||||
long now = System.currentTimeMillis ();
|
||||
byte[] kbuf = kstr.getBytes ();
|
||||
Dbt key = new Dbt (kbuf);
|
||||
key.set_size (kbuf.length);
|
||||
Dbt data = new Dbt ();
|
||||
data.set_flags (Db.DB_DBT_MALLOC);
|
||||
|
||||
db.get (txn, key, data, 0);
|
||||
|
||||
byte[] b = data.get_data ();
|
||||
if (b == null)
|
||||
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
|
||||
|
||||
Node node = null;
|
||||
ByteArrayInputStream bin = new ByteArrayInputStream (b);
|
||||
ObjectInputStream oin = new ObjectInputStream (bin);
|
||||
node = (Node) oin.readObject ();
|
||||
oin.close ();
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
private void saveToDB (DbTxn txn, String kstr, Object obj) throws Exception {
|
||||
long now = System.currentTimeMillis ();
|
||||
byte kbuf[] = kstr.getBytes();
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream ();
|
||||
ObjectOutputStream oout = new ObjectOutputStream (bout);
|
||||
oout.writeObject (obj);
|
||||
oout.close ();
|
||||
byte vbuf[] = bout.toByteArray ();
|
||||
|
||||
Dbt key = new Dbt (kbuf);
|
||||
key.set_size (kbuf.length);
|
||||
Dbt value = new Dbt (vbuf);
|
||||
value.set_size (vbuf.length);
|
||||
|
||||
db.put (txn, key, value, 0);
|
||||
// IServer.getLogger().log ("saved "+obj+", size = "+vbuf.length);
|
||||
}
|
||||
|
||||
private void deleteFromDB (DbTxn txn, String kstr) throws Exception {
|
||||
|
||||
byte kbuf[] = kstr.getBytes();
|
||||
|
||||
Dbt key = new Dbt (kbuf);
|
||||
key.set_size (kbuf.length);
|
||||
|
||||
db.del (txn, key, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// File based fallback methods
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private IDGenerator getIDGenFromFile (String kstr) throws Exception {
|
||||
|
||||
File f = new File (dbBaseDir, kstr);
|
||||
|
||||
if ( ! f.exists() )
|
||||
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
|
||||
|
||||
IDGenerator idgen = null;
|
||||
FileInputStream bin = new FileInputStream (f);
|
||||
ObjectInputStream oin = new ObjectInputStream (bin);
|
||||
idgen = (IDGenerator) oin.readObject ();
|
||||
|
||||
oin.close ();
|
||||
return idgen;
|
||||
}
|
||||
|
||||
|
||||
private Node getNodeFromFile (String kstr) throws Exception {
|
||||
|
||||
File f = new File (dbBaseDir, kstr);
|
||||
|
||||
if ( ! f.exists() )
|
||||
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
|
||||
|
||||
Node node = null;
|
||||
FileInputStream bin = new FileInputStream (f);
|
||||
ObjectInputStream oin = new ObjectInputStream (bin);
|
||||
node = (Node) oin.readObject ();
|
||||
oin.close ();
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
private void saveToFile (String kstr, Object obj) throws Exception {
|
||||
|
||||
File f = new File (dbBaseDir, kstr);
|
||||
|
||||
FileOutputStream bout = new FileOutputStream (f);
|
||||
ObjectOutputStream oout = new ObjectOutputStream (bout);
|
||||
oout.writeObject (obj);
|
||||
oout.close ();
|
||||
}
|
||||
|
||||
private void deleteFromFile (String kstr) throws Exception {
|
||||
|
||||
File f = new File (dbBaseDir, kstr);
|
||||
f.delete();
|
||||
}
|
||||
|
||||
|
||||
}
|
39
src/helma/objectmodel/db/ExternalizableVector.java
Normal file
39
src/helma/objectmodel/db/ExternalizableVector.java
Normal file
|
@ -0,0 +1,39 @@
|
|||
// ExternalizableVector.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A subclass of Vector that implements the Externalizable interface in order
|
||||
* to be able to control how it is serialized and deserialized.
|
||||
*/
|
||||
|
||||
public class ExternalizableVector extends Vector implements Externalizable {
|
||||
|
||||
static final long serialVersionUID = 2316243615310540423L;
|
||||
|
||||
public synchronized void readExternal (ObjectInput in) throws IOException {
|
||||
try {
|
||||
int size = in.readInt ();
|
||||
for (int i=0; i<size; i++)
|
||||
addElement (in.readObject ());
|
||||
} catch (ClassNotFoundException x) {
|
||||
throw new IOException (x.toString ());
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void writeExternal (ObjectOutput out) throws IOException {
|
||||
int size = size ();
|
||||
out.writeInt (size);
|
||||
for (int i=0; i<size; i++)
|
||||
out.writeObject (elementAt (i));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
43
src/helma/objectmodel/db/HopSocketFactory.java
Normal file
43
src/helma/objectmodel/db/HopSocketFactory.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
// HopSocketFactory.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import helma.objectmodel.IServer;
|
||||
import helma.util.*;
|
||||
import java.net.*;
|
||||
import java.rmi.server.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An RMI socket factory that has a "paranoid" option to filter clients.
|
||||
* We only do direct connections, no HTTP proxy stuff, since this is
|
||||
* server-to-server.
|
||||
*/
|
||||
|
||||
public class HopSocketFactory extends RMISocketFactory {
|
||||
|
||||
private InetAddressFilter filter;
|
||||
|
||||
public HopSocketFactory () {
|
||||
filter = new InetAddressFilter ();
|
||||
}
|
||||
|
||||
public void addAddress (String address) {
|
||||
try {
|
||||
filter.addAddress (address);
|
||||
} catch (IOException x) {
|
||||
IServer.getLogger().log ("Could not add "+address+" to Socket Filter: invalid address.");
|
||||
}
|
||||
}
|
||||
|
||||
public Socket createSocket(String host, int port) throws IOException {
|
||||
return new Socket (host, port);
|
||||
}
|
||||
|
||||
public ServerSocket createServerSocket(int port) throws IOException {
|
||||
return new ParanoidServerSocket (port, filter);
|
||||
}
|
||||
|
||||
}
|
73
src/helma/objectmodel/db/IDGenerator.java
Normal file
73
src/helma/objectmodel/db/IDGenerator.java
Normal file
|
@ -0,0 +1,73 @@
|
|||
// IDGenerator.java
|
||||
// Copyright (c) Hannes Wallnöfer 1997-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* An object that generates IDs (Strings) that are unique across the whole system.
|
||||
* It does this keeping a simple long value which is incremented for each new ID.
|
||||
* This is the key generation for nodes stored in the internal database, but it can
|
||||
* also be used for relational nodes if no other mechanism is available. (Sequences
|
||||
* in Oracle are supported, while auto-IDs are not, since the HOP has to know
|
||||
* the keys of new objects.)
|
||||
*/
|
||||
|
||||
public final class IDGenerator implements Serializable {
|
||||
|
||||
private long counter;
|
||||
transient volatile boolean dirty;
|
||||
|
||||
static final long serialVersionUID = 753408631669789263L;
|
||||
|
||||
|
||||
/**
|
||||
* Builds a new IDGenerator starting with 0.
|
||||
*/
|
||||
public IDGenerator () {
|
||||
this.counter = 0l;
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new IDGenerator starting with value.
|
||||
*/
|
||||
public IDGenerator (long value) {
|
||||
this.counter = value;
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delivers a unique id and increases counter by 1.
|
||||
*/
|
||||
public synchronized String newID () {
|
||||
counter += 1l;
|
||||
dirty = true;
|
||||
return Long.toString (counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the counter to a new value
|
||||
*/
|
||||
protected synchronized void setValue (long value) {
|
||||
counter = value;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current counter value
|
||||
*/
|
||||
protected long getValue () {
|
||||
return counter;
|
||||
}
|
||||
|
||||
|
||||
public String toString () {
|
||||
return "helma.objectmodel.db.IDGenerator[counter="+counter+",dirty="+dirty+"]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
1643
src/helma/objectmodel/db/Node.java
Normal file
1643
src/helma/objectmodel/db/Node.java
Normal file
File diff suppressed because it is too large
Load diff
959
src/helma/objectmodel/db/NodeManager.java
Normal file
959
src/helma/objectmodel/db/NodeManager.java
Normal file
|
@ -0,0 +1,959 @@
|
|||
// NodeManager.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Vector;
|
||||
import java.util.Properties;
|
||||
import Acme.LruHashtable;
|
||||
import helma.objectmodel.*;
|
||||
import helma.framework.core.Application;
|
||||
import com.sleepycat.db.*;
|
||||
import java.sql.*;
|
||||
import java.util.Vector;
|
||||
import java.util.Enumeration;
|
||||
import com.workingdogs.village.*;
|
||||
|
||||
/**
|
||||
* The NodeManager is responsible for fetching Nodes from the internal or
|
||||
* external data sources, caching them in a least-recently-used Hashtable,
|
||||
* and writing changes back to the databases.
|
||||
*/
|
||||
|
||||
public final class NodeManager {
|
||||
|
||||
private Application app;
|
||||
|
||||
private LruHashtable cache;
|
||||
|
||||
protected DbWrapper db;
|
||||
|
||||
protected IDGenerator idgen;
|
||||
|
||||
private long idBaseValue = 1l;
|
||||
|
||||
private boolean logSql;
|
||||
|
||||
// a wrapper that catches some Exceptions while accessing this NM
|
||||
public final WrappedNodeManager safe;
|
||||
|
||||
public NodeManager (Application app, String dbHome, Properties props) throws DbException {
|
||||
this.app = app;
|
||||
int cacheSize = Integer.parseInt (props.getProperty ("cachesize", "1000"));
|
||||
cache = new LruHashtable (cacheSize, 0.9f);
|
||||
IServer.getLogger().log ("set up node cache ("+cacheSize+")");
|
||||
|
||||
safe = new WrappedNodeManager (this);
|
||||
|
||||
// get the initial id generator value
|
||||
String idb = props.getProperty ("idBaseValue");
|
||||
if (idb != null) try {
|
||||
idBaseValue = Long.parseLong (idb);
|
||||
idBaseValue = Math.max (1l, idBaseValue); // 0 and 1 are reserved for root nodes
|
||||
} catch (NumberFormatException ignore) {}
|
||||
|
||||
db = new DbWrapper (dbHome, Server.dbFilename, Server.useTransactions);
|
||||
initDb ();
|
||||
|
||||
logSql = "true".equalsIgnoreCase(props.getProperty ("logsql"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to create the root node and id-generator, if they don't exist already.
|
||||
*/
|
||||
public void initDb () throws DbException {
|
||||
|
||||
DbTxn txn = null;
|
||||
try {
|
||||
txn = db.beginTransaction ();
|
||||
|
||||
try {
|
||||
idgen = db.getIDGenerator (txn, "idgen");
|
||||
if (idgen.getValue() < idBaseValue) {
|
||||
idgen.setValue (idBaseValue);
|
||||
db.save (txn, "idgen", idgen);
|
||||
}
|
||||
} catch (ObjectNotFoundException notfound) {
|
||||
// will start with idBaseValue+1
|
||||
idgen = new IDGenerator (idBaseValue);
|
||||
db.save (txn, "idgen", idgen);
|
||||
}
|
||||
|
||||
// check if we need to set the id generator to a base value
|
||||
|
||||
Node node = null;
|
||||
try {
|
||||
node = db.getNode (txn, "0");
|
||||
node.nmgr = safe;
|
||||
} catch (ObjectNotFoundException notfound) {
|
||||
node = new Node ("root", "0", "root", safe);
|
||||
db.save (txn, node.getID (), node);
|
||||
registerNode (node); // register node with nodemanager cache
|
||||
}
|
||||
|
||||
try {
|
||||
node = db.getNode (txn, "1");
|
||||
node.nmgr = safe;
|
||||
} catch (ObjectNotFoundException notfound) {
|
||||
node = new Node ("users", "1", null, safe);
|
||||
db.save (txn, node.getID (), node);
|
||||
registerNode (node); // register node with nodemanager cache
|
||||
}
|
||||
|
||||
db.commitTransaction (txn);
|
||||
} catch (Exception x) {
|
||||
System.err.println (">> "+x);
|
||||
x.printStackTrace ();
|
||||
try {
|
||||
db.abortTransaction (txn);
|
||||
} catch (Exception ignore) {}
|
||||
throw (new DbException ("Error initializing db"));
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown () throws DbException {
|
||||
db.shutdown ();
|
||||
this.cache = null;
|
||||
}
|
||||
|
||||
public void deleteNode (Node node) throws Exception {
|
||||
if (node != null) {
|
||||
String id = node.getID ();
|
||||
synchronized (this) {
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
node.setState (Node.INVALID);
|
||||
deleteNode (db, tx.txn, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Node getNode (String kstr, DbMapping dbmap) throws Exception {
|
||||
|
||||
if (kstr == null)
|
||||
return null;
|
||||
|
||||
Key key = Key.makeKey (dbmap, kstr);
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
// tx.timer.beginEvent ("getNode "+kstr);
|
||||
|
||||
// See if Transactor has already come across this node
|
||||
Node node = tx.getVisitedNode (key);
|
||||
|
||||
if (node != null && node.getState() != Node.INVALID) {
|
||||
// tx.timer.endEvent ("getNode "+kstr);
|
||||
return node;
|
||||
}
|
||||
|
||||
// try to get the node from the shared cache
|
||||
node = (Node) cache.get (key);
|
||||
if (node == null || node.getState() == Node.INVALID) {
|
||||
|
||||
// The requested node isn't in the shared cache. Synchronize with key to make sure only one
|
||||
// version is fetched from the database.
|
||||
synchronized (key) {
|
||||
|
||||
// check again because only in the synchronized section can we be sure that
|
||||
// another thread hasn't fetched the node in the meantime.
|
||||
node = (Node) cache.get (key);
|
||||
|
||||
if (node == null || node.getState() == Node.INVALID) {
|
||||
node = getNodeByKey (db, tx.txn, kstr, dbmap);
|
||||
if (node != null) {
|
||||
cache.put (node.getKey (), node);
|
||||
}
|
||||
}
|
||||
} // synchronized
|
||||
}
|
||||
|
||||
if (node != null)
|
||||
tx.visitCleanNode (node.getKey (), node);
|
||||
|
||||
// tx.timer.endEvent ("getNode "+kstr);
|
||||
return node;
|
||||
}
|
||||
|
||||
public Node getNode (Node home, String kstr, Relation rel) throws Exception {
|
||||
|
||||
if (kstr == null)
|
||||
return null;
|
||||
|
||||
Key key;
|
||||
// If what we want is a virtual node create a "synthetic" key
|
||||
if (rel.virtual /*&& home.getState() != INode.VIRTUAL */ || rel.groupby != null)
|
||||
key = home.getKey ().getVirtualKey (kstr);
|
||||
// if a key for a node from within the DB
|
||||
else
|
||||
key = Key.makeKey (rel.other, rel.getKeyID (home, kstr));
|
||||
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
// tx.timer.beginEvent ("getNode "+kstr);
|
||||
|
||||
// See if Transactor has already come across this node
|
||||
Node node = tx.getVisitedNode (key);
|
||||
|
||||
if (node != null && node.getState() != Node.INVALID) {
|
||||
// tx.timer.endEvent ("getNode "+kstr);
|
||||
return node;
|
||||
}
|
||||
|
||||
// try to get the node from the shared cache
|
||||
node = (Node) cache.get (key);
|
||||
if (node == null || node.getState() == Node.INVALID) {
|
||||
|
||||
// The requested node isn't in the shared cache. Synchronize with key to make sure only one
|
||||
// version is fetched from the database.
|
||||
synchronized (key) {
|
||||
|
||||
// check again because only in the synchronized section can we be sure that
|
||||
// another thread hasn't fetched the node in the meantime.
|
||||
node = (Node) cache.get (key);
|
||||
|
||||
if (node == null || node.getState() == Node.INVALID) {
|
||||
node = getNodeByRelation (db, tx.txn, home, kstr, rel);
|
||||
if (node != null) {
|
||||
Key primKey = node.getKey ();
|
||||
cache.put (primKey, node);
|
||||
if (!key.equals (primKey)) {
|
||||
// cache node with extra (non-primary but unique) key
|
||||
cache.put (key, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // synchronized
|
||||
}
|
||||
|
||||
if (node != null)
|
||||
tx.visitCleanNode (node.getKey (), node);
|
||||
|
||||
// tx.timer.endEvent ("getNode "+kstr);
|
||||
return node;
|
||||
}
|
||||
|
||||
public void registerNode (Node node) {
|
||||
cache.put (node.getKey (), node);
|
||||
}
|
||||
|
||||
|
||||
public void evictNode (Node node) {
|
||||
node.setState (INode.INVALID);
|
||||
cache.remove (node.getKey ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when a key stops being valid for a node.
|
||||
*/
|
||||
public void evictKey (Key key) {
|
||||
cache.remove (key);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// methods to do the actual db work
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
public void insertNode (DbWrapper db, DbTxn txn, Node node) throws Exception {
|
||||
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
// tx.timer.beginEvent ("insertNode "+node);
|
||||
|
||||
DbMapping dbm = node.getDbMapping ();
|
||||
|
||||
if (dbm == null || !dbm.isRelational ()) {
|
||||
db.save (txn, node.getID (), node);
|
||||
} else {
|
||||
IServer.getLogger().log ("inserting relational node: "+node.getID ());
|
||||
TableDataSet tds = null;
|
||||
try {
|
||||
tds = new TableDataSet (dbm.getConnection (), dbm.getSchema (), dbm.getKeyDef ());
|
||||
Record rec = tds.addRecord ();
|
||||
rec.setValue (dbm.getIDField (), node.getID ());
|
||||
|
||||
String nameField = dbm.getNameField ();
|
||||
if (nameField != null)
|
||||
rec.setValue (nameField, node.getName ());
|
||||
|
||||
for (Enumeration e=dbm.prop2db.keys(); e.hasMoreElements(); ) {
|
||||
String propname = (String) e.nextElement ();
|
||||
Property p = node.getProperty (propname, false);
|
||||
Relation rel = dbm.propertyToColumnName (propname);
|
||||
|
||||
if (p != null && rel != null) {
|
||||
switch (p.getType ()) {
|
||||
case IProperty.STRING:
|
||||
rec.setValue (rel.localField, p.getStringValue ());
|
||||
break;
|
||||
case IProperty.BOOLEAN:
|
||||
rec.setValue (rel.localField, p.getBooleanValue ());
|
||||
break;
|
||||
case IProperty.DATE:
|
||||
Timestamp t = new Timestamp (p.getDateValue ().getTime ());
|
||||
rec.setValue (rel.localField, t);
|
||||
break;
|
||||
case IProperty.INTEGER:
|
||||
rec.setValue (rel.localField, p.getIntegerValue ());
|
||||
break;
|
||||
case IProperty.FLOAT:
|
||||
rec.setValue (rel.localField, p.getFloatValue ());
|
||||
break;
|
||||
case IProperty.NODE:
|
||||
if (rel.direction == Relation.FORWARD) {
|
||||
// INode n = p.getNodeValue ();
|
||||
// String foreignID = n == null ? null : n.getID ();
|
||||
rec.setValue (rel.localField, p.getStringValue ());
|
||||
}
|
||||
break;
|
||||
}
|
||||
p.dirty = false;
|
||||
} else if (rel != null && rel.localField != null) {
|
||||
rec.setValueNull (rel.localField);
|
||||
}
|
||||
}
|
||||
rec.markForInsert ();
|
||||
tds.save ();
|
||||
} finally {
|
||||
if (tds != null) {
|
||||
tds.close ();
|
||||
}
|
||||
}
|
||||
dbm.lastDataChange = System.currentTimeMillis ();
|
||||
}
|
||||
// tx.timer.endEvent ("insertNode "+node);
|
||||
}
|
||||
|
||||
public void updateNode (DbWrapper db, DbTxn txn, Node node) throws Exception {
|
||||
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
// tx.timer.beginEvent ("updateNode "+node);
|
||||
|
||||
DbMapping dbm = node.getDbMapping ();
|
||||
|
||||
if (dbm == null || !dbm.isRelational ()) {
|
||||
db.save (txn, node.getID (), node);
|
||||
} else {
|
||||
|
||||
TableDataSet tds = null;
|
||||
try {
|
||||
tds = new TableDataSet (dbm.getConnection (), dbm.getSchema (), dbm.getKeyDef ());
|
||||
Record rec = tds.addRecord ();
|
||||
rec.setValue (dbm.getIDField (), node.getID ());
|
||||
|
||||
int updated = 0;
|
||||
|
||||
for (Enumeration e=dbm.prop2db.keys(); e.hasMoreElements(); ) {
|
||||
String propname = (String) e.nextElement ();
|
||||
Property p = node.getProperty (propname, false);
|
||||
Relation rel = dbm.propertyToColumnName (propname);
|
||||
if (rel != null && rel.readonly)
|
||||
continue;
|
||||
|
||||
if (p != null && rel != null) {
|
||||
|
||||
if (p.dirty) {
|
||||
switch (p.getType ()) {
|
||||
case IProperty.STRING:
|
||||
rec.setValue (rel.localField, p.getStringValue ());
|
||||
break;
|
||||
case IProperty.BOOLEAN:
|
||||
rec.setValue (rel.localField, p.getBooleanValue ());
|
||||
break;
|
||||
case IProperty.DATE:
|
||||
Timestamp t = new Timestamp (p.getDateValue ().getTime ());
|
||||
rec.setValue (rel.localField, t);
|
||||
break;
|
||||
case IProperty.INTEGER:
|
||||
rec.setValue (rel.localField, p.getIntegerValue ());
|
||||
break;
|
||||
case IProperty.FLOAT:
|
||||
rec.setValue (rel.localField, p.getFloatValue ());
|
||||
break;
|
||||
case IProperty.NODE:
|
||||
if (rel.direction == Relation.FORWARD) {
|
||||
// INode n = p.getNodeValue ();
|
||||
// String foreignID = n == null ? null : n.getID ();
|
||||
rec.setValue (rel.localField, p.getStringValue ());
|
||||
}
|
||||
break;
|
||||
}
|
||||
updated++;
|
||||
p.dirty = false;
|
||||
}
|
||||
|
||||
} else if (rel != null && rel.localField != null) {
|
||||
updated++;
|
||||
rec.setValueNull (rel.localField);
|
||||
}
|
||||
}
|
||||
if (updated > 0) {
|
||||
// mark the key value as clean so no try is made to update it
|
||||
rec.markValueClean (dbm.getIDField ());
|
||||
rec.markForUpdate ();
|
||||
tds.save ();
|
||||
}
|
||||
} finally {
|
||||
if (tds != null) {
|
||||
tds.close ();
|
||||
}
|
||||
}
|
||||
dbm.lastDataChange = System.currentTimeMillis ();
|
||||
}
|
||||
// update may cause changes in the node's parent subnode array
|
||||
if (node.isAnonymous()) {
|
||||
Node parent = (Node) node.getParent ();
|
||||
if (parent != null)
|
||||
parent.lastSubnodeChange = System.currentTimeMillis ();
|
||||
}
|
||||
// tx.timer.endEvent ("updateNode "+node);
|
||||
}
|
||||
|
||||
public void deleteNode (DbWrapper db, DbTxn txn, Node node) throws Exception {
|
||||
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
// tx.timer.beginEvent ("deleteNode "+node);
|
||||
|
||||
DbMapping dbm = node.getDbMapping ();
|
||||
|
||||
if (dbm == null || !dbm.isRelational ()) {
|
||||
db.delete (txn, node.getID ());
|
||||
} else {
|
||||
Statement st = null;
|
||||
try {
|
||||
Connection con = dbm.getConnection ();
|
||||
st = con.createStatement ();
|
||||
st.executeUpdate ("DELETE FROM "+dbm.getTableName ()+" WHERE "+dbm.getIDField ()+" = "+node.getID ());
|
||||
} finally {
|
||||
if (st != null)
|
||||
st.close ();
|
||||
}
|
||||
dbm.lastDataChange = System.currentTimeMillis ();
|
||||
}
|
||||
// node may still be cached via non-primary keys. mark as invalid
|
||||
node.setState (Node.INVALID);
|
||||
// tx.timer.endEvent ("deleteNode "+node);
|
||||
}
|
||||
|
||||
public String generateID (DbMapping map) throws Exception {
|
||||
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
// tx.timer.beginEvent ("generateID "+map);
|
||||
|
||||
QueryDataSet qds = null;
|
||||
String retval = null;
|
||||
try {
|
||||
Connection con = map.getConnection ();
|
||||
String q = "SELECT "+map.getIDgen()+".nextval FROM dual";
|
||||
qds = new QueryDataSet (con, q);
|
||||
qds.fetchRecords ();
|
||||
retval = qds.getRecord (0).getValue (1).asString ();
|
||||
} finally {
|
||||
// tx.timer.endEvent ("generateID "+map);
|
||||
if (qds != null) {
|
||||
qds.close ();
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loades subnodes via subnode relation. Only the ID index is loaded, the nodes are
|
||||
* loaded later on demand.
|
||||
*/
|
||||
public Vector getNodeIDs (Node home, Relation rel) throws Exception {
|
||||
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
// tx.timer.beginEvent ("getNodeIDs "+home);
|
||||
|
||||
if (rel == null || rel.other == null || !rel.other.isRelational ()) {
|
||||
// this should never be called for embedded nodes
|
||||
throw new RuntimeException ("NodeMgr.countNodes called for non-relational node "+home);
|
||||
} else {
|
||||
Vector retval = new Vector ();
|
||||
// if we do a groupby query (creating an intermediate layer of groupby nodes),
|
||||
// retrieve the value of that field instead of the primary key
|
||||
String idfield = rel.groupby == null ? rel.other.getIDField () : rel.groupby;
|
||||
Connection con = rel.other.getConnection ();
|
||||
String table = rel.other.getTableName ();
|
||||
|
||||
QueryDataSet qds = null;
|
||||
try {
|
||||
Relation subrel = rel;
|
||||
if (subrel.getFilter () != null)
|
||||
subrel = subrel.getFilter ();
|
||||
|
||||
if (home.getSubnodeRelation() != null) {
|
||||
// subnode relation was explicitly set
|
||||
qds = new QueryDataSet (con, "SELECT "+idfield+" FROM "+table+" "+home.getSubnodeRelation());
|
||||
} else {
|
||||
String q = "SELECT "+idfield+" FROM "+table;
|
||||
if (subrel.direction == Relation.BACKWARD) {
|
||||
String homeid = home.getState() == Node.VIRTUAL ? home.parentID : home.getID ();
|
||||
q += " WHERE "+subrel.remoteField+" = '"+homeid+"'";
|
||||
}
|
||||
// set order, if specified and if not using subnode's relation
|
||||
if (rel.groupby != null)
|
||||
q += " GROUP BY "+rel.groupby+" ORDER BY "+rel.groupby;
|
||||
else if (rel.order != null)
|
||||
q += " ORDER BY "+rel.order;
|
||||
qds = new QueryDataSet (con, q);
|
||||
}
|
||||
|
||||
if (logSql)
|
||||
IServer.getLogger().log ("### getNodeIDs: "+qds.getSelectString());
|
||||
|
||||
qds.fetchRecords ();
|
||||
for (int i=0; i<qds.size (); i++) {
|
||||
Record rec = qds.getRecord (i);
|
||||
retval.addElement (rec.getValue (1).asString ());
|
||||
}
|
||||
|
||||
} finally {
|
||||
// tx.timer.endEvent ("getNodeIDs "+home);
|
||||
if (qds != null) {
|
||||
qds.close ();
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loades subnodes via subnode relation. This is similar to getNodeIDs, but it
|
||||
* actually loades all nodes in one go, which is better for small node collections.
|
||||
* This method is used when xxx.loadmode=aggressive is specified.
|
||||
*/
|
||||
public Vector getNodes (Node home, Relation rel) throws Exception {
|
||||
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
// tx.timer.beginEvent ("getNodes "+home);
|
||||
|
||||
if (rel == null || rel.other == null || !rel.other.isRelational ()) {
|
||||
// this should never be called for embedded nodes
|
||||
throw new RuntimeException ("NodeMgr.countNodes called for non-relational node "+home);
|
||||
} else {
|
||||
Vector retval = new Vector ();
|
||||
DbMapping dbm = rel.other;
|
||||
|
||||
TableDataSet tds = new TableDataSet (dbm.getConnection (), dbm.getSchema (), dbm.getKeyDef ());
|
||||
try {
|
||||
Relation subrel = rel;
|
||||
if (subrel.getFilter () != null)
|
||||
subrel = subrel.getFilter ();
|
||||
|
||||
if (home.getSubnodeRelation() != null) {
|
||||
// HACK: subnodeRelation includes a "where", but we need it without
|
||||
tds.where (home.getSubnodeRelation().trim().substring(5));
|
||||
} else if (subrel.direction == Relation.BACKWARD) {
|
||||
String homeid = home.getState() == Node.VIRTUAL ? home.parentID : home.getID ();
|
||||
tds.where (subrel.remoteField+" = '"+homeid+"'");
|
||||
// set order if specified
|
||||
if (rel.order != null)
|
||||
tds.order (rel.order);
|
||||
} else {
|
||||
// don't set where clause, but set order.
|
||||
if (rel.order != null)
|
||||
tds.order (rel.order);
|
||||
}
|
||||
|
||||
if (logSql)
|
||||
IServer.getLogger().log ("### getNodes: "+tds.getSelectString());
|
||||
|
||||
tds.fetchRecords ();
|
||||
for (int i=0; i<tds.size (); i++) {
|
||||
// create new Nodes.
|
||||
Record rec = tds.getRecord (i);
|
||||
Node node = new Node (rel.other, rec, safe);
|
||||
retval.addElement (node.getID());
|
||||
Key primKey = node.getKey ();
|
||||
// do we need to synchronize on primKey here?
|
||||
Node oldnode = (Node) cache.get (primKey);
|
||||
if (oldnode == null || oldnode.getState() == INode.INVALID)
|
||||
cache.put (primKey, node);
|
||||
}
|
||||
|
||||
} finally {
|
||||
// tx.timer.endEvent ("getNodes "+home);
|
||||
if (tds != null) {
|
||||
tds.close ();
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int countNodes (Node home, Relation rel) throws Exception {
|
||||
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
// tx.timer.beginEvent ("countNodes "+home);
|
||||
|
||||
if (rel == null || rel.other == null || !rel.other.isRelational ()) {
|
||||
// this should never be called for embedded nodes
|
||||
throw new RuntimeException ("NodeMgr.countNodes called for non-relational node "+home);
|
||||
} else {
|
||||
int retval = 0;
|
||||
Connection con = rel.other.getConnection ();
|
||||
String table = rel.other.getTableName ();
|
||||
|
||||
QueryDataSet qds = null;
|
||||
try {
|
||||
Relation subrel = rel;
|
||||
if (subrel.getFilter () != null)
|
||||
subrel = subrel.getFilter ();
|
||||
|
||||
if (home.getSubnodeRelation() != null) {
|
||||
qds = new QueryDataSet (con, "SELECT count(*) FROM "+table+" "+home.getSubnodeRelation());
|
||||
} else if (subrel.direction == Relation.BACKWARD) {
|
||||
String homeid = home.getState() == Node.VIRTUAL ? home.parentID : home.getID ();
|
||||
qds = new QueryDataSet (con, "SELECT count(*) FROM "+table+" WHERE "+subrel.remoteField+" = '"+homeid+"'");
|
||||
} else {
|
||||
qds = new QueryDataSet (con, "SELECT count(*) FROM "+table);
|
||||
}
|
||||
|
||||
if (logSql)
|
||||
IServer.getLogger().log ("### countNodes: "+qds.getSelectString());
|
||||
|
||||
qds.fetchRecords ();
|
||||
if (qds.size () == 0)
|
||||
retval = 0;
|
||||
else
|
||||
retval = qds.getRecord (0).getValue (1).asInt ();
|
||||
|
||||
} finally {
|
||||
// tx.timer.endEvent ("countNodes "+home);
|
||||
if (qds != null) {
|
||||
qds.close ();
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// private getNode methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private Node getNodeByKey (DbWrapper db, DbTxn txn, String kstr, DbMapping dbm) throws Exception {
|
||||
Node node = null;
|
||||
if (dbm == null || !dbm.isRelational ()) {
|
||||
node = db.getNode (txn, kstr);
|
||||
node.nmgr = safe;
|
||||
if (node != null && dbm != null)
|
||||
node.setDbMapping (dbm);
|
||||
} else {
|
||||
TableDataSet tds = null;
|
||||
try {
|
||||
tds = new TableDataSet (dbm.getConnection (), dbm.getSchema (), dbm.getKeyDef ());
|
||||
tds.where (dbm.getIDField ()+" = '"+kstr+"'");
|
||||
|
||||
if (logSql)
|
||||
IServer.getLogger().log ("### getNodeByKey: "+tds.getSelectString());
|
||||
|
||||
tds.fetchRecords ();
|
||||
|
||||
if (tds.size () == 0)
|
||||
return null;
|
||||
if (tds.size () > 1)
|
||||
throw new RuntimeException ("More than one value returned by query.");
|
||||
Record rec = tds.getRecord (0);
|
||||
node = new Node (dbm, rec, safe);
|
||||
|
||||
} finally {
|
||||
if (tds != null) {
|
||||
tds.close ();
|
||||
}
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private Node getNodeByRelation (DbWrapper db, DbTxn txn, Node home, String kstr, Relation rel) throws Exception {
|
||||
Node node = null;
|
||||
if (rel != null && rel.virtual && home.getState() != INode.VIRTUAL) {
|
||||
Key k = home.getKey ().getVirtualKey (kstr);
|
||||
node = (Node) cache.get (k);
|
||||
if (node != null && node.getState() != INode.INVALID) {
|
||||
if (rel.prototype != null && !rel.prototype.equals (node.getString ("prototype", false)))
|
||||
node.setString ("prototype", rel.prototype);
|
||||
return node;
|
||||
}
|
||||
// if subnodes are stored in embedded db we have to actually assign it the virtual node,
|
||||
// otherwise it and its subnodes will be lost across restarts.
|
||||
if (rel.other == null || (!rel.other.isRelational() && !home.getDbMapping().isRelational())) {
|
||||
node = (Node) home.createNode (rel.propname);
|
||||
if (rel.prototype != null)
|
||||
node.setString ("prototype", rel.prototype);
|
||||
} else {
|
||||
node = new Node (home, kstr, safe, rel.prototype);
|
||||
}
|
||||
if (rel.prototype != null) {
|
||||
node.setDbMapping (app.getDbMapping (rel.prototype));
|
||||
} else {
|
||||
// make a db mapping good enough that the virtual node finds its subnodes
|
||||
DbMapping dbm = new DbMapping ();
|
||||
dbm.setSubnodeMapping (rel.other);
|
||||
dbm.setSubnodeRelation (rel.getVirtualSubnodeRelation());
|
||||
dbm.setPropertyMapping (rel.other);
|
||||
dbm.setPropertyRelation (rel.getVirtualPropertyRelation());
|
||||
node.setDbMapping (dbm);
|
||||
}
|
||||
} else if (rel != null && rel.groupby != null) {
|
||||
node = new Node (home, kstr, safe, rel.prototype);
|
||||
DbMapping dbm = new DbMapping ();
|
||||
dbm.setSubnodeMapping (rel.other);
|
||||
dbm.setSubnodeRelation (rel.getGroupbySubnodeRelation());
|
||||
dbm.setPropertyMapping (rel.other);
|
||||
dbm.setPropertyRelation (rel.getGroupbyPropertyRelation());
|
||||
node.setDbMapping (dbm);
|
||||
} else if (rel == null || rel.other == null || !rel.other.isRelational ()) {
|
||||
node = db.getNode (txn, kstr);
|
||||
node.nmgr = safe;
|
||||
node.setDbMapping (rel.other);
|
||||
return node;
|
||||
} else {
|
||||
TableDataSet tds = null;
|
||||
try {
|
||||
DbMapping dbm = rel.other;
|
||||
tds = new TableDataSet (dbm.getConnection (), dbm.getSchema (), dbm.getKeyDef ());
|
||||
StringBuffer where = new StringBuffer ();
|
||||
|
||||
where.append (rel.getRemoteField ());
|
||||
where.append (" = '");
|
||||
where.append (escape(kstr));
|
||||
where.append ("'");
|
||||
|
||||
// Additionally filter properties through subnode relation?
|
||||
if (rel.subnodesAreProperties) {
|
||||
String homeid = home.getState() == Node.VIRTUAL ? home.parentID : home.getID ();
|
||||
// first check for dynamic subrel from node
|
||||
String nodesubrel = home.getSubnodeRelation();
|
||||
if (nodesubrel != null && nodesubrel.trim().length() > 5) {
|
||||
where.append (" and ");
|
||||
where.append (nodesubrel.trim().substring(5).trim());
|
||||
} else {
|
||||
Relation subrel = home.getDbMapping().getSubnodeRelation ();
|
||||
if (subrel != null) {
|
||||
if (subrel.getFilter () != null)
|
||||
subrel = subrel.getFilter ();
|
||||
if (subrel != null && subrel.direction == Relation.BACKWARD) {
|
||||
where.append (" and ");
|
||||
where.append (subrel.remoteField);
|
||||
where.append (" = '");
|
||||
where.append (homeid);
|
||||
where.append ("'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tds.where (where.toString ());
|
||||
|
||||
if (logSql)
|
||||
IServer.getLogger().log ("### getNodeByRelation: "+tds.getSelectString());
|
||||
|
||||
tds.fetchRecords ();
|
||||
|
||||
if (tds.size () == 0)
|
||||
return null;
|
||||
if (tds.size () > 1)
|
||||
throw new RuntimeException ("More than one value returned by query.");
|
||||
Record rec = tds.getRecord (0);
|
||||
node = new Node (rel.other, rec, safe);
|
||||
|
||||
// Check if node is already cached with primary Key.
|
||||
if (!rel.usesPrimaryKey()) {
|
||||
Key pk = node.getKey();
|
||||
Node existing = (Node) cache.get (pk);
|
||||
if (existing != null && existing.getState() != Node.INVALID) {
|
||||
node = existing;
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (tds != null) {
|
||||
tds.close ();
|
||||
}
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
// a utility method to escape single quotes
|
||||
private String escape (String str) {
|
||||
if (str == null)
|
||||
return null;
|
||||
if (str.indexOf ("'") < 0)
|
||||
return str;
|
||||
int l = str.length();
|
||||
StringBuffer sbuf = new StringBuffer (l + 10);
|
||||
for (int i=0; i<l; i++) {
|
||||
char c = str.charAt (i);
|
||||
if (c == '\'')
|
||||
sbuf.append ("\\");
|
||||
sbuf.append (c);
|
||||
}
|
||||
return sbuf.toString ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
561
src/helma/objectmodel/db/Property.java
Normal file
561
src/helma/objectmodel/db/Property.java
Normal file
|
@ -0,0 +1,561 @@
|
|||
// Property.java
|
||||
// Copyright (c) Hannes Wallnöfer 1997-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import helma.util.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
import helma.objectmodel.*;
|
||||
|
||||
/**
|
||||
* A property implementation for Nodes stored inside a database. Basically
|
||||
* the same as for transient nodes, with a few hooks added.
|
||||
*/
|
||||
public final class Property implements IProperty, Serializable, Cloneable {
|
||||
|
||||
|
||||
protected String propname;
|
||||
protected Node node;
|
||||
|
||||
protected String svalue;
|
||||
protected boolean bvalue;
|
||||
protected long lvalue;
|
||||
protected double dvalue;
|
||||
protected String nvalueID;
|
||||
private transient DbMapping dbm;
|
||||
protected Object jvalue;
|
||||
|
||||
protected int type;
|
||||
|
||||
transient boolean dirty;
|
||||
|
||||
static final long serialVersionUID = -1022221688349192379L;
|
||||
|
||||
private void readObject (ObjectInputStream in) throws IOException {
|
||||
try {
|
||||
propname = in.readUTF ();
|
||||
node = (Node) in.readObject ();
|
||||
type = in.readInt ();
|
||||
switch (type) {
|
||||
case STRING:
|
||||
svalue = in.readUTF ();
|
||||
break;
|
||||
case BOOLEAN:
|
||||
bvalue = in.readBoolean ();
|
||||
break;
|
||||
case INTEGER:
|
||||
case DATE:
|
||||
lvalue = in.readLong ();
|
||||
break;
|
||||
case FLOAT:
|
||||
dvalue = in.readDouble ();
|
||||
break;
|
||||
case NODE:
|
||||
nvalueID = in.readUTF ();
|
||||
break;
|
||||
case JAVAOBJECT:
|
||||
jvalue = in.readObject ();
|
||||
break;
|
||||
}
|
||||
} catch (ClassNotFoundException x) {
|
||||
throw new IOException (x.toString ());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeObject (ObjectOutputStream out) throws IOException {
|
||||
// don't even start if this is a non-serializable Java object
|
||||
if (type == JAVAOBJECT && jvalue != null && !(jvalue instanceof Serializable))
|
||||
return;
|
||||
out.writeUTF (propname);
|
||||
out.writeObject (node);
|
||||
out.writeInt (type);
|
||||
switch (type) {
|
||||
case STRING:
|
||||
out.writeUTF (svalue);
|
||||
break;
|
||||
case BOOLEAN:
|
||||
out.writeBoolean (bvalue);
|
||||
break;
|
||||
case INTEGER:
|
||||
case DATE:
|
||||
out.writeLong (lvalue);
|
||||
break;
|
||||
case FLOAT:
|
||||
out.writeDouble (dvalue);
|
||||
break;
|
||||
case NODE:
|
||||
out.writeUTF (nvalueID);
|
||||
break;
|
||||
case JAVAOBJECT:
|
||||
out.writeObject (jvalue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Property (Node node) {
|
||||
this.node = node;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public Property (String propname, Node node) {
|
||||
this.propname = propname;
|
||||
this.node = node;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public Property (String propname, Node node, Node value) {
|
||||
this (propname, node);
|
||||
type = NODE;
|
||||
nvalueID = value == null ? null : value.getID ();
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public String getName () {
|
||||
return propname;
|
||||
}
|
||||
|
||||
public Object getValue () {
|
||||
switch (type) {
|
||||
case STRING:
|
||||
return svalue;
|
||||
case BOOLEAN:
|
||||
return new Boolean (bvalue);
|
||||
case INTEGER:
|
||||
return new Long (lvalue);
|
||||
case FLOAT:
|
||||
return new Double (dvalue);
|
||||
case DATE:
|
||||
return new Date (lvalue);
|
||||
case NODE:
|
||||
return null;
|
||||
case JAVAOBJECT:
|
||||
return jvalue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setStringValue (String value) {
|
||||
if (type == NODE)
|
||||
unregisterNode ();
|
||||
// IServer.getLogger().log ("setting string value of property "+propname + " to "+value);
|
||||
// mark property as dirty
|
||||
dirty = true;
|
||||
// if this is not a string property, try to parse a value out of it
|
||||
if (type == DATE) {
|
||||
try {
|
||||
SimpleDateFormat dateformat = new SimpleDateFormat ();
|
||||
dateformat.setLenient (true);
|
||||
Date date = dateformat.parse (value);
|
||||
this.lvalue = date.getTime ();
|
||||
return;
|
||||
} catch (ParseException nodate) {
|
||||
// store as plain string
|
||||
}
|
||||
}
|
||||
if (type == BOOLEAN) {
|
||||
if ("true".equalsIgnoreCase (value))
|
||||
this.bvalue = true;
|
||||
else if ("false".equalsIgnoreCase (value))
|
||||
this.bvalue = false;
|
||||
return;
|
||||
}
|
||||
if (type == INTEGER) {
|
||||
this.lvalue = Long.parseLong (value);
|
||||
return;
|
||||
}
|
||||
if (type == FLOAT) {
|
||||
this.dvalue = new Double (value).doubleValue ();
|
||||
return;
|
||||
}
|
||||
if (type == JAVAOBJECT)
|
||||
this.jvalue = null;
|
||||
this.svalue = value;
|
||||
type = STRING;
|
||||
}
|
||||
|
||||
|
||||
public void setIntegerValue (long value) {
|
||||
if (type == NODE)
|
||||
unregisterNode ();
|
||||
if (type == JAVAOBJECT)
|
||||
this.jvalue = null;
|
||||
type = INTEGER;
|
||||
this.lvalue = value;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public void setFloatValue (double value) {
|
||||
if (type == NODE)
|
||||
unregisterNode ();
|
||||
if (type == JAVAOBJECT)
|
||||
this.jvalue = null;
|
||||
type = FLOAT;
|
||||
this.dvalue = value;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public void setDateValue (Date value) {
|
||||
if (type == NODE)
|
||||
unregisterNode ();
|
||||
if (type == JAVAOBJECT)
|
||||
this.jvalue = null;
|
||||
type = DATE;
|
||||
this.lvalue = value == null ? 0 : value.getTime();
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public void setBooleanValue (boolean value) {
|
||||
if (type == NODE)
|
||||
unregisterNode ();
|
||||
if (type == JAVAOBJECT)
|
||||
this.jvalue = null;
|
||||
type = BOOLEAN;
|
||||
this.bvalue = value;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public void setNodeValue (Node value) {
|
||||
value.checkWriteLock ();
|
||||
if (type == NODE)
|
||||
unregisterNode ();
|
||||
if (type == JAVAOBJECT)
|
||||
this.jvalue = null;
|
||||
registerNode (value);
|
||||
type = NODE;
|
||||
if (node.dbmap != null) {
|
||||
Relation rel = node.dbmap.getPropertyRelation (propname);
|
||||
if (rel != null && rel.other != null) {
|
||||
DbMapping vmap = value.getDbMapping ();
|
||||
// check if actual type matches expected type
|
||||
if (rel.other != vmap)
|
||||
throw new RuntimeException ("Can't assign property: expected prototype "+rel.other+", got "+vmap);
|
||||
// check if this is a forward relation, i.e. if we point to a field in the value object
|
||||
// if so, we may use something else than the object's id to refer to it.
|
||||
if (!rel.virtual && rel.direction == Relation.FORWARD) {
|
||||
if (rel.remoteField == null || vmap.getIDField().equals (rel.remoteField)) {
|
||||
this.nvalueID = value.getID ();
|
||||
} else try {
|
||||
this.nvalueID = value.getString (vmap.columnNameToProperty (rel.remoteField).propname, false);
|
||||
} catch (Exception x) {
|
||||
throw new RuntimeException ("Can't set "+propname+" to "+value+": error retrieving target property");
|
||||
}
|
||||
this.dbm = null;
|
||||
dirty = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.nvalueID = value == null ? null : value.getID ();
|
||||
this.dbm = value == null ? null : value.getDbMapping ();
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public void setJavaObjectValue (Object value) {
|
||||
if (type == NODE)
|
||||
unregisterNode ();
|
||||
type = JAVAOBJECT;
|
||||
this.jvalue = value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tell a the value node that it is no longer used as a property.
|
||||
* If this was the "main" property for the node, also remove all other references.
|
||||
*/
|
||||
protected void unregisterNode () {
|
||||
if (nvalueID != null) {
|
||||
DbMapping nvmap = null;
|
||||
Relation nvrel = null;
|
||||
if (node.dbmap != null) {
|
||||
nvmap = node.dbmap.getPropertyMapping (propname);
|
||||
nvrel = node.dbmap.getPropertyRelation (propname);
|
||||
}
|
||||
Node nvalue = node.nmgr.getNode (nvalueID, nvmap);
|
||||
if (nvalue == null)
|
||||
return;
|
||||
nvalue.checkWriteLock ();
|
||||
// check if the property node is also a subnode
|
||||
// BUG: this doesn't work because properties for subnode/properties are never stored and therefore
|
||||
// never reused.
|
||||
if (nvrel != null && nvrel.subnodesAreProperties) {
|
||||
node.removeNode (nvalue);
|
||||
}
|
||||
// only need to call unregisterPropLink if the value node is not stored in a relational db
|
||||
// also, getParent is heuristical/implicit for relational nodes, so we don't do deepRemoveNode
|
||||
// based on that for relational nodes.
|
||||
if (nvmap == null || !nvmap.isRelational()) {
|
||||
if (!nvalue.isAnonymous() && propname.equals (nvalue.getName()) && this.node == nvalue.getParent()) {
|
||||
// this is the "main" property of a named node, so handle this as a cascading delete.
|
||||
nvalue.deepRemoveNode ();
|
||||
} else {
|
||||
nvalue.unregisterPropLink (this.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tell the value node that it is being used as a property value.
|
||||
*/
|
||||
protected void registerNode (Node n) {
|
||||
// only need to call registerPropLink if the value node is not stored in a relational db
|
||||
if (n != null && (n.dbmap == null || !n.dbmap.isRelational())) {
|
||||
n.registerPropLink (this.node);
|
||||
}
|
||||
}
|
||||
|
||||
public String getStringValue () {
|
||||
switch (type) {
|
||||
case STRING:
|
||||
return svalue;
|
||||
case BOOLEAN:
|
||||
return "" + bvalue;
|
||||
case DATE:
|
||||
SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy hh:mm");
|
||||
return format.format (new Date (lvalue));
|
||||
case INTEGER:
|
||||
return Long.toString (lvalue);
|
||||
case FLOAT:
|
||||
return Double.toString (dvalue);
|
||||
case NODE:
|
||||
return nvalueID;
|
||||
case JAVAOBJECT:
|
||||
return jvalue.toString ();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return getStringValue ();
|
||||
}
|
||||
|
||||
public long getIntegerValue () {
|
||||
if (type == INTEGER)
|
||||
return lvalue;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public double getFloatValue () {
|
||||
if (type == FLOAT)
|
||||
return dvalue;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
public Date getDateValue () {
|
||||
if (type == DATE)
|
||||
return new Date (lvalue);
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean getBooleanValue () {
|
||||
if (type == BOOLEAN)
|
||||
return bvalue;
|
||||
return false;
|
||||
}
|
||||
|
||||
public INode getNodeValue () {
|
||||
if (type == NODE && nvalueID != null) {
|
||||
Relation rel = null;
|
||||
if (dbm == null && node.dbmap != null) {
|
||||
// try to get DbMap for property, if it isn't known yet
|
||||
rel = node.dbmap.getPropertyRelation (propname);
|
||||
// figure out db mapping from relation
|
||||
if (rel != null) {
|
||||
// is the property a virtual node containing objects from relational db?
|
||||
if (rel.virtual && rel.other.isRelational ())
|
||||
return node.nmgr.getNode (node, propname, rel);
|
||||
else if (!rel.virtual && rel.direction == Relation.FORWARD)
|
||||
return node.nmgr.getNode (node, nvalueID, rel);
|
||||
// avoid setting dbm for virtual relation
|
||||
else if (!rel.virtual /* || node.getState() == INode.VIRTUAL*/ && rel.groupby == null)
|
||||
dbm = rel.other;
|
||||
}
|
||||
}
|
||||
Node retval = node.nmgr.getNode (nvalueID, dbm);
|
||||
if (retval != null && retval.parentID == null) {
|
||||
retval.setParent (node);
|
||||
retval.setName (propname);
|
||||
retval.anonymous = false;
|
||||
}
|
||||
if (retval != null && retval.getDbMapping () == null && rel != null && rel.virtual && rel.prototype == null) {
|
||||
// a virtual node whose child nodes are not relational -
|
||||
// set up dbmapping that describes subnodes and properties
|
||||
DbMapping _dbm = new DbMapping ();
|
||||
_dbm.setSubnodeMapping (rel.other);
|
||||
_dbm.setPropertyMapping (rel.other);
|
||||
_dbm.setSubnodeRelation (rel.getVirtualSubnodeRelation());
|
||||
_dbm.setPropertyRelation (rel.getVirtualPropertyRelation());
|
||||
retval.setDbMapping (_dbm);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getJavaObjectValue () {
|
||||
if (type == JAVAOBJECT)
|
||||
return jvalue;
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getEditor () {
|
||||
switch (type) {
|
||||
case STRING:
|
||||
return "password".equalsIgnoreCase (propname) ?
|
||||
"<input type=password name=\""+propname+"\" value='"+ svalue.replace ('\'', '"') +"'>" :
|
||||
"<input type=text name=\""+propname+"\" value='"+ svalue.replace ('\'', '"') +"'>" ;
|
||||
case BOOLEAN:
|
||||
return "<select name=\""+propname+"\"><option selected value="+bvalue+">"+bvalue+"</option><option value="+!bvalue+">"+!bvalue+"</option></select>";
|
||||
case INTEGER:
|
||||
return "<input type=text name=\""+propname+"\" value=\""+lvalue+"\">" ;
|
||||
case FLOAT:
|
||||
return "<input type=text name=\""+propname+"\" value=\""+dvalue+"\">" ;
|
||||
case DATE:
|
||||
SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy hh:mm");
|
||||
String date = format.format (new Date (lvalue));
|
||||
return "<input type=text name=\""+propname+"\" value=\""+date+"\">";
|
||||
case NODE:
|
||||
DbMapping nvmap = null;
|
||||
if (node.dbmap != null)
|
||||
nvmap = node.dbmap.getPropertyMapping (propname);
|
||||
return "<input type=text size=25 name="+propname+" value='"+ node.nmgr.getNode (nvalueID, nvmap).getName () +"'>";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private String escape (String s) {
|
||||
char c[] = new char[s.length()];
|
||||
s.getChars (0, c.length, c, 0);
|
||||
StringBuffer b = new StringBuffer ();
|
||||
int copyfrom = 0;
|
||||
for (int i = 0; i < c.length; i++) {
|
||||
switch (c[i]) {
|
||||
case '\\':
|
||||
case '"':
|
||||
if (i-copyfrom > 0)
|
||||
b.append (c, copyfrom, i-copyfrom);
|
||||
b.append ('\\');
|
||||
b.append (c[i]);
|
||||
copyfrom = i+1;
|
||||
}
|
||||
}
|
||||
if (c.length-copyfrom > 0)
|
||||
b.append (c, copyfrom, c.length-copyfrom);
|
||||
return b.toString ();
|
||||
}
|
||||
|
||||
public int getType () {
|
||||
return type;
|
||||
|
||||
}
|
||||
|
||||
public String getTypeString () {
|
||||
switch (type) {
|
||||
case STRING:
|
||||
return "string";
|
||||
case BOOLEAN:
|
||||
return "boolean";
|
||||
case DATE:
|
||||
return "date";
|
||||
case INTEGER:
|
||||
return "integer";
|
||||
case FLOAT:
|
||||
return "float";
|
||||
case NODE:
|
||||
return "node";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
public Object clone () {
|
||||
try {
|
||||
Property c = (Property) super.clone();
|
||||
c.propname = this.propname;
|
||||
c.svalue = this.svalue;
|
||||
c.bvalue = this.bvalue;
|
||||
c.lvalue = this.lvalue;
|
||||
c.dvalue = this.dvalue;
|
||||
c.nvalueID = this.nvalueID;
|
||||
c.type = this.type;
|
||||
return c;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// this shouldn't happen, since we are Cloneable
|
||||
throw new InternalError ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
355
src/helma/objectmodel/db/Server.java
Normal file
355
src/helma/objectmodel/db/Server.java
Normal file
|
@ -0,0 +1,355 @@
|
|||
// Server.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import java.rmi.*;
|
||||
import java.rmi.server.*;
|
||||
import java.rmi.registry.*;
|
||||
import java.net.*;
|
||||
import helma.objectmodel.*;
|
||||
import helma.framework.*;
|
||||
import helma.framework.core.*;
|
||||
import helma.xmlrpc.*;
|
||||
import helma.util.*;
|
||||
import com.sleepycat.db.*;
|
||||
|
||||
|
||||
/**
|
||||
* HOP main class.
|
||||
*/
|
||||
|
||||
public class Server extends IServer implements Runnable {
|
||||
|
||||
|
||||
static boolean useTransactions, paranoid;
|
||||
|
||||
private ApplicationManager appManager;
|
||||
|
||||
private Thread mainThread;
|
||||
|
||||
protected static ThreadGroup txgroup;
|
||||
|
||||
static String dbFilename = "hop.db";
|
||||
static String propfile;
|
||||
static String dbPropfile = "db.properties";
|
||||
static String appsPropfile = "apps.properties";
|
||||
static SystemProperties appsProps;
|
||||
static String dbDir = null;
|
||||
static int port = 5055;
|
||||
static int webport = 0;
|
||||
|
||||
Acme.Serve.Serve websrv;
|
||||
|
||||
public static void main (String args[]) throws IOException {
|
||||
|
||||
boolean usageError = false;
|
||||
|
||||
useTransactions = true;
|
||||
|
||||
for (int i=0; i<args.length; i++) {
|
||||
if (args[i].equals ("-h") && i+1<args.length)
|
||||
hopHome = args[++i];
|
||||
else if (args[i].equals ("-f") && i+1<args.length)
|
||||
propfile = args[++i];
|
||||
else if (args[i].equals ("-t"))
|
||||
useTransactions = false;
|
||||
else if (args[i].equals ("-p") && i+1<args.length) {
|
||||
try {
|
||||
port = Integer.parseInt (args[++i]);
|
||||
} catch (Exception portx) {
|
||||
usageError = true;
|
||||
}
|
||||
} else if (args[i].equals ("-w") && i+1<args.length) {
|
||||
try {
|
||||
webport = Integer.parseInt (args[++i]);
|
||||
} catch (Exception portx) {
|
||||
usageError = true;
|
||||
}
|
||||
} else
|
||||
usageError = true;
|
||||
}
|
||||
|
||||
// get main property file from home dir or vice versa, depending on what we have.
|
||||
// get property file from hopHome
|
||||
if (propfile == null) {
|
||||
if (hopHome != null)
|
||||
propfile = new File (hopHome, "server.properties").getAbsolutePath ();
|
||||
else
|
||||
propfile = new File ("server.properties").getAbsolutePath ();
|
||||
}
|
||||
sysProps = new SystemProperties (propfile);
|
||||
getLogger().log ("propfile = "+propfile);
|
||||
// get hopHome from property file
|
||||
if (hopHome == null)
|
||||
hopHome = sysProps.getProperty ("hophome");
|
||||
if (hopHome == null)
|
||||
hopHome = new File (propfile).getParent ();
|
||||
|
||||
getLogger().log ("hopHome = "+hopHome);
|
||||
|
||||
if (usageError ) {
|
||||
System.out.println ("usage: java helma.objectmodel.db.Server [-h dir] [-f file] [-p port] [-w port] [-t]");
|
||||
System.out.println (" -h dir Specify hop home directory");
|
||||
System.out.println (" -f file Specify server.properties file");
|
||||
System.out.println (" -p port Specify TCP port number");
|
||||
System.out.println (" -w port Start embedded Web server on that port");
|
||||
System.out.println (" -t Disable Berkeley DB Transactions");
|
||||
getLogger().log ("Usage Error - exiting");
|
||||
System.exit (0);
|
||||
}
|
||||
|
||||
dbDir = sysProps.getProperty ("dbhome", "db");
|
||||
File helper = new File (dbDir);
|
||||
if (hopHome != null && !helper.isAbsolute ())
|
||||
helper = new File (hopHome, dbDir);
|
||||
dbDir = helper.getAbsolutePath ();
|
||||
getLogger().log ("dbHome = "+dbDir);
|
||||
|
||||
dbPropfile = sysProps.getProperty ("dbpropfile", "db.properties");
|
||||
helper = new File (dbPropfile);
|
||||
if (hopHome != null && !helper.isAbsolute ())
|
||||
helper = new File (hopHome, dbPropfile);
|
||||
dbPropfile = helper.getAbsolutePath ();
|
||||
getLogger().log ("dbPropfile = "+dbPropfile);
|
||||
|
||||
appsPropfile = sysProps.getProperty ("appspropfile", "apps.properties");
|
||||
helper = new File (appsPropfile);
|
||||
if (hopHome != null && !helper.isAbsolute ())
|
||||
helper = new File (hopHome, appsPropfile);
|
||||
appsPropfile = helper.getAbsolutePath ();
|
||||
getLogger().log ("appsPropfile = "+appsPropfile);
|
||||
|
||||
paranoid = "true".equalsIgnoreCase (sysProps.getProperty ("paranoid"));
|
||||
|
||||
String language = sysProps.getProperty ("language");
|
||||
String country = sysProps.getProperty ("country");
|
||||
String timezone = sysProps.getProperty ("timezone");
|
||||
|
||||
if (language != null && country != null)
|
||||
Locale.setDefault (new Locale (language, country));
|
||||
if (timezone != null)
|
||||
TimeZone.setDefault (TimeZone.getTimeZone (timezone));
|
||||
|
||||
getLogger().log ("Locale = "+Locale.getDefault());
|
||||
getLogger().log ("TimeZone = "+TimeZone.getDefault());
|
||||
|
||||
dbSources = new Hashtable ();
|
||||
txgroup = new ThreadGroup ("Transactor");
|
||||
|
||||
new Server ();
|
||||
|
||||
}
|
||||
|
||||
public Server () {
|
||||
|
||||
try {
|
||||
checkRunning (); // check if a server is already running with this db
|
||||
} catch (Exception running) {
|
||||
System.out.println (running.getMessage ());
|
||||
System.exit (1);
|
||||
}
|
||||
|
||||
// nmgr = new NodeManager (this, sysProps);
|
||||
|
||||
mainThread = new Thread (this);
|
||||
mainThread.start ();
|
||||
}
|
||||
|
||||
public void run () {
|
||||
|
||||
try {
|
||||
|
||||
// set up dbSources
|
||||
try {
|
||||
dbProps = new SystemProperties (dbPropfile);
|
||||
String sources = dbProps.getProperty ("sources", "");
|
||||
StringTokenizer st = new StringTokenizer (sources, ",; ");
|
||||
String next = null;
|
||||
while (st.hasMoreTokens ()) try {
|
||||
next = st.nextToken ();
|
||||
new DbSource (next);
|
||||
} catch (Exception wrong) {
|
||||
getLogger().log ("Error creating DbSource "+next);
|
||||
getLogger().log ("Reason: "+wrong);
|
||||
}
|
||||
} catch (Exception x) {
|
||||
getLogger().log ("Error loading data source properties: "+x);
|
||||
}
|
||||
|
||||
// start embedded web server if port is specified
|
||||
if (webport > 0) {
|
||||
websrv = new Acme.Serve.Serve (webport, sysProps);
|
||||
}
|
||||
|
||||
String xmlparser = sysProps.getProperty ("xmlparser");
|
||||
if (xmlparser != null)
|
||||
XmlRpc.setDriver (xmlparser);
|
||||
// XmlRpc.setDebug (true);
|
||||
xmlrpc = new WebServer (port+1);
|
||||
if (paranoid) {
|
||||
xmlrpc.setParanoid (true);
|
||||
String xallow = sysProps.getProperty ("allowXmlRpc");
|
||||
if (xallow != null) {
|
||||
StringTokenizer st = new StringTokenizer (xallow, " ,;");
|
||||
while (st.hasMoreTokens ())
|
||||
xmlrpc.acceptClient (st.nextToken ());
|
||||
}
|
||||
}
|
||||
getLogger().log ("Starting XML-RPC server on port "+(port+1));
|
||||
|
||||
// the following seems not to be necessary after all ...
|
||||
// System.setSecurityManager(new RMISecurityManager());
|
||||
if (paranoid) {
|
||||
HopSocketFactory factory = new HopSocketFactory ();
|
||||
String rallow = sysProps.getProperty ("allowWeb");
|
||||
if (rallow != null) {
|
||||
StringTokenizer st = new StringTokenizer (rallow, " ,;");
|
||||
while (st.hasMoreTokens ())
|
||||
factory.addAddress (st.nextToken ());
|
||||
}
|
||||
RMISocketFactory.setSocketFactory (factory);
|
||||
}
|
||||
|
||||
if (websrv == null) {
|
||||
getLogger().log ("Starting server on port "+port);
|
||||
LocateRegistry.createRegistry (port);
|
||||
}
|
||||
|
||||
|
||||
// start application framework
|
||||
String appDir = sysProps.getProperty ("apphome", "apps");
|
||||
File appHome = new File (appDir);
|
||||
if (hopHome != null && !appHome.isAbsolute())
|
||||
appHome = new File (hopHome, appDir);
|
||||
appsProps = new SystemProperties (appsPropfile);
|
||||
File dbHome = new File (dbDir);
|
||||
appManager = new ApplicationManager (port, appHome, dbHome, appsProps, this);
|
||||
|
||||
|
||||
} catch (Exception gx) {
|
||||
getLogger().log ("Error initializing embedded database: "+gx);
|
||||
gx.printStackTrace ();
|
||||
/* try {
|
||||
transactor.abort ();
|
||||
} catch (Exception ignore) {} */
|
||||
return;
|
||||
}
|
||||
|
||||
// start applications
|
||||
appManager.startAll ();
|
||||
|
||||
// start embedded web server
|
||||
if (websrv != null) {
|
||||
Thread webthread = new Thread (websrv, "WebServer");
|
||||
webthread.start ();
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
while (Thread.currentThread () == mainThread) {
|
||||
try {
|
||||
mainThread.sleep (3000l);
|
||||
} catch (InterruptedException ie) {
|
||||
return;
|
||||
}
|
||||
appManager.checkForChanges ();
|
||||
// print some thread stats now and then
|
||||
if (count++ > 20) {
|
||||
printThreadStats ();
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void checkRunning () throws Exception {
|
||||
try {
|
||||
java.net.Socket socket = new java.net.Socket ("localhost", port);
|
||||
} catch (Exception x) {
|
||||
return;
|
||||
}
|
||||
// if we got so far, another server is already running on this port and db
|
||||
throw new Exception ("Error: Server already running on this port");
|
||||
}
|
||||
|
||||
public void printThreadStats () {
|
||||
getLogger().log ("Thread Stats: "+txgroup.activeCount()+" active");
|
||||
Runtime rt = Runtime.getRuntime ();
|
||||
long free = rt.freeMemory ();
|
||||
long total = rt.totalMemory ();
|
||||
getLogger().log ("Free memory: "+(free/1024)+" kB");
|
||||
getLogger().log ("Total memory: "+(total/1024)+" kB");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
338
src/helma/objectmodel/db/Transactor.java
Normal file
338
src/helma/objectmodel/db/Transactor.java
Normal file
|
@ -0,0 +1,338 @@
|
|||
// Transactor.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.sql.*;
|
||||
import helma.objectmodel.*;
|
||||
import helma.util.Timer;
|
||||
import helma.framework.TimeoutException;
|
||||
import com.sleepycat.db.*;
|
||||
|
||||
/**
|
||||
* A subclass of thread that keeps track of changed nodes and triggers
|
||||
* changes in the database when a transaction is commited.
|
||||
*/
|
||||
|
||||
public class Transactor extends Thread {
|
||||
|
||||
NodeManager nmgr;
|
||||
|
||||
// List of nodes to be updated
|
||||
private Hashtable nodes;
|
||||
// List of visited clean nodes
|
||||
private Hashtable cleannodes;
|
||||
// Is a transaction in progress?
|
||||
private volatile boolean active;
|
||||
private volatile boolean killed;
|
||||
|
||||
// Transaction for the embedded database
|
||||
protected DbTxn txn;
|
||||
// Transactions for SQL data sources
|
||||
protected Hashtable sqlCon;
|
||||
|
||||
public Timer timer;
|
||||
// when did the current transaction start?
|
||||
private long tstart;
|
||||
// a name to log the transaction. For HTTP transactions this is the rerquest path
|
||||
private String tname;
|
||||
|
||||
|
||||
public Transactor (Runnable runnable, NodeManager nmgr) {
|
||||
super (Server.txgroup, runnable, "Transactor");
|
||||
this.nmgr = nmgr;
|
||||
nodes = new Hashtable ();
|
||||
cleannodes = new Hashtable ();
|
||||
sqlCon = new Hashtable ();
|
||||
active = false;
|
||||
killed = false;
|
||||
timer = new Timer();
|
||||
}
|
||||
|
||||
public void visitNode (Node node) {
|
||||
if (node != null) {
|
||||
Key key = node.getKey ();
|
||||
if (!nodes.containsKey (key)) {
|
||||
nodes.put (key, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dropNode (Node node) {
|
||||
if (node != null) {
|
||||
Key key = node.getKey ();
|
||||
nodes.remove (key);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitCleanNode (Node node) {
|
||||
if (node != null) {
|
||||
Key key = node.getKey ();
|
||||
if (!cleannodes.containsKey (key)) {
|
||||
cleannodes.put (key, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void visitCleanNode (Key key, Node node) {
|
||||
if (node != null) {
|
||||
if (!cleannodes.containsKey (key)) {
|
||||
cleannodes.put (key, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Node getVisitedNode (Object key) {
|
||||
return key == null ? null : (Node) cleannodes.get (key);
|
||||
}
|
||||
|
||||
public boolean isActive () {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void registerConnection (DbSource src, Connection con) {
|
||||
sqlCon.put (src, con);
|
||||
}
|
||||
|
||||
public Connection getConnection (DbSource src) {
|
||||
return (Connection) sqlCon.get (src);
|
||||
}
|
||||
|
||||
public synchronized void begin (String tnm) throws Exception {
|
||||
if (active) {
|
||||
abort ();
|
||||
}
|
||||
nodes.clear ();
|
||||
cleannodes.clear ();
|
||||
txn = nmgr.db.beginTransaction ();
|
||||
active = true;
|
||||
tstart = System.currentTimeMillis ();
|
||||
tname = tnm;
|
||||
}
|
||||
|
||||
public synchronized void commit () throws Exception {
|
||||
|
||||
if (killed) {
|
||||
abort ();
|
||||
return;
|
||||
}
|
||||
|
||||
int ins = 0, upd = 0, dlt = 0;
|
||||
int l = nodes.size ();
|
||||
|
||||
for (Enumeration e=nodes.elements (); e.hasMoreElements (); ) {
|
||||
Node node = (Node) e.nextElement ();
|
||||
|
||||
// update nodes in db
|
||||
int nstate = node.getState ();
|
||||
if (nstate == Node.NEW) {
|
||||
nmgr.registerNode (node); // register node with nodemanager cache
|
||||
nmgr.insertNode (nmgr.db, txn, node);
|
||||
node.setState (Node.CLEAN);
|
||||
ins++;
|
||||
// IServer.getLogger().log ("inserted: "+node.getFullName ());
|
||||
} else if (nstate == Node.MODIFIED) {
|
||||
nmgr.updateNode (nmgr.db, txn, node);
|
||||
node.setState (Node.CLEAN);
|
||||
upd++;
|
||||
IServer.getLogger().log ("updated: "+node.getFullName ());
|
||||
} else if (nstate == Node.DELETED) {
|
||||
// IServer.getLogger().log ("deleted: "+node.getFullName ()+" ("+node.getName ()+")");
|
||||
nmgr.deleteNode (nmgr.db, txn, node);
|
||||
nmgr.evictNode (node);
|
||||
dlt++;
|
||||
} else {
|
||||
// IServer.getLogger().log ("noop: "+node.getFullName ());
|
||||
}
|
||||
node.clearWriteLock ();
|
||||
}
|
||||
|
||||
nodes.clear ();
|
||||
cleannodes.clear ();
|
||||
// sqlCon.clear ();
|
||||
|
||||
if (nmgr.idgen.dirty) {
|
||||
nmgr.db.save (txn, "idgen", nmgr.idgen);
|
||||
nmgr.idgen.dirty = false;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
active = false;
|
||||
nmgr.db.commitTransaction (txn);
|
||||
txn = null;
|
||||
}
|
||||
|
||||
IServer.getLogger().log (tname+" "+l+" marked, "+ins+" inserted, "+upd+" updated, "+dlt+" deleted in "+(System.currentTimeMillis()-tstart)+" millis");
|
||||
}
|
||||
|
||||
public synchronized void abort () throws Exception {
|
||||
|
||||
int l = nodes.size ();
|
||||
for (Enumeration e=nodes.elements(); e.hasMoreElements(); ) {
|
||||
Node node = (Node) e.nextElement ();
|
||||
// Declare node as invalid, so it won't be used by other threads that want to
|
||||
// write on it and remove it from cache
|
||||
nmgr.evictNode (node);
|
||||
node.clearWriteLock ();
|
||||
}
|
||||
nodes.clear ();
|
||||
cleannodes.clear ();
|
||||
for (Enumeration e=sqlCon.elements(); e.hasMoreElements(); ) {
|
||||
try {
|
||||
Connection con = (Connection) e.nextElement ();
|
||||
con.close ();
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
sqlCon.clear ();
|
||||
|
||||
if (active) {
|
||||
active = false;
|
||||
nmgr.db.abortTransaction (txn);
|
||||
txn = null;
|
||||
}
|
||||
IServer.getLogger().log (tname+" aborted after "+(System.currentTimeMillis()-tstart)+" millis");
|
||||
}
|
||||
|
||||
public synchronized void kill () {
|
||||
killed = true;
|
||||
// The thread is told to stop by setting the thread flag in the EcmaScript
|
||||
// evaluator, so we can hope that it stops without doing anything else.
|
||||
try {
|
||||
join (1000);
|
||||
} catch (InterruptedException ir) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
// Interrupt the thread if it has not noticed the flag (e.g. because it is busy
|
||||
// reading from a network socket).
|
||||
if (isAlive()) {
|
||||
interrupt ();
|
||||
try {
|
||||
join (3000);
|
||||
} catch (InterruptedException ignore) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
if (isAlive())
|
||||
// Sorry to be so rude...
|
||||
stop (new TimeoutException());
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanup () {
|
||||
if (sqlCon != null) {
|
||||
for (Enumeration e=sqlCon.elements(); e.hasMoreElements(); ) {
|
||||
try {
|
||||
Connection con = (Connection) e.nextElement ();
|
||||
con.close ();
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
sqlCon.clear ();
|
||||
sqlCon = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
186
src/helma/objectmodel/db/WrappedNodeManager.java
Normal file
186
src/helma/objectmodel/db/WrappedNodeManager.java
Normal file
|
@ -0,0 +1,186 @@
|
|||
// WrappedNodeManager.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import helma.objectmodel.*;
|
||||
import java.util.Vector;
|
||||
|
||||
|
||||
/**
|
||||
* A wrapper around NodeManager that catches most Exceptions, or rethrows them as RuntimeExceptions.
|
||||
* The idea behind this is that we don't care a lot about Exception classes, since Hop programming is done
|
||||
* in JavaScript which doesn't know about them (except for the exception message).
|
||||
*/
|
||||
|
||||
public class WrappedNodeManager {
|
||||
|
||||
NodeManager nmgr;
|
||||
|
||||
public WrappedNodeManager (NodeManager nmgr) {
|
||||
this.nmgr = nmgr;
|
||||
}
|
||||
|
||||
public Node getNode (String id, DbMapping dbmap) {
|
||||
try {
|
||||
return nmgr.getNode (id, dbmap);
|
||||
} catch (ObjectNotFoundException x) {
|
||||
return null;
|
||||
} catch (Exception x) {
|
||||
Server.getLogger().log ("Error retrieving Node via DbMapping: "+x.getMessage ());
|
||||
if ("true".equalsIgnoreCase (Server.sysProps.getProperty("debug")))
|
||||
x.printStackTrace();
|
||||
throw new RuntimeException ("Error retrieving Node: "+x.getMessage ());
|
||||
}
|
||||
}
|
||||
|
||||
public Node getNode (Node home, String id, Relation rel) {
|
||||
try {
|
||||
return nmgr.getNode (home, id, rel);
|
||||
} catch (ObjectNotFoundException x) {
|
||||
return null;
|
||||
} catch (Exception x) {
|
||||
Server.getLogger().log ("Error retrieving Node \""+id+"\" from "+home+": "+x.getMessage ());
|
||||
if ("true".equalsIgnoreCase (Server.sysProps.getProperty("debug")))
|
||||
x.printStackTrace();
|
||||
throw new RuntimeException ("Error retrieving Node: "+x.getMessage ());
|
||||
}
|
||||
}
|
||||
|
||||
public Vector getNodes (Node home, Relation rel) {
|
||||
try {
|
||||
return nmgr.getNodes (home, rel);
|
||||
} catch (Exception x) {
|
||||
if ("true".equalsIgnoreCase (Server.sysProps.getProperty("debug")))
|
||||
x.printStackTrace();
|
||||
throw new RuntimeException ("Error retrieving Nodes: "+x.getMessage ());
|
||||
}
|
||||
}
|
||||
|
||||
public Vector getNodeIDs (Node home, Relation rel) {
|
||||
try {
|
||||
return nmgr.getNodeIDs (home, rel);
|
||||
} catch (Exception x) {
|
||||
if ("true".equalsIgnoreCase (Server.sysProps.getProperty("debug")))
|
||||
x.printStackTrace();
|
||||
throw new RuntimeException ("Error retrieving NodeIDs: "+x.getMessage ());
|
||||
}
|
||||
}
|
||||
|
||||
public int countNodes (Node home, Relation rel) {
|
||||
try {
|
||||
return nmgr.countNodes (home, rel);
|
||||
} catch (Exception x) {
|
||||
if ("true".equalsIgnoreCase (Server.sysProps.getProperty("debug")))
|
||||
x.printStackTrace();
|
||||
throw new RuntimeException ("Error counting Node: "+x.getMessage ());
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteNode (Node node) {
|
||||
try {
|
||||
nmgr.deleteNode (node);
|
||||
} catch (Exception x) {
|
||||
if ("true".equalsIgnoreCase (Server.sysProps.getProperty("debug")))
|
||||
x.printStackTrace();
|
||||
throw new RuntimeException ("Error deleting Node: "+x.getMessage ());
|
||||
}
|
||||
}
|
||||
|
||||
public void registerNode (Node node) {
|
||||
nmgr.registerNode (node);
|
||||
}
|
||||
|
||||
public void evictNode (Node node) {
|
||||
nmgr.evictNode (node);
|
||||
}
|
||||
|
||||
public void evictKey (Key key) {
|
||||
nmgr.evictKey (key);
|
||||
}
|
||||
|
||||
|
||||
public String generateID () {
|
||||
return nmgr.idgen.newID ();
|
||||
}
|
||||
|
||||
public String generateID (DbMapping map) {
|
||||
try {
|
||||
if (map == null || map.getIDgen() == null)
|
||||
return nmgr.idgen.newID ();
|
||||
else
|
||||
return nmgr.generateID (map);
|
||||
} catch (Exception x) {
|
||||
throw new RuntimeException (x.toString ());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
208
src/helma/servlet/AcmeFileServlet.java
Normal file
208
src/helma/servlet/AcmeFileServlet.java
Normal file
|
@ -0,0 +1,208 @@
|
|||
// FileServlet - servlet similar to a standard httpd
|
||||
//
|
||||
// Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
//
|
||||
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||
// fine Java utilities: http://www.acme.com/java/
|
||||
|
||||
package helma.servlet;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
import Acme.Serve.*;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/// Servlet similar to a standard httpd.
|
||||
// <P>
|
||||
// Implements the "GET" and "HEAD" methods for files and directories.
|
||||
// Handles index.html.
|
||||
// Redirects directory URLs that lack a trailing /.
|
||||
// Handles If-Modified-Since and Range.
|
||||
// <P>
|
||||
// <A HREF="/resources/classes/Acme/Serve/FileServlet.java">Fetch the software.</A><BR>
|
||||
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||
// <P>
|
||||
// @see Acme.Serve.Serve
|
||||
|
||||
public class AcmeFileServlet extends FileServlet
|
||||
{
|
||||
|
||||
private File root;
|
||||
|
||||
|
||||
/// Constructor.
|
||||
public AcmeFileServlet(File root)
|
||||
{
|
||||
super ();
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
/// Services a single request from the client.
|
||||
// @param req the servlet request
|
||||
// @param req the servlet response
|
||||
// @exception ServletException when an exception has occurred
|
||||
public void service( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException
|
||||
{
|
||||
boolean headOnly;
|
||||
if ( req.getMethod().equalsIgnoreCase( "get" ) )
|
||||
headOnly = false;
|
||||
else if ( ! req.getMethod().equalsIgnoreCase( "head" ) )
|
||||
headOnly = true;
|
||||
else
|
||||
{
|
||||
res.sendError( HttpServletResponse.SC_NOT_IMPLEMENTED );
|
||||
return;
|
||||
}
|
||||
|
||||
String path = req.getServletPath();
|
||||
if ( path == null || path.charAt( 0 ) != '/' )
|
||||
{
|
||||
res.sendError( HttpServletResponse.SC_BAD_REQUEST );
|
||||
return;
|
||||
}
|
||||
if ( path.indexOf( "/../" ) != -1 || path.endsWith( "/.." ) )
|
||||
{
|
||||
res.sendError( HttpServletResponse.SC_FORBIDDEN );
|
||||
return;
|
||||
}
|
||||
|
||||
// Make a version without the leading /.
|
||||
String pathname = path.substring( 1 );
|
||||
if ( pathname.length() == 0 )
|
||||
pathname = "./";
|
||||
|
||||
dispatchPathname( req, res, headOnly, path, pathname );
|
||||
}
|
||||
|
||||
|
||||
protected void dispatchPathname( HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path, String pathname ) throws IOException
|
||||
{
|
||||
String filename = pathname.replace( '/', File.separatorChar );
|
||||
if ( filename.charAt( filename.length() - 1 ) == File.separatorChar )
|
||||
filename = filename.substring( 0, filename.length() - 1 );
|
||||
if (filename.startsWith ("static"))
|
||||
filename = filename.substring ( Math.min (7, filename.length()) );
|
||||
|
||||
File file = new File( root, filename );
|
||||
|
||||
if ( file.exists() )
|
||||
{
|
||||
if ( ! file.isDirectory() )
|
||||
serveFile( req, res, headOnly, path, filename, file );
|
||||
else
|
||||
{
|
||||
if ( pathname.charAt( pathname.length() - 1 ) != '/' )
|
||||
redirectDirectory( req, res, path, file );
|
||||
else
|
||||
{
|
||||
String indexFilename =
|
||||
filename + File.separatorChar + "index.html";
|
||||
File indexFile = new File( indexFilename );
|
||||
if ( indexFile.exists() )
|
||||
serveFile(
|
||||
req, res, headOnly, path, indexFilename,
|
||||
indexFile );
|
||||
else
|
||||
serveDirectory(
|
||||
req, res, headOnly, path, filename, file );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( pathname.endsWith( "/index.html" ) )
|
||||
dispatchPathname(
|
||||
req, res, headOnly, path,
|
||||
pathname.substring( 0, pathname.length() - 10 ) );
|
||||
else if ( pathname.equals( "index.html" ) )
|
||||
dispatchPathname( req, res, headOnly, path, "./" );
|
||||
else
|
||||
res.sendError( HttpServletResponse.SC_NOT_FOUND );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void serveDirectory( HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path, String filename, File file ) throws IOException
|
||||
{
|
||||
log( "indexing " + path );
|
||||
if ( ! file.canRead() )
|
||||
{
|
||||
res.sendError( HttpServletResponse.SC_FORBIDDEN );
|
||||
return;
|
||||
}
|
||||
res.setStatus( HttpServletResponse.SC_OK );
|
||||
res.setContentType( "text/html" );
|
||||
OutputStream out = res.getOutputStream();
|
||||
if ( ! headOnly )
|
||||
{
|
||||
PrintStream p = new PrintStream( new BufferedOutputStream( out ) );
|
||||
p.println( "<HTML><HEAD>" );
|
||||
p.println( "<TITLE>Index of " + path + "</TITLE>" );
|
||||
p.println( "</HEAD><BODY BGCOLOR=\"#ffffff\">" );
|
||||
p.println( "<H2>Index of " + path + "</H2>" );
|
||||
p.println( "<PRE>" );
|
||||
p.println( "mode bytes last-changed name" );
|
||||
p.println( "<HR>" );
|
||||
String[] names = file.list();
|
||||
Acme.Utils.sortStrings( names );
|
||||
for ( int i = 0; i < names.length; ++i )
|
||||
{
|
||||
String aFilename = filename + File.separatorChar + names[i];
|
||||
File aFile = new File( aFilename );
|
||||
String aFileType;
|
||||
if ( aFile.isDirectory() )
|
||||
aFileType = "d";
|
||||
else if ( aFile.isFile() )
|
||||
aFileType = "-";
|
||||
else
|
||||
aFileType = "?";
|
||||
String aFileRead = ( aFile.canRead() ? "r" : "-" );
|
||||
String aFileWrite = ( aFile.canWrite() ? "w" : "-" );
|
||||
String aFileExe = "-";
|
||||
String aFileSize = Acme.Fmt.fmt( aFile.length(), 8 );
|
||||
String aFileDate =
|
||||
Acme.Utils.lsDateStr( new Date( aFile.lastModified() ) );
|
||||
String aFileDirsuf = ( aFile.isDirectory() ? "/" : "" );
|
||||
String aFileSuf = ( aFile.isDirectory() ? "/" : "" );
|
||||
p.println(
|
||||
aFileType + aFileRead + aFileWrite + aFileExe +
|
||||
" " + aFileSize + " " + aFileDate + " " +
|
||||
"<A HREF=\"" + names[i] + aFileDirsuf + "\">" +
|
||||
names[i] + aFileSuf + "</A>" );
|
||||
}
|
||||
p.println( "</PRE>" );
|
||||
p.println( "<HR>" );
|
||||
ServeUtils.writeAddress( p );
|
||||
p.println( "</BODY></HTML>" );
|
||||
p.flush();
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
|
||||
|
||||
}
|
254
src/helma/servlet/AcmeServletClient.java
Normal file
254
src/helma/servlet/AcmeServletClient.java
Normal file
|
@ -0,0 +1,254 @@
|
|||
// ServletClient.java
|
||||
// Copyright (c) Hannes Wallnoefer, Raphael Spannocchi 1998-2000
|
||||
|
||||
/* Portierung von helma.asp.AspClient auf Servlets */
|
||||
/* Author: Raphael Spannocchi Datum: 27.11.1998 */
|
||||
|
||||
package helma.servlet;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import helma.framework.*;
|
||||
import helma.framework.core.Application;
|
||||
import helma.objectmodel.Node;
|
||||
import helma.util.Uploader;
|
||||
|
||||
/**
|
||||
* This is the HOP servlet adapter that uses the Acme servlet API clone and communicates
|
||||
* directly with hop applications instead of using RMI.
|
||||
*/
|
||||
|
||||
public class AcmeServletClient extends HttpServlet{
|
||||
|
||||
private int uploadLimit; // limit to HTTP uploads in kB
|
||||
private Hashtable apps;
|
||||
private Application app;
|
||||
private String cookieDomain;
|
||||
private boolean caching;
|
||||
private boolean debug;
|
||||
|
||||
|
||||
public AcmeServletClient (Application app) {
|
||||
this.app = app;
|
||||
this.uploadLimit = 1024; // generous 1mb upload limit
|
||||
}
|
||||
|
||||
|
||||
public void service (HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
execute (request, response);
|
||||
}
|
||||
|
||||
public void doGet (HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
execute (request, response);
|
||||
}
|
||||
|
||||
public void doPost (HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
execute (request, response);
|
||||
}
|
||||
|
||||
|
||||
private void execute (HttpServletRequest request, HttpServletResponse response) {
|
||||
String protocol = request.getProtocol ();
|
||||
Cookie[] cookies = request.getCookies();
|
||||
try {
|
||||
RequestTrans reqtrans = new RequestTrans ();
|
||||
// HACK - sessions not fully supported in Acme.Serve
|
||||
// Thats ok, we dont need the session object, just the id.
|
||||
reqtrans.session = request.getRequestedSessionId();
|
||||
if (cookies != null) {
|
||||
for (int i=0; i < cookies.length;i++) try { // get Cookies
|
||||
String nextKey = cookies[i].getName ();
|
||||
String nextPart = cookies[i].getValue ();
|
||||
reqtrans.set (nextKey, nextPart);
|
||||
} catch (Exception badCookie) {}
|
||||
}
|
||||
// get optional path info
|
||||
String pathInfo = request.getServletPath ();
|
||||
if (pathInfo != null) {
|
||||
if (pathInfo.indexOf (app.getName()) == 1)
|
||||
pathInfo = pathInfo.substring (app.getName().length()+1);
|
||||
reqtrans.path = trim (pathInfo);
|
||||
} else
|
||||
reqtrans.path = "";
|
||||
|
||||
String host = request.getHeader ("Host");
|
||||
if (host != null) {
|
||||
host = host.toLowerCase();
|
||||
reqtrans.set ("http_host", host);
|
||||
}
|
||||
|
||||
String referer = request.getHeader ("Referer");
|
||||
if (referer != null)
|
||||
reqtrans.set ("http_referer", referer);
|
||||
|
||||
String remotehost = request.getRemoteAddr ();
|
||||
if (remotehost != null)
|
||||
reqtrans.set ("http_remotehost", remotehost);
|
||||
|
||||
String browser = request.getHeader ("User-Agent");
|
||||
if (browser != null)
|
||||
reqtrans.set ("http_browser", browser);
|
||||
|
||||
for (Enumeration e = request.getParameterNames(); e.hasMoreElements(); ) {
|
||||
// Params parsen
|
||||
String nextKey = (String)e.nextElement();
|
||||
String[] paramValues = request.getParameterValues(nextKey);
|
||||
String nextValue = paramValues[0]; // Only take first value
|
||||
reqtrans.set (nextKey, nextValue); // generic Header, Parameter
|
||||
}
|
||||
|
||||
String contentType = request.getContentType();
|
||||
if (contentType != null && contentType.indexOf("multipart/form-data")==0) {
|
||||
// File Upload
|
||||
Uploader up;
|
||||
try {
|
||||
if ((up = getUpload (uploadLimit, request)) != null) {
|
||||
Hashtable upload = up.getParts ();
|
||||
for (Enumeration e = upload.keys(); e.hasMoreElements(); ) {
|
||||
String nextKey = (String) e.nextElement ();
|
||||
Object nextPart = upload.get (nextKey);
|
||||
reqtrans.set (nextKey, nextPart);
|
||||
}
|
||||
}
|
||||
} catch (Exception upx) {
|
||||
String uploadErr = upx.getMessage ();
|
||||
if (uploadErr == null || uploadErr.length () == 0)
|
||||
uploadErr = upx.toString ();
|
||||
reqtrans.set ("uploadError", uploadErr);
|
||||
}
|
||||
}
|
||||
|
||||
ResponseTrans restrans = null;
|
||||
restrans = app.execute (reqtrans);
|
||||
writeResponse (response, restrans, cookies, protocol);
|
||||
|
||||
} catch (Exception x) {
|
||||
x.printStackTrace ();
|
||||
try {
|
||||
response.setContentType ("text/html");
|
||||
Writer out = response.getWriter ();
|
||||
if (debug)
|
||||
out.write ("<b>Error:</b><br>" +x);
|
||||
else
|
||||
out.write ("This server is temporarily unavailable. Please check back later.");
|
||||
out.flush ();
|
||||
} catch (Exception io_e) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void writeResponse (HttpServletResponse res, ResponseTrans trans, Cookie[] cookies, String protocol) {
|
||||
for (int i = 0; i < trans.countCookies(); i++) try {
|
||||
Cookie c = new Cookie(trans.getKeyAt(i), trans.getValueAt(i));
|
||||
c.setPath ("/");
|
||||
if (cookieDomain != null)
|
||||
c.setDomain (cookieDomain);
|
||||
int expires = trans.getDaysAt(i);
|
||||
if (expires > 0)
|
||||
c.setMaxAge(expires * 60*60*24); // Cookie time to live, days -> seconds
|
||||
res.addCookie(c);
|
||||
} catch (Exception ign) {}
|
||||
|
||||
if (trans.redirect != null) {
|
||||
try {
|
||||
res.sendRedirect(trans.redirect);
|
||||
} catch(Exception io_e) {}
|
||||
|
||||
} else {
|
||||
if (!trans.cache || ! caching) {
|
||||
// Disable caching of response.
|
||||
if (protocol == null || !protocol.endsWith ("1.1"))
|
||||
res.setHeader ("Pragma", "no-cache"); // for HTTP 1.0
|
||||
else
|
||||
res.setHeader ("Cache-Control", "no-cache"); // for HTTP 1.1
|
||||
}
|
||||
res.setStatus( HttpServletResponse.SC_OK );
|
||||
res.setContentLength (trans.getContentLength ());
|
||||
res.setContentType (trans.contentType);
|
||||
try {
|
||||
Writer writer = res.getWriter ();
|
||||
writer.write (trans.getContentString ());
|
||||
writer.flush ();
|
||||
} catch(Exception io_e) { System.out.println ("Error in writeResponse: "+io_e); }
|
||||
}
|
||||
}
|
||||
|
||||
private void redirectResponse (HttpServletRequest request, HttpServletResponse res, ResponseTrans trans, String url) {
|
||||
try {
|
||||
res.sendRedirect(url);
|
||||
} catch (Exception e) {
|
||||
System.err.println ("Exception bei redirect: " + e + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Uploader getUpload (HttpServletRequest request) throws Exception {
|
||||
return getUpload (500, request);
|
||||
}
|
||||
|
||||
public Uploader getUpload (int maxKbytes, HttpServletRequest request) throws Exception {
|
||||
int contentLength = request.getContentLength ();
|
||||
BufferedInputStream in = new BufferedInputStream (request.getInputStream ());
|
||||
Uploader up = null;
|
||||
if (contentLength > maxKbytes*1024) {
|
||||
throw new RuntimeException ("Upload exceeds limit of "+maxKbytes+" kb.");
|
||||
}
|
||||
String contentType = request.getContentType ();
|
||||
up = new Uploader(maxKbytes);
|
||||
up.load (in, contentType, contentLength);
|
||||
return up;
|
||||
}
|
||||
|
||||
|
||||
public Object getUploadPart(Uploader up, String name) {
|
||||
return up.getParts().get(name);
|
||||
}
|
||||
|
||||
|
||||
public String getServletInfo(){
|
||||
return new String("Helma ServletClient");
|
||||
}
|
||||
|
||||
|
||||
private String trim (String str) {
|
||||
|
||||
if (str == null)
|
||||
return null;
|
||||
char[] val = str.toCharArray ();
|
||||
int len = val.length;
|
||||
int st = 0;
|
||||
|
||||
while ((st < len) && (val[st] <= ' ' || val[st] == '/')) {
|
||||
st++;
|
||||
}
|
||||
while ((st < len) && (val[len - 1] <= ' ' || val[len - 1] == '/')) {
|
||||
len--;
|
||||
}
|
||||
return ((st > 0) || (len < val.length)) ? new String (val, st, len-st) : str;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
312
src/helma/servlet/ServletClient.java
Normal file
312
src/helma/servlet/ServletClient.java
Normal file
|
@ -0,0 +1,312 @@
|
|||
// ServletClient.java
|
||||
// Copyright (c) Hannes Wallnöfer, Raphael Spannocchi 1998-2000
|
||||
|
||||
/* Portierung von helma.asp.AspClient auf Servlets */
|
||||
/* Author: Raphael Spannocchi Datum: 27.11.1998 */
|
||||
|
||||
package helma.servlet;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.*;
|
||||
import java.io.*;
|
||||
import java.rmi.Naming;
|
||||
import java.rmi.RemoteException;
|
||||
import java.util.*;
|
||||
import helma.framework.*;
|
||||
import helma.objectmodel.Node;
|
||||
import helma.util.*;
|
||||
|
||||
/**
|
||||
* This is the HOP servlet adapter. This class communicates with hop applications
|
||||
* via RMI.
|
||||
*/
|
||||
|
||||
public class ServletClient extends HttpServlet{
|
||||
|
||||
private String host = null;
|
||||
private int port = 0;
|
||||
private int uploadLimit; // limit to HTTP uploads in kB
|
||||
private Hashtable apps;
|
||||
private String appName;
|
||||
private String appUrl;
|
||||
private String cookieDomain;
|
||||
private boolean caching;
|
||||
private boolean debug;
|
||||
|
||||
|
||||
public void init (ServletConfig init) {
|
||||
apps = new Hashtable();
|
||||
|
||||
appName = init.getInitParameter ("application");
|
||||
if (appName == null) appName = "base";
|
||||
|
||||
host = init.getInitParameter ("host");
|
||||
if (host == null) host = "localhost";
|
||||
|
||||
String portstr = init.getInitParameter ("port");
|
||||
port = portstr == null ? 5055 : Integer.parseInt (portstr);
|
||||
|
||||
String upstr = init.getInitParameter ("uploadLimit");
|
||||
uploadLimit = upstr == null ? 500 : Integer.parseInt (upstr);
|
||||
|
||||
cookieDomain = init.getInitParameter ("cookieDomain");
|
||||
|
||||
appUrl = "//" + host + ":" + port + "/";
|
||||
debug = ("true".equalsIgnoreCase (init.getInitParameter ("debug")));
|
||||
|
||||
caching = ! ("false".equalsIgnoreCase (init.getInitParameter ("caching")));
|
||||
}
|
||||
|
||||
|
||||
private IRemoteApp getApp (String appID) throws Exception {
|
||||
IRemoteApp retval = (IRemoteApp) apps.get (appID);
|
||||
if (retval != null) {
|
||||
return retval;
|
||||
}
|
||||
retval = (IRemoteApp) Naming.lookup (appUrl + appID);
|
||||
apps.put (appID, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
public void doGet (HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
execute (appName, request, response);
|
||||
}
|
||||
|
||||
public void doPost (HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
execute (appName, request, response);
|
||||
}
|
||||
|
||||
// not used anymore
|
||||
private void get(String appID, HttpServletRequest request, HttpServletResponse response) {
|
||||
}
|
||||
|
||||
private void execute (String appID, HttpServletRequest request, HttpServletResponse response) {
|
||||
String protocol = request.getProtocol ();
|
||||
Cookie[] cookies = request.getCookies();
|
||||
try {
|
||||
RequestTrans reqtrans = new RequestTrans ();
|
||||
if (cookies != null) {
|
||||
for (int i=0; i < cookies.length;i++) try { // get Cookies
|
||||
String nextKey = cookies[i].getName ();
|
||||
String nextPart = cookies[i].getValue ();
|
||||
if ("HopSession".equals (nextKey))
|
||||
reqtrans.session = nextPart;
|
||||
else
|
||||
reqtrans.set (nextKey, nextPart);
|
||||
} catch (Exception badCookie) {}
|
||||
}
|
||||
|
||||
// check if we need to create a session id
|
||||
if (reqtrans.session == null) {
|
||||
reqtrans.session = Long.toString (Math.round (Math.random ()*Long.MAX_VALUE), 16);
|
||||
reqtrans.session += "@"+Long.toString (System.currentTimeMillis (), 16);
|
||||
Cookie c = new Cookie("HopSession", reqtrans.session);
|
||||
c.setPath ("/");
|
||||
if (cookieDomain != null)
|
||||
c.setDomain (cookieDomain);
|
||||
response.addCookie(c);
|
||||
}
|
||||
|
||||
// get optional path info
|
||||
String pathInfo = request.getPathInfo ();
|
||||
if (pathInfo != null)
|
||||
reqtrans.path = trim (pathInfo);
|
||||
else
|
||||
reqtrans.path = "";
|
||||
|
||||
String host = request.getHeader ("Host");
|
||||
if (host != null) {
|
||||
host = host.toLowerCase();
|
||||
reqtrans.set ("http_host", host);
|
||||
}
|
||||
|
||||
String referer = request.getHeader ("Referer");
|
||||
if (referer != null)
|
||||
reqtrans.set ("http_referer", referer);
|
||||
|
||||
String remotehost = request.getRemoteAddr ();
|
||||
if (remotehost != null)
|
||||
reqtrans.set ("http_remotehost", remotehost);
|
||||
|
||||
String browser = request.getHeader ("User-Agent");
|
||||
if (browser != null)
|
||||
reqtrans.set ("http_browser", browser);
|
||||
|
||||
for (Enumeration e = request.getParameterNames(); e.hasMoreElements(); ) {
|
||||
// Params parsen
|
||||
String nextKey = (String)e.nextElement();
|
||||
String[] paramValues = request.getParameterValues(nextKey);
|
||||
String nextValue = paramValues[0]; // Only take first value
|
||||
reqtrans.set (nextKey, nextValue); // generic Header, Parameter
|
||||
}
|
||||
|
||||
String contentType = request.getContentType();
|
||||
if (contentType != null && contentType.indexOf("multipart/form-data")==0) {
|
||||
// File Upload
|
||||
Uploader up;
|
||||
try {
|
||||
if ((up = getUpload (uploadLimit, request)) != null) {
|
||||
Hashtable upload = up.getParts ();
|
||||
for (Enumeration e = upload.keys(); e.hasMoreElements(); ) {
|
||||
String nextKey = (String) e.nextElement ();
|
||||
Object nextPart = upload.get (nextKey);
|
||||
reqtrans.set (nextKey, nextPart);
|
||||
}
|
||||
}
|
||||
} catch (Exception upx) {
|
||||
String uploadErr = upx.getMessage ();
|
||||
if (uploadErr == null || uploadErr.length () == 0)
|
||||
uploadErr = upx.toString ();
|
||||
reqtrans.set ("uploadError", uploadErr);
|
||||
}
|
||||
}
|
||||
|
||||
// get RMI ref to application and execute request
|
||||
IRemoteApp app = getApp (appID);
|
||||
ResponseTrans restrans = null;
|
||||
try {
|
||||
restrans = app.execute (reqtrans);
|
||||
} catch (RemoteException cnx) {
|
||||
apps.remove (appID);
|
||||
app = getApp (appID);
|
||||
app.ping ();
|
||||
restrans = app.execute (reqtrans);
|
||||
}
|
||||
writeResponse (response, restrans, cookies, protocol);
|
||||
|
||||
} catch (Exception x) {
|
||||
apps.remove (appID);
|
||||
try {
|
||||
response.setContentType ("text/html");
|
||||
Writer out = response.getWriter ();
|
||||
if (debug)
|
||||
out.write ("<b>Error:</b><br>" +x);
|
||||
else
|
||||
out.write ("This server is temporarily unavailable. Please check back later.");
|
||||
out.flush ();
|
||||
} catch (Exception io_e) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void writeResponse (HttpServletResponse res, ResponseTrans trans, Cookie[] cookies, String protocol) {
|
||||
|
||||
for (int i = 0; i < trans.countCookies(); i++) try {
|
||||
Cookie c = new Cookie(trans.getKeyAt(i), trans.getValueAt(i));
|
||||
c.setPath ("/");
|
||||
if (cookieDomain != null)
|
||||
c.setDomain (cookieDomain);
|
||||
int expires = trans.getDaysAt(i);
|
||||
if (expires > 0)
|
||||
c.setMaxAge(expires * 60*60*24); // Cookie time to live, days -> seconds
|
||||
res.addCookie(c);
|
||||
} catch (Exception ign) {}
|
||||
|
||||
if (trans.redirect != null) {
|
||||
try {
|
||||
res.sendRedirect(trans.redirect);
|
||||
} catch(Exception io_e) {}
|
||||
|
||||
} else {
|
||||
if (!trans.cache || ! caching) {
|
||||
// Disable caching of response.
|
||||
if (protocol == null || !protocol.endsWith ("1.1"))
|
||||
res.setHeader ("Pragma", "no-cache"); // for HTTP 1.0
|
||||
else
|
||||
res.setHeader ("Cache-Control", "no-cache"); // for HTTP 1.1
|
||||
}
|
||||
res.setContentLength (trans.getContentLength ());
|
||||
res.setContentType (trans.contentType);
|
||||
try {
|
||||
Writer writer = res.getWriter ();
|
||||
writer.write (trans.getContentString ());
|
||||
writer.flush ();
|
||||
} catch(Exception io_e) {}
|
||||
}
|
||||
}
|
||||
|
||||
private void redirectResponse (HttpServletRequest request, HttpServletResponse res, ResponseTrans trans, String url) {
|
||||
try {
|
||||
res.sendRedirect(url);
|
||||
} catch (Exception e) {
|
||||
System.err.println ("Exception at redirect: " + e + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Uploader getUpload (HttpServletRequest request) throws Exception {
|
||||
return getUpload (500, request);
|
||||
}
|
||||
|
||||
public Uploader getUpload (int maxKbytes, HttpServletRequest request) throws Exception {
|
||||
int contentLength = request.getContentLength ();
|
||||
BufferedInputStream in = new BufferedInputStream (request.getInputStream ());
|
||||
Uploader up = null;
|
||||
try {
|
||||
if (contentLength > maxKbytes*1024) {
|
||||
// consume all input to make Apache happy
|
||||
byte b[] = new byte[1024];
|
||||
int read = 0;
|
||||
while (read > -1)
|
||||
read = in.read (b, 0, 1024);
|
||||
throw new RuntimeException ("Upload exceeds limit of "+maxKbytes+" kb.");
|
||||
}
|
||||
String contentType = request.getContentType ();
|
||||
up = new Uploader(maxKbytes);
|
||||
up.load (in, contentType, contentLength);
|
||||
} finally {
|
||||
try { in.close (); } catch (Exception ignore) {}
|
||||
}
|
||||
return up;
|
||||
}
|
||||
|
||||
|
||||
public Object getUploadPart(Uploader up, String name) {
|
||||
return up.getParts().get(name);
|
||||
}
|
||||
|
||||
|
||||
public String getServletInfo(){
|
||||
return new String("Helma ServletClient");
|
||||
}
|
||||
|
||||
|
||||
private String trim (String str) {
|
||||
|
||||
if (str == null)
|
||||
return null;
|
||||
char[] val = str.toCharArray ();
|
||||
int len = val.length;
|
||||
int st = 0;
|
||||
|
||||
while ((st < len) && (val[st] <= ' ' || val[st] == '/')) {
|
||||
st++;
|
||||
}
|
||||
while ((st < len) && (val[len - 1] <= ' ' || val[len - 1] == '/')) {
|
||||
len--;
|
||||
}
|
||||
return ((st > 0) || (len < val.length)) ? new String (val, st, len-st) : str;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
365
src/helma/util/HtmlEncoder.java
Normal file
365
src/helma/util/HtmlEncoder.java
Normal file
|
@ -0,0 +1,365 @@
|
|||
// HtmlEncoder.java
|
||||
// Copyright (c) Hannes Wallnöfer 1997-2000
|
||||
|
||||
package helma.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import java.text.*;
|
||||
|
||||
|
||||
/**
|
||||
* This is a utility class to encode special characters and do formatting
|
||||
* for HTML output.
|
||||
*/
|
||||
|
||||
public final class HtmlEncoder {
|
||||
|
||||
|
||||
static final Hashtable convertor = new Hashtable (128);
|
||||
|
||||
// conversion table
|
||||
static {
|
||||
convertor.put(new Integer(160), " ");
|
||||
convertor.put(new Integer(161), "¡");
|
||||
convertor.put(new Integer(162), "¢");
|
||||
convertor.put(new Integer(163), "£");
|
||||
convertor.put(new Integer(164), "¤");
|
||||
convertor.put(new Integer(165), "¥");
|
||||
convertor.put(new Integer(166), "¦");
|
||||
convertor.put(new Integer(167), "§");
|
||||
convertor.put(new Integer(168), "¨");
|
||||
convertor.put(new Integer(169), "©");
|
||||
convertor.put(new Integer(170), "ª");
|
||||
convertor.put(new Integer(171), "«");
|
||||
convertor.put(new Integer(172), "¬");
|
||||
convertor.put(new Integer(173), "­");
|
||||
convertor.put(new Integer(174), "®");
|
||||
convertor.put(new Integer(175), "¯");
|
||||
convertor.put(new Integer(176), "°");
|
||||
convertor.put(new Integer(177), "±");
|
||||
convertor.put(new Integer(178), "²");
|
||||
convertor.put(new Integer(179), "³");
|
||||
convertor.put(new Integer(180), "´");
|
||||
convertor.put(new Integer(181), "µ");
|
||||
convertor.put(new Integer(182), "¶");
|
||||
convertor.put(new Integer(183), "·");
|
||||
convertor.put(new Integer(184), "¸");
|
||||
convertor.put(new Integer(185), "¹");
|
||||
convertor.put(new Integer(186), "º");
|
||||
convertor.put(new Integer(187), "»");
|
||||
convertor.put(new Integer(188), "¼");
|
||||
convertor.put(new Integer(189), "½");
|
||||
convertor.put(new Integer(190), "¾");
|
||||
convertor.put(new Integer(191), "¿");
|
||||
convertor.put(new Integer(192), "À");
|
||||
convertor.put(new Integer(193), "Á");
|
||||
convertor.put(new Integer(194), "Â");
|
||||
convertor.put(new Integer(195), "Ã");
|
||||
convertor.put(new Integer(196), "Ä");
|
||||
convertor.put(new Integer(197), "Å");
|
||||
convertor.put(new Integer(198), "Æ");
|
||||
convertor.put(new Integer(199), "Ç");
|
||||
convertor.put(new Integer(200), "È");
|
||||
convertor.put(new Integer(201), "É");
|
||||
convertor.put(new Integer(202), "Ê");
|
||||
convertor.put(new Integer(203), "Ë");
|
||||
convertor.put(new Integer(204), "Ì");
|
||||
convertor.put(new Integer(205), "Í");
|
||||
convertor.put(new Integer(206), "Î");
|
||||
convertor.put(new Integer(207), "Ï");
|
||||
convertor.put(new Integer(208), "Ð");
|
||||
convertor.put(new Integer(209), "Ñ");
|
||||
convertor.put(new Integer(210), "Ò");
|
||||
convertor.put(new Integer(211), "Ó");
|
||||
convertor.put(new Integer(212), "Ô");
|
||||
convertor.put(new Integer(213), "Õ");
|
||||
convertor.put(new Integer(214), "Ö");
|
||||
convertor.put(new Integer(215), "×");
|
||||
convertor.put(new Integer(216), "Ø");
|
||||
convertor.put(new Integer(217), "Ù");
|
||||
convertor.put(new Integer(218), "Ú");
|
||||
convertor.put(new Integer(219), "Û");
|
||||
convertor.put(new Integer(220), "Ü");
|
||||
convertor.put(new Integer(221), "Ý");
|
||||
convertor.put(new Integer(222), "Þ");
|
||||
convertor.put(new Integer(223), "ß");
|
||||
convertor.put(new Integer(224), "à");
|
||||
convertor.put(new Integer(225), "á");
|
||||
convertor.put(new Integer(226), "â");
|
||||
convertor.put(new Integer(227), "ã");
|
||||
convertor.put(new Integer(228), "ä");
|
||||
convertor.put(new Integer(229), "å");
|
||||
convertor.put(new Integer(230), "æ");
|
||||
convertor.put(new Integer(231), "ç");
|
||||
convertor.put(new Integer(232), "è");
|
||||
convertor.put(new Integer(233), "é");
|
||||
convertor.put(new Integer(234), "ê");
|
||||
convertor.put(new Integer(235), "ë");
|
||||
convertor.put(new Integer(236), "ì");
|
||||
convertor.put(new Integer(237), "í");
|
||||
convertor.put(new Integer(238), "î");
|
||||
convertor.put(new Integer(239), "ï");
|
||||
convertor.put(new Integer(240), "ð");
|
||||
convertor.put(new Integer(241), "ñ");
|
||||
convertor.put(new Integer(242), "ò");
|
||||
convertor.put(new Integer(243), "ó");
|
||||
convertor.put(new Integer(244), "ô");
|
||||
convertor.put(new Integer(245), "õ");
|
||||
convertor.put(new Integer(246), "ö");
|
||||
convertor.put(new Integer(247), "÷");
|
||||
convertor.put(new Integer(248), "ø");
|
||||
convertor.put(new Integer(249), "ù");
|
||||
convertor.put(new Integer(250), "ú");
|
||||
convertor.put(new Integer(251), "û");
|
||||
convertor.put(new Integer(252), "ü");
|
||||
convertor.put(new Integer(253), "ý");
|
||||
convertor.put(new Integer(254), "þ");
|
||||
convertor.put(new Integer(255), "ÿ");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public final static String encode (String what) {
|
||||
// try to make stringbuffer large enough from the start
|
||||
StringBuffer ret = new StringBuffer (Math.round (what.length()*1.4f));
|
||||
encode (what, ret);
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public final static void encode (String what, StringBuffer ret) {
|
||||
if (what == null || what.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
StringReader in = new StringReader (what);
|
||||
int c;
|
||||
boolean closeTag=false, readTag=false, tagOpen=false;
|
||||
boolean ignoreNewline = false, swallow = false;
|
||||
StringBuffer tag = new StringBuffer ();
|
||||
try {
|
||||
while ((c = in.read()) != -1) {
|
||||
if (readTag) {
|
||||
if (Character.isLetterOrDigit ((char) c))
|
||||
tag.append ((char) c);
|
||||
else if ('/' == c)
|
||||
closeTag = true;
|
||||
else {
|
||||
String t = tag.toString ();
|
||||
// set ignoreNewline on some tags, depending on wheather they're
|
||||
// being opened or closed.
|
||||
if ("td".equalsIgnoreCase (t)) {
|
||||
ignoreNewline = closeTag;
|
||||
swallow = true; // for some reason, it's a good idea to swallow (ignore) newlines after some tags
|
||||
} else if ("th".equalsIgnoreCase (t)) {
|
||||
ignoreNewline = closeTag;
|
||||
swallow = true;
|
||||
} else if ("table".equalsIgnoreCase (t)) {
|
||||
ignoreNewline = !closeTag;
|
||||
swallow = true;
|
||||
} else if ("ul".equalsIgnoreCase (t)) {
|
||||
ignoreNewline = !closeTag;
|
||||
swallow = true;
|
||||
} else if ("ol".equalsIgnoreCase (t)) {
|
||||
ignoreNewline = !closeTag;
|
||||
swallow = true;
|
||||
} else if ("li".equalsIgnoreCase (t)) {
|
||||
swallow = true;
|
||||
ignoreNewline = closeTag;
|
||||
} else if ("p".equalsIgnoreCase (t)) {
|
||||
swallow = true;
|
||||
}
|
||||
|
||||
readTag = false;
|
||||
closeTag = false;
|
||||
tag.setLength (0);
|
||||
}
|
||||
} // if (readTag)
|
||||
|
||||
switch (c) {
|
||||
// case '&':
|
||||
// ret.append ("&");
|
||||
// break;
|
||||
case '\n':
|
||||
if (!ignoreNewline && !swallow)
|
||||
ret.append ("<br>");
|
||||
ret.append ('\n');
|
||||
if (!tagOpen)
|
||||
swallow = false;
|
||||
break;
|
||||
case '<':
|
||||
closeTag = false;
|
||||
readTag = true;
|
||||
tagOpen = true;
|
||||
ret.append ('<');
|
||||
break;
|
||||
case '>':
|
||||
tagOpen = false;
|
||||
ret.append ('>');
|
||||
break;
|
||||
default:
|
||||
if (c < 160)
|
||||
ret.append ((char) c);
|
||||
else if (c >= 160 && c <= 255)
|
||||
ret.append (convertor.get(new Integer(c)));
|
||||
else {
|
||||
ret.append ("&#");
|
||||
ret.append (c);
|
||||
ret.append (";");
|
||||
}
|
||||
if (!tagOpen && !Character.isWhitespace ((char)c))
|
||||
swallow = false;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public final static String encodeFormValue (String what) {
|
||||
StringBuffer ret = new StringBuffer (Math.round (what.length()*1.4f));
|
||||
encodeAll (what, ret, false);
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public final static String encodeAll (String what) {
|
||||
StringBuffer ret = new StringBuffer (Math.round (what.length()*1.4f));
|
||||
encodeAll (what, ret, true);
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public final static String encodeAll (String what, StringBuffer ret) {
|
||||
encodeAll (what, ret, true);
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public final static void encodeAll (String what, StringBuffer ret, boolean encodeNewline) {
|
||||
if (what == null || what.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
StringReader in = new StringReader (what);
|
||||
int c;
|
||||
try {
|
||||
while ((c = in.read()) != -1) {
|
||||
switch (c) {
|
||||
case '<' :
|
||||
ret.append ("<");
|
||||
break;
|
||||
case '>':
|
||||
ret.append (">");
|
||||
break;
|
||||
case '&':
|
||||
ret.append ("&");
|
||||
break;
|
||||
case '"':
|
||||
ret.append (""");
|
||||
break;
|
||||
case '\n':
|
||||
if (encodeNewline) {
|
||||
ret.append ("<br>");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (c < 160)
|
||||
ret.append ((char) c);
|
||||
else if (c >= 160 && c <= 255)
|
||||
ret.append (convertor.get(new Integer(c)));
|
||||
else {
|
||||
ret.append ("&#");
|
||||
ret.append (c);
|
||||
ret.append (";");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
public final static String encodeSoft (String what) {
|
||||
StringBuffer ret = new StringBuffer (Math.round (what.length()*1.4f));
|
||||
encodeSoft (what, ret);
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
public final static void encodeSoft (String what, StringBuffer ret) {
|
||||
if (what == null || what.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
StringReader in = new StringReader (what);
|
||||
int c;
|
||||
try {
|
||||
while ((c = in.read()) != -1) {
|
||||
switch (c) {
|
||||
case 128: // Euro-Symbol. This is for missing Unicode support in TowerJ.
|
||||
ret.append ("€");
|
||||
break;
|
||||
default:
|
||||
if (c < 160)
|
||||
ret.append ((char) c);
|
||||
else if (c >= 160 && c <= 255)
|
||||
ret.append (convertor.get(new Integer(c)));
|
||||
else {
|
||||
ret.append ("&#");
|
||||
ret.append (c);
|
||||
ret.append (";");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
|
||||
public final static String encodeXml (String what) {
|
||||
StringBuffer ret = new StringBuffer (Math.round (what.length()*1.4f));
|
||||
encodeXml (what, ret);
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
public final static void encodeXml (String what, StringBuffer ret) {
|
||||
if (what == null || what.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
StringReader in = new StringReader (what);
|
||||
int c;
|
||||
try {
|
||||
while ((c = in.read()) != -1) {
|
||||
switch (c) {
|
||||
case '<' :
|
||||
ret.append ("<");
|
||||
break;
|
||||
case '>':
|
||||
ret.append (">");
|
||||
break;
|
||||
case '&':
|
||||
ret.append ("&");
|
||||
break;
|
||||
default:
|
||||
ret.append ((char) c);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
56
src/helma/util/InetAddressFilter.java
Normal file
56
src/helma/util/InetAddressFilter.java
Normal file
|
@ -0,0 +1,56 @@
|
|||
// InetAddressFilter.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A class for paranoid servers to filter IP addresses.
|
||||
*/
|
||||
|
||||
public class InetAddressFilter {
|
||||
|
||||
Vector patterns;
|
||||
|
||||
public InetAddressFilter () {
|
||||
patterns = new Vector ();
|
||||
}
|
||||
|
||||
public void addAddress (String address) throws IOException {
|
||||
int pattern[] = new int[4];
|
||||
StringTokenizer st = new StringTokenizer (address, ".");
|
||||
if (st.countTokens () != 4)
|
||||
throw new IOException ("\""+address+"\" does not represent a valid IP address");
|
||||
for (int i=0; i<4; i++) {
|
||||
String next = st.nextToken ();
|
||||
if ("*".equals (next))
|
||||
pattern[i] = 256;
|
||||
else
|
||||
pattern[i] = (byte) Integer.parseInt (next);
|
||||
}
|
||||
patterns.addElement (pattern);
|
||||
}
|
||||
|
||||
public boolean matches (InetAddress address) {
|
||||
if (address == null)
|
||||
return false;
|
||||
byte add[] = address.getAddress ();
|
||||
if (add == null)
|
||||
return false;
|
||||
int l = patterns.size();
|
||||
for (int k=0; k<l; k++) {
|
||||
int pattern[] = (int[]) patterns.elementAt (k);
|
||||
for (int i=0; i<4; i++) {
|
||||
if (pattern[i] < 255 && pattern[i] != add[i]) // not wildcard and doesn't match
|
||||
break;
|
||||
if (i == 3)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
103
src/helma/util/Logger.java
Normal file
103
src/helma/util/Logger.java
Normal file
|
@ -0,0 +1,103 @@
|
|||
// Logger.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
|
||||
/**
|
||||
* Utility class for asynchronous logging.
|
||||
*/
|
||||
|
||||
public class Logger implements Runnable {
|
||||
|
||||
private Thread logger;
|
||||
|
||||
private Vector entries;
|
||||
private String filename;
|
||||
private String dirname;
|
||||
private File dir;
|
||||
private File currentFile;
|
||||
private PrintWriter currentWriter;
|
||||
private int fileindex = 0;
|
||||
private DecimalFormat nformat;
|
||||
private DateFormat dformat;
|
||||
private long dateLastRendered;
|
||||
private String dateCache;
|
||||
private PrintStream out = null;
|
||||
|
||||
public Logger (PrintStream out) {
|
||||
dformat = DateFormat.getInstance ();
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
public Logger (String dirname, String filename) throws IOException {
|
||||
if (filename == null || dirname == null)
|
||||
throw new IOException ("Logger can't use null as file or directory name");
|
||||
this.filename = filename;
|
||||
this.dirname = dirname;
|
||||
nformat = new DecimalFormat ("00000");
|
||||
dformat = DateFormat.getInstance ();
|
||||
dir = new File (dirname);
|
||||
if (!dir.exists())
|
||||
dir.mkdirs ();
|
||||
currentFile = new File (dir, filename+nformat.format(++fileindex)+".log");
|
||||
while (currentFile.exists())
|
||||
currentFile = new File (dir, filename+nformat.format(++fileindex)+".log");
|
||||
currentWriter = new PrintWriter (new FileWriter (currentFile), false);
|
||||
entries = new Vector ();
|
||||
logger = new Thread (this);
|
||||
// logger.setPriority (Thread.MIN_PRIORITY+2);
|
||||
logger.start ();
|
||||
}
|
||||
|
||||
public void log (String msg) {
|
||||
// it's enough to render the date every 15 seconds
|
||||
if (System.currentTimeMillis () - 15000 > dateLastRendered)
|
||||
renderDate ();
|
||||
// log directly to printstream or to buffer?
|
||||
if (out == null)
|
||||
entries.addElement (dateCache + " " + msg);
|
||||
else
|
||||
out.println (dateCache + " " + msg);
|
||||
}
|
||||
|
||||
private void renderDate () {
|
||||
dateCache = dformat.format (new Date());
|
||||
dateLastRendered = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
public void run () {
|
||||
while (Thread.currentThread () == logger) {
|
||||
try {
|
||||
if (currentFile.length() > 10000000) {
|
||||
// rotate log files each 10 megs
|
||||
swapFile ();
|
||||
}
|
||||
int l = entries.size();
|
||||
for (int i=0; i<l; i++) {
|
||||
Object entry = entries.elementAt (0);
|
||||
entries.removeElementAt (0);
|
||||
currentWriter.println (entry.toString());
|
||||
}
|
||||
currentWriter.flush ();
|
||||
logger.sleep (1000l);
|
||||
} catch (InterruptedException ir) {
|
||||
Thread.currentThread().interrupt ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void swapFile () {
|
||||
try {
|
||||
currentWriter.close();
|
||||
currentFile = new File (dir, filename+nformat.format(++fileindex)+".log");
|
||||
currentWriter = new PrintWriter (new FileWriter (currentFile), false);
|
||||
} catch (IOException iox) {
|
||||
System.err.println ("Error swapping Log files: "+iox);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
50
src/helma/util/ParanoidServerSocket.java
Normal file
50
src/helma/util/ParanoidServerSocket.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
// ParanoidServerSocket.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.util;
|
||||
|
||||
import java.net.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A server socket that can allow connections to only a few selected hosts.
|
||||
*/
|
||||
|
||||
public class ParanoidServerSocket extends ServerSocket {
|
||||
|
||||
private InetAddressFilter filter;
|
||||
|
||||
|
||||
public ParanoidServerSocket (int port) throws IOException {
|
||||
super (port);
|
||||
}
|
||||
|
||||
public ParanoidServerSocket (int port, InetAddressFilter filter) throws IOException {
|
||||
super (port);
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
public Socket accept () throws IOException {
|
||||
Socket s = null;
|
||||
while (s == null) {
|
||||
s = super.accept ();
|
||||
if (filter != null && !filter.matches (s.getInetAddress ())) {
|
||||
System.err.println ("Refusing connection from "+s.getInetAddress ());
|
||||
try {
|
||||
s.close();
|
||||
} catch (IOException ignore) {}
|
||||
s = null;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public void setFilter (InetAddressFilter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
public InetAddressFilter getFilter () {
|
||||
return this.filter;
|
||||
}
|
||||
|
||||
}
|
70
src/helma/util/Timer.java
Normal file
70
src/helma/util/Timer.java
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Timer.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.util;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.PrintStream;
|
||||
|
||||
/**
|
||||
* Utility class for timing a series of events
|
||||
*/
|
||||
|
||||
public class Timer {
|
||||
|
||||
private Vector timeline;
|
||||
private Hashtable events;
|
||||
|
||||
public Timer () {
|
||||
timeline = new Vector ();
|
||||
events = new Hashtable ();
|
||||
}
|
||||
|
||||
public void reset () {
|
||||
timeline.setSize (0);
|
||||
events.clear ();
|
||||
}
|
||||
|
||||
public void beginEvent (String name) {
|
||||
timeline.addElement (name);
|
||||
events.put (name, new Event (name));
|
||||
}
|
||||
|
||||
public void endEvent (String name) {
|
||||
Event event = (Event) events.get (name);
|
||||
if (event != null)
|
||||
event.terminate ();
|
||||
}
|
||||
|
||||
public void dump (PrintStream out) {
|
||||
for (int i=0; i<timeline.size(); i++) {
|
||||
String name = (String) timeline.elementAt (i);
|
||||
Event event = (Event) events.get (name);
|
||||
out.println (event);
|
||||
}
|
||||
}
|
||||
|
||||
class Event {
|
||||
|
||||
String name;
|
||||
long start, end;
|
||||
|
||||
Event (String name) {
|
||||
this.name = name;
|
||||
start = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
void terminate () {
|
||||
end = System.currentTimeMillis ();
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
long now = System.currentTimeMillis ();
|
||||
if (end == 0l)
|
||||
return (" + "+(now-start)+" "+name);
|
||||
else
|
||||
return (" "+(end-start)+" "+name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
110
src/helma/util/Uploader.java
Normal file
110
src/helma/util/Uploader.java
Normal file
|
@ -0,0 +1,110 @@
|
|||
// Uploader.java
|
||||
// Copyright (c) Hannes Wallnöfer 1996-2000
|
||||
|
||||
package helma.util;
|
||||
|
||||
import helma.mime.*;
|
||||
import helma.objectmodel.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Utility class for file uploads via HTTP POST.
|
||||
*/
|
||||
|
||||
public class Uploader {
|
||||
|
||||
public Hashtable parts;
|
||||
int maxKbytes;
|
||||
|
||||
public Uploader () {
|
||||
maxKbytes = 500;
|
||||
}
|
||||
|
||||
public Uploader (int max) {
|
||||
maxKbytes = max;
|
||||
}
|
||||
|
||||
public Hashtable getParts () {
|
||||
return parts;
|
||||
}
|
||||
|
||||
|
||||
public void load (InputStream is, String contentType, int contentLength) throws Exception {
|
||||
|
||||
parts = new Hashtable ();
|
||||
|
||||
String boundary = getSubHeader (contentType, "boundary");
|
||||
if (boundary == null)
|
||||
throw new MimeParserException ("Error parsing MIME input stream.");
|
||||
if (maxKbytes > -1 && contentLength > maxKbytes*1024)
|
||||
throw new IOException ("Size of upload exceeds limit of " + maxKbytes + " kB.");
|
||||
|
||||
byte b[] = new byte[contentLength];
|
||||
MultipartInputStream in = new MultipartInputStream (new BufferedInputStream (is), boundary.getBytes ());
|
||||
|
||||
while (in.nextInputStream ()) {
|
||||
|
||||
MimeParser parser = new MimeParser (in, new MimeHeadersFactory ());
|
||||
MimeHeaders headers = (MimeHeaders) parser.parse ();
|
||||
|
||||
InputStream bodystream = parser.getInputStream ();
|
||||
int read, count = 0;
|
||||
|
||||
while ((read = bodystream.read (b, count, 4096)) > -1) {
|
||||
count += read;
|
||||
if (count == b.length) {
|
||||
byte newb[] = new byte[count+4096];
|
||||
System.arraycopy (b, 0, newb, 0, count);
|
||||
b = newb;
|
||||
}
|
||||
}
|
||||
|
||||
byte newb[] = new byte[count];
|
||||
System.arraycopy (b, 0, newb, 0, count);
|
||||
|
||||
String type = headers.getValue("Content-Type");
|
||||
String disposition = headers.getValue ("Content-Disposition");
|
||||
String name = getSubHeader (disposition, "name");
|
||||
String filename = getSubHeader (disposition, "filename");
|
||||
if (filename != null) {
|
||||
int sep = filename.lastIndexOf ("\\");
|
||||
if (sep > -1)
|
||||
filename = filename.substring (sep+1);
|
||||
sep = filename.lastIndexOf ("/");
|
||||
if (sep > -1)
|
||||
filename = filename.substring (sep+1);
|
||||
}
|
||||
if (filename != null) {
|
||||
Node node = new Node (filename);
|
||||
node.setContent (newb, type);
|
||||
parts.put (name, node);
|
||||
} else {
|
||||
parts.put (name, new String (newb));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String getSubHeader (String header, String subHeaderName) {
|
||||
if (header == null)
|
||||
return null;
|
||||
String retval = null;
|
||||
StringTokenizer headerTokenizer = new StringTokenizer(header, ";");
|
||||
while (headerTokenizer.hasMoreTokens()) {
|
||||
String token = headerTokenizer.nextToken().trim ();
|
||||
int i = token.indexOf ("=");
|
||||
if (i > 0) {
|
||||
String hname = token.substring (0, i).trim ();
|
||||
if (hname.equalsIgnoreCase (subHeaderName))
|
||||
retval = token.substring (i+1).replace ('"', ' ').trim ();
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
}
|
126
src/helma/util/UrlEncoder.java
Normal file
126
src/helma/util/UrlEncoder.java
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* @(#)URLEncoder.java 1.12 98/07/01
|
||||
*
|
||||
* Copyright 1995-1998 by Sun Microsystems, Inc.,
|
||||
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is the confidential and proprietary information
|
||||
* of Sun Microsystems, Inc. ("Confidential Information"). You
|
||||
* shall not disclose such Confidential Information and shall use
|
||||
* it only in accordance with the terms of the license agreement
|
||||
* you entered into with Sun.
|
||||
*/
|
||||
|
||||
// Repackaged by Hannes Wallnoefer because this is the only way to
|
||||
// encode space as %20 (no, subclassing doesn't work here).
|
||||
|
||||
package helma.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.BitSet;
|
||||
|
||||
/**
|
||||
* The class contains a utility method for converting a
|
||||
* <code>String</code> into a MIME format called
|
||||
* "<code>x-www-form-urlencoded</code>" format.
|
||||
* <p>
|
||||
* To convert a <code>String</code>, each character is examined in turn:
|
||||
* <ul>
|
||||
* <li>The ASCII characters '<code>a</code>' through '<code>z</code>',
|
||||
* '<code>A</code>' through '<code>Z</code>', and '<code>0</code>'
|
||||
* through '<code>9</code>' remain the same.
|
||||
* <li>All other characters are converted into the 3-character string
|
||||
* "<code>%<i>xy</i></code>", where <i>xy</i> is the two-digit
|
||||
* hexadecimal representation of the lower 8-bits of the character.
|
||||
* </ul>
|
||||
*
|
||||
* @author Herb Jellinek
|
||||
* @version 1.12, 07/01/98
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public class UrlEncoder {
|
||||
static BitSet dontNeedEncoding;
|
||||
static final int caseDiff = ('a' - 'A');
|
||||
|
||||
/* The list of characters that are not encoded have been determined by
|
||||
referencing O'Reilly's "HTML: The Definitive Guide" (page 164). */
|
||||
|
||||
static {
|
||||
dontNeedEncoding = new BitSet(256);
|
||||
int i;
|
||||
for (i = 'a'; i <= 'z'; i++) {
|
||||
dontNeedEncoding.set(i);
|
||||
}
|
||||
for (i = 'A'; i <= 'Z'; i++) {
|
||||
dontNeedEncoding.set(i);
|
||||
}
|
||||
for (i = '0'; i <= '9'; i++) {
|
||||
dontNeedEncoding.set(i);
|
||||
}
|
||||
// dontNeedEncoding.set(' '); /* removed to encode space as %20 */
|
||||
dontNeedEncoding.set('-');
|
||||
dontNeedEncoding.set('_');
|
||||
dontNeedEncoding.set('.');
|
||||
dontNeedEncoding.set('*');
|
||||
}
|
||||
|
||||
/**
|
||||
* You can't call the constructor.
|
||||
*/
|
||||
// private URLEncoder() { }
|
||||
|
||||
/**
|
||||
* Translates a string into <code>x-www-form-urlencoded</code> format.
|
||||
*
|
||||
* @param s <code>String</code> to be translated.
|
||||
* @return the translated <code>String</code>.
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public static String encode(String s) {
|
||||
int maxBytesPerChar = 10;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(s.length());
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
|
||||
OutputStreamWriter writer = new OutputStreamWriter(buf);
|
||||
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
int c = (int)s.charAt(i);
|
||||
if (dontNeedEncoding.get(c)) {
|
||||
if (c == ' ') {
|
||||
c = '+';
|
||||
}
|
||||
out.write(c);
|
||||
} else {
|
||||
// convert to external encoding before hex conversion
|
||||
try {
|
||||
writer.write(c);
|
||||
writer.flush();
|
||||
} catch(IOException e) {
|
||||
buf.reset();
|
||||
continue;
|
||||
}
|
||||
byte[] ba = buf.toByteArray();
|
||||
for (int j = 0; j < ba.length; j++) {
|
||||
out.write('%');
|
||||
char ch = Character.forDigit((ba[j] >> 4) & 0xF, 16);
|
||||
// converting to use uppercase letter as part of
|
||||
// the hex value if ch is a letter.
|
||||
if (Character.isLetter(ch)) {
|
||||
ch -= caseDiff;
|
||||
}
|
||||
out.write(ch);
|
||||
ch = Character.forDigit(ba[j] & 0xF, 16);
|
||||
if (Character.isLetter(ch)) {
|
||||
ch -= caseDiff;
|
||||
}
|
||||
out.write(ch);
|
||||
}
|
||||
buf.reset();
|
||||
}
|
||||
}
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
}
|
350
src/helma/util/WebBroadcaster.java
Normal file
350
src/helma/util/WebBroadcaster.java
Normal file
|
@ -0,0 +1,350 @@
|
|||
// WebBroadcaster.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A utility hack to do "html web broadcasts".
|
||||
*/
|
||||
|
||||
public class WebBroadcaster implements Runnable {
|
||||
|
||||
private Vector connections;
|
||||
private ServerSocket serverSocket;
|
||||
private Thread listener;
|
||||
private boolean paranoid;
|
||||
static String lastResult = "";
|
||||
private Vector accept, deny;
|
||||
static String reloadJS = "<html>\r\n<head><SCRIPT language=JavaScript>\r\n<!--\r\nfunction reload (url) {\r\n window.location.href=url;\r\n}\r\n//-->\r\n</SCRIPT>\r\n";
|
||||
static String scrollJS = "<SCRIPT language=JavaScript>\r\n<!--\r\nfunction scroller () {\r\n window.scroll(1, 500000);\r\n window.setTimeout(\"scroller()\", 100);\r\n}\r\nscroller();\r\n//-->\r\n</SCRIPT>\r\n</head>\r\n<body>\r\n";
|
||||
long time;
|
||||
int last;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static void main (String args[]) {
|
||||
System.out.println ("Usage: java helma.util.WebBroadcaster [port]");
|
||||
int p = 8080;
|
||||
if (args.length > 0) try {
|
||||
p = Integer.parseInt (args[0]);
|
||||
} catch (NumberFormatException nfx) {
|
||||
System.out.println ("Error parsing port number: "+args[0]);
|
||||
}
|
||||
|
||||
try {
|
||||
WebBroadcaster server = new WebBroadcaster (p);
|
||||
// webserver.setParanoid (false);
|
||||
// webserver.acceptClient ("192.168.*.*");
|
||||
System.out.println ("started web broadcast server on port "+p);
|
||||
} catch (IOException x) {
|
||||
System.out.println ("Error creating web broadcast server: "+x);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a Web server at the specified port number.
|
||||
*/
|
||||
public WebBroadcaster (int port) throws IOException {
|
||||
super();
|
||||
connections = new Vector ();
|
||||
accept = new Vector ();
|
||||
deny = new Vector ();
|
||||
// make a new server socket with extra large queue size
|
||||
this.serverSocket = new ServerSocket (port, 2000);
|
||||
listener = new Thread (this);
|
||||
listener.start ();
|
||||
}
|
||||
|
||||
|
||||
public void broadcast (String message) {
|
||||
long start = System.currentTimeMillis ();
|
||||
int l = connections.size ();
|
||||
synchronized (this) {
|
||||
if (l != last) {
|
||||
System.out.println ("broadcasting to "+l+" clients in "+time+" millis.");
|
||||
last = l;
|
||||
}
|
||||
}
|
||||
for (int i=l-1; i>=0; i--) {
|
||||
try {
|
||||
Connection c = (Connection) connections.elementAt (i);
|
||||
c.send (message);
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
time = System.currentTimeMillis () - start;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Switch client filtering on/off.
|
||||
* @see acceptClient(java.lang.String)
|
||||
* @see denyClient(java.lang.String)
|
||||
*/
|
||||
public void setParanoid (boolean p) {
|
||||
paranoid = p;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add an IP address to the list of accepted clients. The parameter can contain '*' as wildcard
|
||||
* character, e.g. "192.168.*.*". You must call setParanoid(true) in order for this to have any
|
||||
* effect.
|
||||
*
|
||||
* @see denyClient(java.lang.String)
|
||||
* @see setParanoid(boolean)
|
||||
*/
|
||||
public void acceptClient (String address) throws IllegalArgumentException {
|
||||
try {
|
||||
AddressMatcher m = new AddressMatcher (address);
|
||||
accept.addElement (m);
|
||||
} catch (Exception x) {
|
||||
throw new IllegalArgumentException ("\""+address+"\" does not represent a valid IP address");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an IP address to the list of denied clients. The parameter can contain '*' as wildcard
|
||||
* character, e.g. "192.168.*.*". You must call setParanoid(true) in order for this to have any
|
||||
* effect.
|
||||
*
|
||||
* @see acceptClient(java.lang.String)
|
||||
* @see setParanoid(boolean)
|
||||
*/
|
||||
public void denyClient (String address) throws IllegalArgumentException {
|
||||
try {
|
||||
AddressMatcher m = new AddressMatcher (address);
|
||||
deny.addElement (m);
|
||||
} catch (Exception x) {
|
||||
throw new IllegalArgumentException ("\""+address+"\" does not represent a valid IP address");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkSocket (Socket s) {
|
||||
int l = deny.size ();
|
||||
byte address[] = s.getInetAddress ().getAddress ();
|
||||
for (int i=0; i<l; i++) {
|
||||
AddressMatcher match = (AddressMatcher) deny.elementAt (i);
|
||||
if (match.matches (address))
|
||||
return false;
|
||||
}
|
||||
l = accept.size ();
|
||||
for (int i=0; i<l; i++) {
|
||||
AddressMatcher match = (AddressMatcher) accept.elementAt (i);
|
||||
if (match.matches (address))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for client requests until stopped.
|
||||
*/
|
||||
public void run() {
|
||||
Thread current = Thread.currentThread ();
|
||||
try {
|
||||
while (listener == current) {
|
||||
Socket socket = serverSocket.accept();
|
||||
try {
|
||||
// if (!paranoid || checkSocket (socket))
|
||||
new Connection (socket);
|
||||
// else
|
||||
// socket.close ();
|
||||
} catch (Exception x) {
|
||||
System.out.println ("Error in listener: "+x);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception) {
|
||||
System.err.println("Error accepting Web connections (" + exception + ").");
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
serverSocket.close();
|
||||
serverSocket = null;
|
||||
}
|
||||
catch (IOException ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
class Connection implements Runnable {
|
||||
|
||||
private Socket socket;
|
||||
private InputStream input;
|
||||
private BufferedWriter output;
|
||||
private Thread responder;
|
||||
|
||||
public Connection (Socket socket) throws IOException {
|
||||
super();
|
||||
this.socket = socket;
|
||||
socket.setSoTimeout (30000);
|
||||
input = new BufferedInputStream (socket.getInputStream());
|
||||
output = new BufferedWriter (new OutputStreamWriter(socket.getOutputStream()));
|
||||
responder = new Thread (this);
|
||||
responder.start();
|
||||
}
|
||||
|
||||
/* public void run () {
|
||||
Thread current = Thread.currentThread ();
|
||||
if (current == cleaner) {
|
||||
cleanup ();
|
||||
} else if (current == responder) {
|
||||
respond ();
|
||||
}
|
||||
} */
|
||||
|
||||
public void run () {
|
||||
|
||||
boolean newConnection = false;
|
||||
|
||||
try {
|
||||
|
||||
DataInputStream reader = new DataInputStream (input);
|
||||
boolean keepalive = false;
|
||||
int cycle = 0;
|
||||
// implement keep-alive connections
|
||||
do {
|
||||
// if (cycle > 0) System.out.println ("Reusing connection: "+cycle);
|
||||
String line = reader.readLine();
|
||||
if (line == null) throw new IOException ("connection reset");
|
||||
int contentLength = 0;
|
||||
StringTokenizer tokens = new StringTokenizer(line);
|
||||
String method = tokens.nextToken();
|
||||
String uri = tokens.nextToken ();
|
||||
String httpversion = tokens.nextToken ();
|
||||
keepalive = "HTTP/1.1".equals (httpversion);
|
||||
|
||||
do {
|
||||
// System.out.println (line);
|
||||
line = reader.readLine();
|
||||
if (line != null) {
|
||||
line = line.toLowerCase ();
|
||||
if (line.startsWith ("content-length:"))
|
||||
contentLength = Integer.parseInt (line.substring (15).trim ());
|
||||
if (line.startsWith ("connection:"))
|
||||
keepalive = line.indexOf ("keep-alive") > -1;
|
||||
}
|
||||
} while (line != null && ! line.equals(""));
|
||||
// System.out.println ("");
|
||||
|
||||
if ("GET".equals (method)) {
|
||||
|
||||
output.write (httpversion+" 200 OK\r\n");
|
||||
output.write ("Server: helma.WebBroadcast\r\n");
|
||||
output.write ("Content-Type: text/html\r\n");
|
||||
newConnection = uri.startsWith ("/NEW");
|
||||
if (!newConnection) {
|
||||
output.write ("Content-Length: 5\r\n");
|
||||
if (keepalive)
|
||||
output.write ("Connection: keep-alive\r\n");
|
||||
output.write ("\r\n");
|
||||
output.write ("done.");
|
||||
output.flush ();
|
||||
cycle += 1;
|
||||
if (uri.startsWith ("/MSG"))
|
||||
broadcast (uri+"<br>\r\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
output.write ("Connection: close\r\n");
|
||||
output.write ("\r\n");
|
||||
output.write (reloadJS);
|
||||
output.write (scrollJS);
|
||||
output.flush ();
|
||||
|
||||
connections.addElement (this);
|
||||
|
||||
} else {
|
||||
output.write ("HTTP/1.0 400 Bad Request\r\n");
|
||||
output.write ("Server: helma.WebBroadcast\r\n\r\n");
|
||||
output.write ("Bad Request.");
|
||||
// output.write (lastResult);
|
||||
output.flush ();
|
||||
keepalive = false;
|
||||
}
|
||||
} while (keepalive && !newConnection);
|
||||
} catch (Exception x) {
|
||||
System.out.print (".");
|
||||
} finally {
|
||||
if (newConnection) // leave connection open
|
||||
return;
|
||||
try {
|
||||
output.close();
|
||||
} catch (IOException ignore) {}
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException ignore) {}
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanup () {
|
||||
|
||||
}
|
||||
|
||||
public synchronized void send (String message) {
|
||||
try {
|
||||
output.write (message);
|
||||
output.flush ();
|
||||
} catch (Exception exception) {
|
||||
try {
|
||||
connections.removeElement (this);
|
||||
} catch (Exception ignore) {}
|
||||
try {
|
||||
output.close();
|
||||
} catch (IOException ignore) {}
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException ignore) {}
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String toString () {
|
||||
return socket.getInetAddress ().getHostName ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class AddressMatcher {
|
||||
|
||||
int pattern[];
|
||||
|
||||
public AddressMatcher (String address) throws Exception {
|
||||
pattern = new int[4];
|
||||
StringTokenizer st = new StringTokenizer (address, ".");
|
||||
if (st.countTokens () != 4)
|
||||
throw new Exception ("\""+address+"\" does not represent a valid IP address");
|
||||
for (int i=0; i<4; i++) {
|
||||
String next = st.nextToken ();
|
||||
if ("*".equals (next))
|
||||
pattern[i] = 256;
|
||||
else
|
||||
pattern[i] = (byte) Integer.parseInt (next);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matches (byte address[]) {
|
||||
for (int i=0; i<4; i++) {
|
||||
if (pattern[i] > 255) // wildcard
|
||||
continue;
|
||||
if (pattern[i] != address[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
20
src/helma/xmlrpc/AuthenticatedXmlRpcHandler.java
Normal file
20
src/helma/xmlrpc/AuthenticatedXmlRpcHandler.java
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2000 Hannes Wallnoefer
|
||||
*/
|
||||
|
||||
package helma.xmlrpc;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* An XML-RPC handler that also handles user authentication.
|
||||
*/
|
||||
|
||||
public interface AuthenticatedXmlRpcHandler {
|
||||
|
||||
/**
|
||||
* Return the result, or throw an Exception if something went wrong.
|
||||
*/
|
||||
public Object execute (String method, Vector params, String user, String password) throws Exception;
|
||||
|
||||
}
|
130
src/helma/xmlrpc/Base64.java
Normal file
130
src/helma/xmlrpc/Base64.java
Normal file
|
@ -0,0 +1,130 @@
|
|||
package helma.xmlrpc;
|
||||
|
||||
/**
|
||||
* Provides encoding of raw bytes to base64-encoded characters, and
|
||||
* decoding of base64 characters to raw bytes.
|
||||
*
|
||||
* @author Kevin Kelley (kelley@iguana.ruralnet.net)
|
||||
* @version 1.0
|
||||
* @date 06 August 1998
|
||||
*/
|
||||
|
||||
public class Base64
|
||||
{
|
||||
/**
|
||||
* returns an array of base64-encoded characters to represent the
|
||||
* passed data array.
|
||||
*
|
||||
* @param data the array of bytes to encode
|
||||
* @return base64-coded character array.
|
||||
*/
|
||||
|
||||
static public char[] encode(byte[] data) {
|
||||
|
||||
char[] out = new char[((data.length + 2) / 3) * 4];
|
||||
//
|
||||
// 3 bytes encode to 4 chars. Output is always an even
|
||||
// multiple of 4 characters.
|
||||
//
|
||||
|
||||
for (int i = 0, index = 0; i < data.length; i += 3,
|
||||
index += 4) {
|
||||
boolean quad = false;
|
||||
boolean trip = false;
|
||||
int val = (0xFF & (int) data[i]);
|
||||
|
||||
val <<= 8;
|
||||
if ((i + 1) < data.length) {
|
||||
val |= (0xFF & (int) data[i + 1]);
|
||||
trip = true;
|
||||
}
|
||||
val <<= 8;
|
||||
if ((i + 2) < data.length) {
|
||||
val |= (0xFF & (int) data[i + 2]);
|
||||
quad = true;
|
||||
}
|
||||
out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
|
||||
val >>= 6;
|
||||
out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
|
||||
val >>= 6;
|
||||
out[index + 1] = alphabet[val & 0x3F];
|
||||
val >>= 6;
|
||||
out[index + 0] = alphabet[val & 0x3F];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of bytes which were encoded in the passed
|
||||
* character array.
|
||||
*
|
||||
* @param data the array of base64-encoded characters
|
||||
* @return decoded data array
|
||||
*/
|
||||
static public byte[] decode(byte[] data) {
|
||||
int len = ((data.length + 3) / 4) * 3;
|
||||
if (data.length > 0 && data[data.length - 1] == '=')
|
||||
--len;
|
||||
if (data.length > 1 && data[data.length - 2] == '=')
|
||||
--len;
|
||||
byte[] out = new byte[len];
|
||||
|
||||
int shift = 0; // # of excess bits stored in accum
|
||||
int accum = 0; // excess bits
|
||||
int index = 0;
|
||||
|
||||
for (int ix = 0; ix < data.length; ix++) {
|
||||
int value = codes[data[ix] & 0xFF]; // ignore high byte of char
|
||||
if (value >= 0) {
|
||||
// skip over non-code
|
||||
accum <<= 6; // bits shift up by 6 each time thru
|
||||
shift += 6; // loop, with new bits being put in
|
||||
accum |= value; // at the bottom.
|
||||
if (shift >= 8) {
|
||||
// whenever there are 8 or more shifted in,
|
||||
shift -= 8; // write them out (from the top, leaving any
|
||||
// excess at the bottom for next iteration.
|
||||
out[index++] = (byte)((accum >> shift) & 0xff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (index != out.length) throw new RuntimeException("Error decoding BASE64 element: miscalculated data length!");
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
//
|
||||
// code characters for values 0..63
|
||||
//
|
||||
|
||||
static private char[] alphabet =
|
||||
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
|
||||
.toCharArray();
|
||||
|
||||
//
|
||||
// lookup table for converting base64 characters to value in range 0..63
|
||||
//
|
||||
|
||||
static private byte[] codes = new byte[256];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < 256; i++)
|
||||
codes[i] = -1;
|
||||
|
||||
for (int i = 'A'; i <= 'Z'; i++)
|
||||
codes[i] = (byte)(i - 'A');
|
||||
|
||||
for (int i = 'a'; i <= 'z'; i++)
|
||||
codes[i] = (byte)(26 + i - 'a');
|
||||
|
||||
for (int i = '0'; i <= '9'; i++)
|
||||
codes[i] = (byte)(52 + i - '0');
|
||||
|
||||
codes['+'] = 62;
|
||||
codes['/'] = 63;
|
||||
}
|
||||
}
|
104
src/helma/xmlrpc/Benchmark.java
Normal file
104
src/helma/xmlrpc/Benchmark.java
Normal file
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* Copyright 1999 Hannes Wallnoefer
|
||||
*/
|
||||
|
||||
package helma.xmlrpc;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Benchmark implements Runnable {
|
||||
|
||||
XmlRpcClient client;
|
||||
static String url;
|
||||
static int clients = 8;
|
||||
int gCalls = 0, gErrors = 0;
|
||||
|
||||
Date date;
|
||||
|
||||
public Benchmark () throws Exception {
|
||||
client = new XmlRpcClientLite (url);
|
||||
|
||||
Vector args = new Vector ();
|
||||
// Some JITs (Symantec, IBM) have problems with several Threads
|
||||
// starting all at the same time.
|
||||
// This initial XML-RPC call seems to pacify them.
|
||||
args.addElement (new Integer (123));
|
||||
client.execute ("math.abs", args);
|
||||
date = new Date ();
|
||||
date = new Date ((date.getTime()/1000)*1000);
|
||||
|
||||
for (int i=0; i<clients; i++)
|
||||
new Thread (this).start ();
|
||||
}
|
||||
|
||||
public void run () {
|
||||
int errors = 0;
|
||||
int calls = 0;
|
||||
long start = System.currentTimeMillis ();
|
||||
try {
|
||||
int val = (int) (-100 * Math.random ());
|
||||
Vector args = new Vector ();
|
||||
|
||||
// ECHO STRING
|
||||
// args.addElement (Integer.toString (val));
|
||||
// ABS INT
|
||||
args.addElement (new Integer (val));
|
||||
// ECHO DATE
|
||||
// args.addElement (date);
|
||||
|
||||
for (int i=0; i<100; i++) {
|
||||
|
||||
// ABS INT
|
||||
Integer ret = (Integer) client.execute ("math.abs", args);
|
||||
// ECHO
|
||||
// Vector v = (Vector) client.execute ("echo", args);
|
||||
// ECHO DATE
|
||||
// Date d = (Date) v.elementAt (0);
|
||||
|
||||
// ABS INT
|
||||
if (ret.intValue () != Math.abs (val)) {
|
||||
// ECHO DATE
|
||||
// if (date.getTime() != d.getTime()) {
|
||||
// ECHO STRING
|
||||
// if (!Integer.toString(val).equals (v.elementAt (0))) {
|
||||
errors += 1;
|
||||
}
|
||||
calls += 1;
|
||||
}
|
||||
} catch (IOException x) {
|
||||
System.err.println ("Exception in client: "+x);
|
||||
x.printStackTrace ();
|
||||
} catch (XmlRpcException x) {
|
||||
System.err.println ("Server reported error: "+x);
|
||||
} catch (Exception other) {
|
||||
System.err.println ("Exception in Benchmark client: "+other);
|
||||
}
|
||||
int millis = (int) (System.currentTimeMillis () - start);
|
||||
checkout (calls, errors, millis);
|
||||
}
|
||||
|
||||
private synchronized void checkout (int calls, int errors, int millis) {
|
||||
clients--;
|
||||
gCalls += calls;
|
||||
gErrors += errors;
|
||||
System.err.println ("Benchmark thread finished: "+calls+" calls, "+errors+" errors in "+millis+" milliseconds.");
|
||||
if (clients == 0) {
|
||||
System.err.println ("");
|
||||
System.err.println ("Benchmark result: "+(1000*gCalls/millis)+" calls per second.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main (String args[]) throws Exception {
|
||||
if (args.length > 0 && args.length < 3) {
|
||||
url = args[0];
|
||||
XmlRpc.setKeepAlive (true);
|
||||
if (args.length == 2)
|
||||
XmlRpc.setDriver (args[1]);
|
||||
new Benchmark ();
|
||||
} else {
|
||||
System.err.println ("Usage: java helma.xmlrpc.Benchmark URL [SAXDriver]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
61
src/helma/xmlrpc/ServerInputStream.java
Normal file
61
src/helma/xmlrpc/ServerInputStream.java
Normal file
|
@ -0,0 +1,61 @@
|
|||
package helma.xmlrpc;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
// This class is borrowed from Apache JServ
|
||||
class ServerInputStream extends InputStream {
|
||||
// bytes remaining to be read from the input stream. This is
|
||||
// initialized from CONTENT_LENGTH (or getContentLength()).
|
||||
// This is used in order to correctly return a -1 when all the
|
||||
// data POSTed was read. If this is left to -1, content length is
|
||||
// assumed as unknown and the standard InputStream methods will be used
|
||||
long available = -1;
|
||||
|
||||
private InputStream in;
|
||||
|
||||
public ServerInputStream(InputStream in, int available) {
|
||||
this.in = in;
|
||||
this.available = available;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (available > 0) {
|
||||
available--;
|
||||
return in.read();
|
||||
} else if (available == -1)
|
||||
return in.read ();
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int read(byte b[]) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
public int read(byte b[], int off, int len) throws IOException {
|
||||
if (available > 0) {
|
||||
if (len > available) {
|
||||
// shrink len
|
||||
len = (int) available;
|
||||
}
|
||||
int read = in.read(b, off, len);
|
||||
if (read != -1) {
|
||||
available -= read;
|
||||
} else {
|
||||
available = -1;
|
||||
}
|
||||
return read;
|
||||
} else if (available == -1)
|
||||
return in.read (b, off, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long skip(long n) throws IOException {
|
||||
long skip = in.skip(n);
|
||||
if (available > 0)
|
||||
available -= skip;
|
||||
return skip;
|
||||
}
|
||||
|
||||
|
||||
}
|
448
src/helma/xmlrpc/WebServer.java
Normal file
448
src/helma/xmlrpc/WebServer.java
Normal file
|
@ -0,0 +1,448 @@
|
|||
package helma.xmlrpc;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A minimal web server that exclusively handles XML-RPC requests.
|
||||
*/
|
||||
public class WebServer implements Runnable {
|
||||
|
||||
XmlRpcServer xmlrpc;
|
||||
private ServerSocket serverSocket;
|
||||
private int port;
|
||||
private Thread listener;
|
||||
private boolean paranoid;
|
||||
private Vector accept, deny;
|
||||
private Stack threadpool;
|
||||
private ThreadGroup runners;
|
||||
|
||||
|
||||
static final byte[] ctype = "Content-Type: text/xml\r\n".getBytes();
|
||||
static final byte[] clength = "Content-Length: ".getBytes();
|
||||
static final byte[] newline = "\r\n".getBytes();
|
||||
static final byte[] doubleNewline = "\r\n\r\n".getBytes();
|
||||
static final byte[] conkeep = "Connection: Keep-Alive\r\n".getBytes();
|
||||
static final byte[] conclose = "Connection: close\r\n".getBytes();
|
||||
static final byte[] ok = " 200 OK\r\n".getBytes();
|
||||
static final byte[] server = "Server: Helma XML-RPC 1.0\r\n".getBytes();
|
||||
|
||||
|
||||
/**
|
||||
* This <em>can</em> be called from command line, but you'll have to edit and recompile
|
||||
* to change the server port or handler objects. By default, it sets up the following responders:
|
||||
* <ul><li> A java.lang.String object
|
||||
* <li> The java.lang.Math class (making its static methods callable via XML-RPC)
|
||||
* <li> An Echo handler that returns the argument array
|
||||
* </ul>
|
||||
*/
|
||||
public static void main (String args[]) {
|
||||
System.err.println ("Usage: java helma.xmlrpc.WebServer [port]");
|
||||
int p = 8080;
|
||||
if (args.length > 0) try {
|
||||
p = Integer.parseInt (args[0]);
|
||||
} catch (NumberFormatException nfx) {
|
||||
System.err.println ("Error parsing port number: "+args[0]);
|
||||
}
|
||||
// XmlRpc.setDebug (true);
|
||||
XmlRpc.setKeepAlive (true);
|
||||
// XmlRpc.setEncoding ("UTF-8");
|
||||
try {
|
||||
WebServer webserver = new WebServer (p);
|
||||
// webserver.setParanoid (true);
|
||||
// webserver.acceptClient ("192.168.*.*");
|
||||
webserver.addHandler ("string", "Welcome to XML-RPC!");
|
||||
webserver.addHandler ("math", Math.class);
|
||||
webserver.addHandler ("auth", new AuthDemo());
|
||||
webserver.addHandler ("$default", new Echo());
|
||||
// XmlRpcClients can be used as Proxies in XmlRpcServers which is a cool feature for applets.
|
||||
webserver.addHandler ("mttf", new XmlRpcClient ("http://www.mailtothefuture.com:80/RPC2"));
|
||||
System.err.println ("started web server on port "+p);
|
||||
} catch (IOException x) {
|
||||
System.err.println ("Error creating web server: "+x);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a Web server at the specified port number.
|
||||
*/
|
||||
public WebServer (int port) throws IOException {
|
||||
this (port, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a Web server at the specified port number and IP address.
|
||||
*/
|
||||
public WebServer (int port, InetAddress add) throws IOException {
|
||||
this.port = port;
|
||||
xmlrpc = new XmlRpcServer ();
|
||||
accept = new Vector ();
|
||||
deny = new Vector ();
|
||||
threadpool = new Stack ();
|
||||
runners = new ThreadGroup ("XML-RPC Runner");
|
||||
this.serverSocket = new ServerSocket (port, 50, add);
|
||||
listener = new Thread (this, "XML-RPC Weblistener");
|
||||
listener.start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register a handler object with this name. Methods of this objects will be
|
||||
* callable over XML-RPC as "name.method".
|
||||
*/
|
||||
public void addHandler (String name, Object target) {
|
||||
xmlrpc.addHandler (name, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a handler object that was previously registered with this server.
|
||||
*/
|
||||
public void removeHandler (String name) {
|
||||
xmlrpc.removeHandler (name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch client filtering on/off.
|
||||
* @see acceptClient(java.lang.String)
|
||||
* @see denyClient(java.lang.String)
|
||||
*/
|
||||
public void setParanoid (boolean p) {
|
||||
paranoid = p;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add an IP address to the list of accepted clients. The parameter can contain '*' as wildcard
|
||||
* character, e.g. "192.168.*.*". You must call setParanoid(true) in order for this to have any
|
||||
* effect.
|
||||
*
|
||||
* @see denyClient(java.lang.String)
|
||||
* @see setParanoid(boolean)
|
||||
*/
|
||||
public void acceptClient (String address) throws IllegalArgumentException {
|
||||
try {
|
||||
AddressMatcher m = new AddressMatcher (address);
|
||||
accept.addElement (m);
|
||||
} catch (Exception x) {
|
||||
throw new IllegalArgumentException ("\""+address+"\" does not represent a valid IP address");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an IP address to the list of denied clients. The parameter can contain '*' as wildcard
|
||||
* character, e.g. "192.168.*.*". You must call setParanoid(true) in order for this to have any
|
||||
* effect.
|
||||
*
|
||||
* @see acceptClient(java.lang.String)
|
||||
* @see setParanoid(boolean)
|
||||
*/
|
||||
public void denyClient (String address) throws IllegalArgumentException {
|
||||
try {
|
||||
AddressMatcher m = new AddressMatcher (address);
|
||||
deny.addElement (m);
|
||||
} catch (Exception x) {
|
||||
throw new IllegalArgumentException ("\""+address+"\" does not represent a valid IP address");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkSocket (Socket s) {
|
||||
int l = deny.size ();
|
||||
byte address[] = s.getInetAddress ().getAddress ();
|
||||
for (int i=0; i<l; i++) {
|
||||
AddressMatcher match = (AddressMatcher) deny.elementAt (i);
|
||||
if (match.matches (address))
|
||||
return false;
|
||||
}
|
||||
l = accept.size ();
|
||||
for (int i=0; i<l; i++) {
|
||||
AddressMatcher match = (AddressMatcher) accept.elementAt (i);
|
||||
if (match.matches (address))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for client requests until stopped.
|
||||
*/
|
||||
public void run() {
|
||||
try {
|
||||
while (listener != null) {
|
||||
try {
|
||||
Socket socket = serverSocket.accept();
|
||||
if (!paranoid || checkSocket (socket)) {
|
||||
Runner runner = getRunner ();
|
||||
runner.handle (socket);
|
||||
// new Connection (socket);
|
||||
} else
|
||||
socket.close ();
|
||||
} catch (Exception ex) {
|
||||
System.err.println("Exception in XML-RPC listener loop (" + ex + ").");
|
||||
} catch (Error err) {
|
||||
System.err.println("Error in XML-RPC listener loop (" + err + ").");
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
System.err.println("Error accepting XML-RPC connections (" + exception + ").");
|
||||
}
|
||||
finally {
|
||||
System.err.println("Closing XML-RPC server socket.");
|
||||
try {
|
||||
serverSocket.close();
|
||||
serverSocket = null;
|
||||
}
|
||||
catch (IOException ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stop listening on the server port.
|
||||
*/
|
||||
public void shutdown () {
|
||||
if (listener != null) {
|
||||
Thread l = listener;
|
||||
listener = null;
|
||||
l.interrupt ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Runner getRunner () {
|
||||
try {
|
||||
return (Runner) threadpool.pop ();
|
||||
} catch (EmptyStackException empty) {
|
||||
if (runners.activeCount () > 255)
|
||||
throw new RuntimeException ("System overload");
|
||||
return new Runner ();
|
||||
}
|
||||
}
|
||||
|
||||
void releaseRunner (Runner runner) {
|
||||
threadpool.push (runner);
|
||||
}
|
||||
|
||||
class Runner implements Runnable {
|
||||
|
||||
Thread thread;
|
||||
Connection con;
|
||||
int count;
|
||||
|
||||
public void handle (Socket socket) throws IOException {
|
||||
con = new Connection (socket);
|
||||
count = 0;
|
||||
if (thread == null || !thread.isAlive()) {
|
||||
thread = new Thread (runners, this);
|
||||
thread.start ();
|
||||
} else {
|
||||
synchronized (this) {
|
||||
notify ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void run () {
|
||||
while (Thread.currentThread () == thread) {
|
||||
con.run ();
|
||||
count++;
|
||||
con = null;
|
||||
|
||||
if (count > 200 || threadpool.size() > 20)
|
||||
return;
|
||||
|
||||
synchronized (this) {
|
||||
releaseRunner (this);
|
||||
try {
|
||||
wait ();
|
||||
} catch (InterruptedException ir) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end class Runner
|
||||
|
||||
|
||||
class Connection implements Runnable {
|
||||
|
||||
private Socket socket;
|
||||
private BufferedInputStream input;
|
||||
private BufferedOutputStream output;
|
||||
// private Thread responder;
|
||||
private long lastRequest;
|
||||
private String user, password;
|
||||
|
||||
public Connection (Socket socket) throws IOException {
|
||||
// set read timeout to 30 seconds
|
||||
socket.setSoTimeout (30000);
|
||||
|
||||
this.socket = socket;
|
||||
input = new BufferedInputStream (socket.getInputStream());
|
||||
output = new BufferedOutputStream (socket.getOutputStream());
|
||||
// responder = new Thread (this, "xmlrpc-worker");
|
||||
// responder.start();
|
||||
}
|
||||
|
||||
|
||||
public void run () {
|
||||
try {
|
||||
boolean keepalive = false;
|
||||
|
||||
do {
|
||||
// reset user authentication
|
||||
user = password = null;
|
||||
String line = readLine ();
|
||||
// Netscape sends an extra \n\r after bodypart, swallow it
|
||||
if ("".equals (line))
|
||||
line = readLine();
|
||||
if (XmlRpc.debug)
|
||||
System.err.println (line);
|
||||
// get time of last request
|
||||
lastRequest = System.currentTimeMillis ();
|
||||
int contentLength = -1;
|
||||
|
||||
// tokenize first line of HTTP request
|
||||
StringTokenizer tokens = new StringTokenizer(line);
|
||||
String method = tokens.nextToken();
|
||||
String uri = tokens.nextToken ();
|
||||
String httpversion = tokens.nextToken ();
|
||||
keepalive = XmlRpc.getKeepAlive() && "HTTP/1.1".equals (httpversion);
|
||||
do {
|
||||
line = readLine();
|
||||
if (line != null) {
|
||||
if (XmlRpc.debug)
|
||||
System.err.println (line);
|
||||
String lineLower = line.toLowerCase ();
|
||||
if (lineLower.startsWith ("content-length:"))
|
||||
contentLength = Integer.parseInt (line.substring (15).trim ());
|
||||
if (lineLower.startsWith ("connection:"))
|
||||
keepalive = XmlRpc.getKeepAlive() && lineLower.indexOf ("keep-alive") > -1;
|
||||
if (lineLower.startsWith ("authorization: basic "))
|
||||
parseAuth (line);
|
||||
}
|
||||
} while (line != null && ! line.equals(""));
|
||||
|
||||
if ("POST".equalsIgnoreCase (method)) {
|
||||
ServerInputStream sin = new ServerInputStream (input, contentLength);
|
||||
byte result[] = xmlrpc.execute (sin, user, password);
|
||||
output.write (httpversion.getBytes());
|
||||
output.write (ok);
|
||||
output.write (server);
|
||||
if (keepalive)
|
||||
output.write (conkeep);
|
||||
else
|
||||
output.write (conclose);
|
||||
output.write (ctype);
|
||||
output.write (clength);
|
||||
output.write (Integer.toString (result.length).getBytes());
|
||||
output.write (doubleNewline);
|
||||
output.write (result);
|
||||
output.flush ();
|
||||
} else {
|
||||
output.write (httpversion.getBytes());
|
||||
output.write (" 400 Bad Request\r\n".getBytes());
|
||||
output.write ("Server: helma.XML-RPC\r\n\r\n".getBytes());
|
||||
output.write (("Method "+method+" not implemented (try POST)").getBytes());
|
||||
output.flush ();
|
||||
keepalive = false;
|
||||
}
|
||||
} while (keepalive);
|
||||
} catch (Exception exception) {
|
||||
if (XmlRpc.debug) {
|
||||
System.err.println (exception);
|
||||
exception.printStackTrace ();
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] buffer;
|
||||
private String readLine () throws IOException {
|
||||
if (buffer == null) {
|
||||
buffer = new byte[512];
|
||||
}
|
||||
int next;
|
||||
int count = 0;
|
||||
for (;;) {
|
||||
next = input.read();
|
||||
if (next < 0 || next == '\n')
|
||||
break;
|
||||
if (next != '\r') {
|
||||
buffer[count++] = (byte) next;
|
||||
}
|
||||
if (count >= 512)
|
||||
throw new IOException ("HTTP Header too long");
|
||||
}
|
||||
return new String (buffer, 0, count);
|
||||
}
|
||||
|
||||
private void parseAuth (String line) {
|
||||
try {
|
||||
byte[] c = Base64.decode (line.substring (21).getBytes());
|
||||
String str = new String (c);
|
||||
int col = str.indexOf (":");
|
||||
user = str.substring (0, col);
|
||||
password = str.substring (col+1);
|
||||
} catch (Throwable ignore) {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class AddressMatcher {
|
||||
|
||||
int pattern[];
|
||||
|
||||
public AddressMatcher (String address) throws Exception {
|
||||
pattern = new int[4];
|
||||
StringTokenizer st = new StringTokenizer (address, ".");
|
||||
if (st.countTokens () != 4)
|
||||
throw new Exception ("\""+address+"\" does not represent a valid IP address");
|
||||
for (int i=0; i<4; i++) {
|
||||
String next = st.nextToken ();
|
||||
if ("*".equals (next))
|
||||
pattern[i] = 256;
|
||||
else
|
||||
pattern[i] = (byte) Integer.parseInt (next);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matches (byte address[]) {
|
||||
for (int i=0; i<4; i++) {
|
||||
if (pattern[i] > 255) // wildcard
|
||||
continue;
|
||||
if (pattern[i] != address[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// An echo handler for debugging purposes
|
||||
class Echo implements XmlRpcHandler {
|
||||
public Object execute (String method, Vector v) throws Exception {
|
||||
return (v);
|
||||
}
|
||||
}
|
||||
|
||||
// An simple class that implements authentication
|
||||
class AuthDemo implements AuthenticatedXmlRpcHandler {
|
||||
public Object execute (String method, Vector v, String user, String password) throws Exception {
|
||||
// our simplistic authentication guidelines never fail ;)
|
||||
if (user == null || user.startsWith ("bad"))
|
||||
throw new XmlRpcException (5, "Sorry, you're not allowed in here!");
|
||||
return ("Hello "+user);
|
||||
}
|
||||
}
|
||||
|
583
src/helma/xmlrpc/XmlRpc.java
Normal file
583
src/helma/xmlrpc/XmlRpc.java
Normal file
|
@ -0,0 +1,583 @@
|
|||
/**
|
||||
* Copyright 1999 Hannes Wallnoefer
|
||||
* XML-RPC base class. See http://www.xmlrpc.com/
|
||||
*/
|
||||
|
||||
package helma.xmlrpc;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
import org.xml.sax.*;
|
||||
|
||||
|
||||
/**
|
||||
* This abstract base class provides basic capabilities for XML-RPC, like parsing of parameters
|
||||
* or encoding Java objects into XML-RPC format. Any XML parser with a <a href=http://www.megginson.com/SAX/>
|
||||
* SAX</a> interface can be used.<p>
|
||||
* XmlRpcServer and XmlRpcClient are the classes that actually implement an XML-RCP server and client.
|
||||
* @see XmlRpcServer
|
||||
* @see XmlRpcClient
|
||||
*/
|
||||
|
||||
public abstract class XmlRpc extends HandlerBase {
|
||||
|
||||
public static final String version = "helma XML-RPC 1.0";
|
||||
|
||||
String methodName;
|
||||
|
||||
// class name of SAX parser to use
|
||||
private static Class parserClass;
|
||||
private static Hashtable saxDrivers = new Hashtable ();
|
||||
static {
|
||||
saxDrivers.put ("xp", "com.jclark.xml.sax.Driver");
|
||||
saxDrivers.put ("ibm1", "com.ibm.xml.parser.SAXDriver");
|
||||
saxDrivers.put ("ibm2", "com.ibm.xml.parsers.SAXParser");
|
||||
saxDrivers.put ("aelfred", "com.microstar.xml.SAXDriver");
|
||||
saxDrivers.put ("oracle1", "oracle.xml.parser.XMLParser");
|
||||
saxDrivers.put ("oracle2", "oracle.xml.parser.v2.SAXParser");
|
||||
saxDrivers.put ("openxml", "org.openxml.parser.XMLSAXParser");
|
||||
}
|
||||
|
||||
|
||||
// the stack we're parsing our values into.
|
||||
Stack values;
|
||||
Value currentValue;
|
||||
|
||||
// formats for parsing and generating dateTime values
|
||||
|
||||
// DateFormat datetime;
|
||||
// now comes wapped into a synchronized class because dateFormat is not threadsafe
|
||||
static Formatter dateformat = new Formatter ();
|
||||
|
||||
// used to collect character data of parameter values
|
||||
StringBuffer cdata;
|
||||
boolean readCdata;
|
||||
|
||||
// XML RPC parameter types used for dataMode
|
||||
static final int STRING = 0;
|
||||
static final int INTEGER = 1;
|
||||
static final int BOOLEAN = 2;
|
||||
static final int DOUBLE = 3;
|
||||
static final int DATE = 4;
|
||||
static final int BASE64 = 5;
|
||||
static final int STRUCT = 6;
|
||||
static final int ARRAY = 7;
|
||||
static final int NIL = 8;
|
||||
|
||||
// Error level + message
|
||||
int errorLevel;
|
||||
String errorMsg;
|
||||
|
||||
static final int NONE = 0;
|
||||
static final int RECOVERABLE = 1;
|
||||
static final int FATAL = 2;
|
||||
|
||||
// use HTTP keepalive?
|
||||
static boolean keepalive = false;
|
||||
|
||||
// for debugging output
|
||||
public static boolean debug = false;
|
||||
final static String types[] = {"String", "Integer", "Boolean", "Double", "Date", "Base64", "Struct", "Array", "Nil"};
|
||||
|
||||
// mapping between java encoding names and "real" names used in XML prolog.
|
||||
// if you use an encoding not listed here send feedback to xmlrpc@helma.org
|
||||
|
||||
static String encoding = "ISO8859_1";
|
||||
static Properties encodings = new Properties ();
|
||||
static {
|
||||
encodings.put ("UTF8", "UTF-8");
|
||||
encodings.put ("ISO8859_1", "ISO-8859-1");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the SAX Parser to be used. The argument can either be the full class name or
|
||||
* a user friendly shortcut if the parser is known to this class. The parsers that can
|
||||
* currently be set by shortcut are listed in the main documentation page. If you are using
|
||||
* another parser please send me the name of the SAX driver and I'll include it in a future release.
|
||||
* If setDriver() is never called then the System property "sax.driver" is consulted. If that is not defined
|
||||
* the driver defaults to OpenXML.
|
||||
*/
|
||||
public static void setDriver (String driver) throws ClassNotFoundException {
|
||||
String parserClassName = null;
|
||||
try {
|
||||
parserClassName = (String) saxDrivers.get (driver);
|
||||
if (parserClassName == null)
|
||||
parserClassName = driver;
|
||||
parserClass = Class.forName (parserClassName);
|
||||
} catch (ClassNotFoundException x) {
|
||||
throw new ClassNotFoundException ("SAX driver not found: "+parserClassName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SAX Parser to be used by directly passing the Class object.
|
||||
*/
|
||||
public static void setDriver (Class driver) {
|
||||
parserClass = driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the encoding of the XML. This should be the name of a Java encoding
|
||||
* contained in the encodings Hashtable.
|
||||
*/
|
||||
public static void setEncoding (String enc) {
|
||||
encoding = enc;
|
||||
}
|
||||
|
||||
public String getEncoding () {
|
||||
return encodings.getProperty (encoding, encoding);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Switch debugging output on/off.
|
||||
*/
|
||||
public static void setDebug (boolean val) {
|
||||
debug = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch HTTP keepalive on/off.
|
||||
*/
|
||||
public static void setKeepAlive (boolean val) {
|
||||
keepalive = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* get current HTTP keepalive mode.
|
||||
*/
|
||||
public static boolean getKeepAlive () {
|
||||
return keepalive;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the input stream. For each root level object, method <code>objectParsed</code>
|
||||
* is called.
|
||||
*/
|
||||
synchronized void parse (InputStream is) throws Exception {
|
||||
|
||||
// reset values (XmlRpc objects are reusable)
|
||||
errorLevel = NONE;
|
||||
errorMsg = null;
|
||||
values = new Stack ();
|
||||
if (cdata == null)
|
||||
cdata = new StringBuffer (128);
|
||||
else
|
||||
cdata.setLength (0);
|
||||
readCdata = false;
|
||||
currentValue = null;
|
||||
|
||||
long now = System.currentTimeMillis ();
|
||||
if (parserClass == null) {
|
||||
// try to get the name of the SAX driver from the System properties
|
||||
setDriver (System.getProperty ("sax.driver", "org.openxml.parser.XMLSAXParser"));
|
||||
}
|
||||
|
||||
Parser parser = null;
|
||||
try {
|
||||
parser = (Parser) parserClass.newInstance ();
|
||||
} catch (NoSuchMethodError nsm) {
|
||||
// This is thrown if no constructor exists for the parser class
|
||||
// and is transformed into a regular exception.
|
||||
throw new Exception ("Can't create Parser: "+parserClass);
|
||||
}
|
||||
|
||||
parser.setDocumentHandler (this);
|
||||
parser.setErrorHandler (this);
|
||||
|
||||
parser.parse (new InputSource (is));
|
||||
if (debug)
|
||||
System.err.println ("Spent "+(System.currentTimeMillis () - now)+" millis parsing");
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the XML representation of a supported Java object to the XML writer.
|
||||
*/
|
||||
void writeObject (Object what, XmlWriter writer) {
|
||||
writer.startElement ("value");
|
||||
if (what == null) {
|
||||
// try sending experimental <ni/> element
|
||||
writer.emptyElement ("nil");
|
||||
} else if (what instanceof String) {
|
||||
writer.chardata (what.toString ());
|
||||
} else if (what instanceof Integer) {
|
||||
writer.startElement ("int");
|
||||
writer.write (what.toString ());
|
||||
writer.endElement ("int");
|
||||
} else if (what instanceof Boolean) {
|
||||
writer.startElement ("boolean");
|
||||
writer.write (((Boolean) what).booleanValue () ? "1" : "0");
|
||||
writer.endElement ("boolean");
|
||||
} else if (what instanceof Double || what instanceof Float) {
|
||||
writer.startElement ("double");
|
||||
writer.write (what.toString ());
|
||||
writer.endElement ("double");
|
||||
} else if (what instanceof Date) {
|
||||
writer.startElement ("dateTime.iso8601");
|
||||
Date d = (Date) what;
|
||||
writer.write (dateformat.format (d));
|
||||
writer.endElement ("dateTime.iso8601");
|
||||
} else if (what instanceof byte[]) {
|
||||
writer.startElement ("base64");
|
||||
writer.write (Base64.encode ((byte[]) what));
|
||||
writer.endElement ("base64");
|
||||
} else if (what instanceof Vector) {
|
||||
writer.startElement ("array");
|
||||
writer.startElement ("data");
|
||||
Vector v = (Vector) what;
|
||||
int l2 = v.size ();
|
||||
for (int i2=0; i2<l2; i2++)
|
||||
writeObject (v.elementAt (i2), writer);
|
||||
writer.endElement ("data");
|
||||
writer.endElement ("array");
|
||||
} else if (what instanceof Hashtable) {
|
||||
writer.startElement ("struct");
|
||||
Hashtable h = (Hashtable) what;
|
||||
for (Enumeration e = h.keys (); e.hasMoreElements (); ) {
|
||||
try {
|
||||
String nextkey = (String) e.nextElement ();
|
||||
Object nextval = h.get (nextkey);
|
||||
writer.startElement ("member");
|
||||
writer.startElement ("name");
|
||||
writer.write (nextkey);
|
||||
writer.endElement ("name");
|
||||
writeObject (nextval, writer);
|
||||
writer.endElement ("member");
|
||||
} catch (ClassCastException cce) {
|
||||
throw new ClassCastException ("Only Strings may be used as keys in XML-RPC structs, but Hashtable contained "+cce.getMessage());
|
||||
}
|
||||
}
|
||||
writer.endElement ("struct");
|
||||
} else
|
||||
throw new RuntimeException ("unsupported Java type: " + what.getClass ());
|
||||
writer.endElement ("value");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is called when a root level object has been parsed.
|
||||
*/
|
||||
abstract void objectParsed (Object what);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// methods called by XML parser
|
||||
|
||||
/**
|
||||
* Method called by SAX driver.
|
||||
*/
|
||||
public void characters (char ch[], int start, int length) throws SAXException {
|
||||
if (!readCdata)
|
||||
return;
|
||||
cdata.append (ch, start, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called by SAX driver.
|
||||
*/
|
||||
public void endElement (String name) throws SAXException {
|
||||
|
||||
if (debug)
|
||||
System.err.println ("endElement: "+name);
|
||||
|
||||
// finalize character data, if appropriate
|
||||
if (currentValue != null && readCdata) {
|
||||
currentValue.characterData (cdata.toString ());
|
||||
cdata.setLength (0);
|
||||
readCdata = false;
|
||||
}
|
||||
|
||||
if ("value".equals (name)) {
|
||||
int depth = values.size ();
|
||||
// Only handle top level objects or objects contained in arrays here.
|
||||
// For objects contained in structs, wait for </member> (see code below).
|
||||
if (depth == 0 || values.peek ().hashCode () != STRUCT) {
|
||||
Value v = currentValue;
|
||||
if (depth == 0) {
|
||||
// This is a top-level object
|
||||
objectParsed (v.value);
|
||||
currentValue = null;
|
||||
} else {
|
||||
// add object to sub-array; if current container is a struct, add later (at </member>)
|
||||
currentValue = (Value) values.pop ();
|
||||
currentValue.endElement (v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle objects contained in structs.
|
||||
if ("member".equals (name)) {
|
||||
Value v = currentValue;
|
||||
currentValue = (Value) values.pop ();
|
||||
currentValue.endElement (v);
|
||||
}
|
||||
|
||||
else if ("methodName".equals (name)) {
|
||||
methodName = cdata.toString ();
|
||||
cdata.setLength (0);
|
||||
readCdata = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method called by SAX driver.
|
||||
*/
|
||||
public void startElement (String name, AttributeList atts) throws SAXException {
|
||||
|
||||
if (debug)
|
||||
System.err.println ("startElement: "+name);
|
||||
|
||||
if ("value".equals (name)) {
|
||||
// System.err.println ("starting value");
|
||||
if (currentValue != null)
|
||||
values.push (currentValue);
|
||||
currentValue = new Value ();
|
||||
// cdata object is reused
|
||||
cdata.setLength(0);
|
||||
readCdata = true;
|
||||
}
|
||||
|
||||
else if ("methodName".equals (name)) {
|
||||
cdata.setLength(0);
|
||||
readCdata = true;
|
||||
}
|
||||
|
||||
else if ("name".equals (name)) {
|
||||
cdata.setLength(0);
|
||||
readCdata = true;
|
||||
}
|
||||
|
||||
else if ("string".equals (name)) {
|
||||
cdata.setLength(0);
|
||||
readCdata = true;
|
||||
} else if ("i4".equals (name) || "int".equals (name)) {
|
||||
currentValue.setType (INTEGER);
|
||||
cdata.setLength(0);
|
||||
readCdata = true;
|
||||
} else if ("boolean".equals (name)) {
|
||||
currentValue.setType (BOOLEAN);
|
||||
cdata.setLength(0);
|
||||
readCdata = true;
|
||||
} else if ("double".equals (name)) {
|
||||
currentValue.setType (DOUBLE);
|
||||
cdata.setLength(0);
|
||||
readCdata = true;
|
||||
} else if ("dateTime.iso8601".equals (name)) {
|
||||
currentValue.setType (DATE);
|
||||
cdata.setLength(0);
|
||||
readCdata = true;
|
||||
} else if ("base64".equals (name)) {
|
||||
currentValue.setType (BASE64);
|
||||
cdata.setLength(0);
|
||||
readCdata = true;
|
||||
} else if ("struct".equals (name))
|
||||
currentValue.setType (STRUCT);
|
||||
else if ("array".equals (name))
|
||||
currentValue.setType (ARRAY);
|
||||
else if ("nil".equals (name))
|
||||
currentValue.setType (NIL);
|
||||
}
|
||||
|
||||
|
||||
public void error (SAXParseException e) throws SAXException {
|
||||
System.err.println ("Error parsing XML: "+e);
|
||||
errorLevel = RECOVERABLE;
|
||||
errorMsg = e.toString ();
|
||||
}
|
||||
|
||||
public void fatalError(SAXParseException e) throws SAXException {
|
||||
System.err.println ("Fatal error parsing XML: "+e);
|
||||
errorLevel = FATAL;
|
||||
errorMsg = e.toString ();
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents an XML-RPC Value while the request is being parsed.
|
||||
*/
|
||||
class Value {
|
||||
|
||||
int type;
|
||||
Object value;
|
||||
// the name to use for the next member of struct values
|
||||
String nextMemberName;
|
||||
|
||||
Hashtable struct;
|
||||
Vector array;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public Value () {
|
||||
this.type = STRING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that a new child element has been parsed.
|
||||
*/
|
||||
public void endElement (Value child) {
|
||||
if (type == ARRAY)
|
||||
array.addElement (child.value);
|
||||
else if (type == STRUCT)
|
||||
struct.put (nextMemberName, child.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type of this value. If it's a container, create the corresponding java container.
|
||||
*/
|
||||
public void setType (int type) {
|
||||
// System.err.println ("setting type to "+types[type]);
|
||||
this.type = type;
|
||||
if (type == ARRAY)
|
||||
value = array = new Vector ();
|
||||
if (type == STRUCT)
|
||||
value = struct = new Hashtable ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the character data for the element and interpret it according to the
|
||||
* element type
|
||||
*/
|
||||
public void characterData (String cdata) {
|
||||
switch (type) {
|
||||
case INTEGER:
|
||||
value = new Integer (cdata.trim ());
|
||||
break;
|
||||
case BOOLEAN:
|
||||
value = "1".equals (cdata.trim ()) ? Boolean.TRUE : Boolean.FALSE;
|
||||
break;
|
||||
case DOUBLE:
|
||||
value = new Double (cdata.trim ());
|
||||
break;
|
||||
case DATE:
|
||||
try {
|
||||
value = dateformat.parse (cdata.trim ());
|
||||
} catch (ParseException p) {
|
||||
// System.err.println ("Exception while parsing date: "+p);
|
||||
throw new RuntimeException (p.getMessage ());
|
||||
}
|
||||
break;
|
||||
case BASE64:
|
||||
value = Base64.decode (cdata.getBytes());
|
||||
break;
|
||||
case STRING:
|
||||
value = cdata;
|
||||
break;
|
||||
case STRUCT:
|
||||
// this is the name to use for the next member of this struct
|
||||
nextMemberName = cdata;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This is a performance hack to get the type of a value without casting the Object.
|
||||
// It breaks the contract of method hashCode, but it doesn't matter since
|
||||
// Value objects are never used as keys in Hashtables.
|
||||
public int hashCode () {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return (types[type]+" element "+value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// A quick and dirty XML writer.
|
||||
class XmlWriter {
|
||||
|
||||
StringBuffer buf;
|
||||
String enc;
|
||||
|
||||
public XmlWriter (StringBuffer buf) {
|
||||
// The encoding used for XML-RPC is ISO-8859-1 for pragmatical reasons (Frontier/Win).
|
||||
this (buf, encoding);
|
||||
}
|
||||
|
||||
public XmlWriter (StringBuffer buf, String enc) {
|
||||
this.buf = buf;
|
||||
this.enc = enc;
|
||||
// get name of encoding for XML prolog
|
||||
String encName = encodings.getProperty (enc, enc);
|
||||
buf.append ("<?xml version=\"1.0\" encoding=\""+encName+"\"?>");
|
||||
}
|
||||
|
||||
public void startElement (String elem) {
|
||||
buf.append ("<");
|
||||
buf.append (elem);
|
||||
buf.append (">");
|
||||
}
|
||||
|
||||
public void endElement (String elem) {
|
||||
buf.append ("</");
|
||||
buf.append (elem);
|
||||
buf.append (">");
|
||||
}
|
||||
|
||||
public void emptyElement (String elem) {
|
||||
buf.append ("<");
|
||||
buf.append (elem);
|
||||
buf.append ("/>");
|
||||
}
|
||||
|
||||
|
||||
public void chardata (String text) {
|
||||
int l = text.length ();
|
||||
for (int i=0; i<l; i++) {
|
||||
char c = text.charAt (i);
|
||||
switch (c) {
|
||||
case '<' :
|
||||
buf.append ("<");
|
||||
break;
|
||||
case '&' :
|
||||
buf.append ("&");
|
||||
break;
|
||||
default :
|
||||
buf.append (c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void write (char[] text) {
|
||||
buf.append (text);
|
||||
}
|
||||
|
||||
public void write (String text) {
|
||||
buf.append (text);
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return buf.toString ();
|
||||
}
|
||||
|
||||
public byte[] getBytes () throws UnsupportedEncodingException {
|
||||
return buf.toString ().getBytes (enc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// wraps a DateFormat because it's not threadsafe
|
||||
class Formatter {
|
||||
|
||||
private DateFormat f;
|
||||
|
||||
public Formatter () {
|
||||
f = new SimpleDateFormat ("yyyyMMdd'T'HH:mm:ss");
|
||||
}
|
||||
|
||||
public synchronized String format (Date d) {
|
||||
return f.format (d);
|
||||
}
|
||||
|
||||
public synchronized Date parse (String s) throws ParseException {
|
||||
return f.parse (s);
|
||||
}
|
||||
}
|
226
src/helma/xmlrpc/XmlRpcClient.java
Normal file
226
src/helma/xmlrpc/XmlRpcClient.java
Normal file
|
@ -0,0 +1,226 @@
|
|||
/**
|
||||
* Copyright 1999 Hannes Wallnoefer
|
||||
* Implements a XML-RPC client. See http://www.xmlrpc.com/
|
||||
*/
|
||||
|
||||
package helma.xmlrpc;
|
||||
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import org.xml.sax.*;
|
||||
|
||||
/**
|
||||
* A multithreaded, reusable XML-RPC client object. Use this if you need a full-grown
|
||||
* HTTP client (e.g. for Proxy and Cookies support). If you don't need that, <code>XmlRpcClientLite</code>
|
||||
* may work better for you.
|
||||
*/
|
||||
public class XmlRpcClient implements XmlRpcHandler {
|
||||
|
||||
URL url;
|
||||
String auth;
|
||||
|
||||
/**
|
||||
* Construct a XML-RPC client with this URL.
|
||||
*/
|
||||
public XmlRpcClient (URL url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a XML-RPC client for the URL represented by this String.
|
||||
*/
|
||||
public XmlRpcClient (String url) throws MalformedURLException {
|
||||
this.url = new URL (url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a XML-RPC client for the specified hostname and port.
|
||||
*/
|
||||
public XmlRpcClient (String hostname, int port) throws MalformedURLException {
|
||||
this.url = new URL ("http://"+hostname+":"+port+"/RPC2");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets Authentication for this client. This will be sent as Basic Authentication header
|
||||
* to the server as described in <a href="http://www.ietf.org/rfc/rfc2617.txt">http://www.ietf.org/rfc/rfc2617.txt</a>.
|
||||
*/
|
||||
public void setBasicAuthentication (String user, String password) {
|
||||
if (user == null || password == null)
|
||||
auth = null;
|
||||
else {
|
||||
char[] basicAuth = Base64.encode ((user+":"+password).getBytes());
|
||||
auth = new String (basicAuth).trim();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an XML-RPC request and send it to the server. Parse the result and
|
||||
* return the corresponding Java object.
|
||||
*
|
||||
* @exception XmlRpcException: If the remote host returned a fault message.
|
||||
* @exception IOException: If the call could not be made because of lower level problems.
|
||||
*/
|
||||
public Object execute (String method, Vector params) throws XmlRpcException, IOException {
|
||||
Worker worker = getWorker ();
|
||||
try {
|
||||
Object retval = worker.execute (method, params);
|
||||
return retval;
|
||||
} finally {
|
||||
if (workers < 50 && !worker.fault)
|
||||
pool.push (worker);
|
||||
else
|
||||
workers -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
Stack pool = new Stack ();
|
||||
int workers = 0;
|
||||
|
||||
private final Worker getWorker () throws IOException {
|
||||
try {
|
||||
return (Worker) pool.pop ();
|
||||
} catch (EmptyStackException x) {
|
||||
if (workers < 100) {
|
||||
workers += 1;
|
||||
return new Worker ();
|
||||
}
|
||||
throw new IOException ("XML-RPC System overload");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Worker extends XmlRpc {
|
||||
|
||||
boolean fault;
|
||||
Object result = null;
|
||||
StringBuffer strbuf;
|
||||
|
||||
public Worker () throws IOException {
|
||||
super ();
|
||||
}
|
||||
|
||||
|
||||
public Object execute (String method, Vector params) throws XmlRpcException, IOException {
|
||||
fault = false;
|
||||
long now = System.currentTimeMillis ();
|
||||
try {
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream ();
|
||||
|
||||
if (strbuf == null)
|
||||
strbuf = new StringBuffer ();
|
||||
else
|
||||
strbuf.setLength (0);
|
||||
XmlWriter writer = new XmlWriter (strbuf);
|
||||
writeRequest (writer, method, params);
|
||||
byte[] request = writer.getBytes();
|
||||
|
||||
URLConnection con = url.openConnection ();
|
||||
con.setDoInput (true);
|
||||
con.setDoOutput (true);
|
||||
con.setUseCaches (false);
|
||||
con.setAllowUserInteraction(false);
|
||||
con.setRequestProperty ("Content-Length", Integer.toString (request.length));
|
||||
con.setRequestProperty ("Content-Type", "text/xml");
|
||||
if (auth != null)
|
||||
con.setRequestProperty ("Authorization", "Basic "+auth);
|
||||
// con.connect ();
|
||||
OutputStream out = con.getOutputStream ();
|
||||
out.write (request);
|
||||
out.flush ();
|
||||
InputStream in = con.getInputStream ();
|
||||
parse (in);
|
||||
} catch (Exception x) {
|
||||
x.printStackTrace ();
|
||||
throw new IOException (x.getMessage ());
|
||||
}
|
||||
if (fault) { // generate an XmlRpcException
|
||||
XmlRpcException exception = null;
|
||||
try {
|
||||
Hashtable f = (Hashtable) result;
|
||||
String faultString = (String) f.get ("faultString");
|
||||
int faultCode = Integer.parseInt (f.get ("faultCode").toString ());
|
||||
exception = new XmlRpcException (faultCode, faultString.trim ());
|
||||
} catch (Exception x) {
|
||||
throw new XmlRpcException (0, "Invalid fault response");
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
if (debug)
|
||||
System.err.println ("Spent "+(System.currentTimeMillis () - now)+" in request");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the return value has been parsed.
|
||||
*/
|
||||
void objectParsed (Object what) {
|
||||
result = what;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate an XML-RPC request from a method name and a parameter vector.
|
||||
*/
|
||||
void writeRequest (XmlWriter writer, String method, Vector params) throws IOException {
|
||||
writer.startElement ("methodCall");
|
||||
|
||||
writer.startElement ("methodName");
|
||||
writer.write (method);
|
||||
writer.endElement ("methodName");
|
||||
|
||||
writer.startElement ("params");
|
||||
int l = params.size ();
|
||||
for (int i=0; i<l; i++) {
|
||||
writer.startElement ("param");
|
||||
writeObject (params.elementAt (i), writer);
|
||||
writer.endElement ("param");
|
||||
}
|
||||
writer.endElement ("params");
|
||||
writer.endElement ("methodCall");
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides method in XmlRpc to handle fault repsonses.
|
||||
*/
|
||||
public void startElement (String name, AttributeList atts) throws SAXException {
|
||||
if ("fault".equals (name))
|
||||
fault = true;
|
||||
else
|
||||
super.startElement (name, atts);
|
||||
}
|
||||
|
||||
} // end of inner class Worker
|
||||
|
||||
|
||||
/**
|
||||
* Just for testing.
|
||||
*/
|
||||
public static void main (String args[]) throws Exception {
|
||||
// XmlRpc.setDebug (true);
|
||||
try {
|
||||
String url = args[0];
|
||||
String method = args[1];
|
||||
Vector v = new Vector ();
|
||||
for (int i=2; i<args.length; i++) try {
|
||||
v.addElement (new Integer (Integer.parseInt (args[i])));
|
||||
} catch (NumberFormatException nfx) {
|
||||
v.addElement (args[i]);
|
||||
}
|
||||
XmlRpcClient client = new XmlRpcClient (url);
|
||||
try {
|
||||
System.err.println (client.execute (method, v));
|
||||
} catch (Exception ex) {
|
||||
System.err.println ("Error: "+ex.getMessage());
|
||||
}
|
||||
} catch (Exception x) {
|
||||
System.err.println (x);
|
||||
System.err.println ("Usage: java helma.xmlrpc.XmlRpcClient <url> <method> <arg> ....");
|
||||
System.err.println ("Arguments are sent as integers or strings.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
358
src/helma/xmlrpc/XmlRpcClientLite.java
Normal file
358
src/helma/xmlrpc/XmlRpcClientLite.java
Normal file
|
@ -0,0 +1,358 @@
|
|||
/**
|
||||
* Copyright 1999 Hannes Wallnoefer
|
||||
* Implements a XML-RPC client. See http://www.xmlrpc.com/
|
||||
*/
|
||||
|
||||
package helma.xmlrpc;
|
||||
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import org.xml.sax.*;
|
||||
|
||||
/**
|
||||
* A multithreaded, reusable XML-RPC client object. This version uses a homegrown
|
||||
* HTTP client which can be quite a bit faster than java.net.URLConnection, especially
|
||||
* when used with XmlRpc.setKeepAlive(true).
|
||||
*/
|
||||
public class XmlRpcClientLite extends XmlRpcClient {
|
||||
|
||||
/**
|
||||
* Construct a XML-RPC client with this URL.
|
||||
*/
|
||||
public XmlRpcClientLite (URL url) {
|
||||
super (url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a XML-RPC client for the URL represented by this String.
|
||||
*/
|
||||
public XmlRpcClientLite (String url) throws MalformedURLException {
|
||||
super (url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a XML-RPC client for the specified hostname and port.
|
||||
*/
|
||||
public XmlRpcClientLite (String hostname, int port) throws MalformedURLException {
|
||||
super (hostname, port);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate an XML-RPC request and send it to the server. Parse the result and
|
||||
* return the corresponding Java object.
|
||||
*
|
||||
* @exception XmlRpcException: If the remote host returned a fault message.
|
||||
* @exception IOException: If the call could not be made because of lower level problems.
|
||||
*/
|
||||
public Object execute (String method, Vector params) throws XmlRpcException, IOException {
|
||||
Worker worker = getWorker ();
|
||||
try {
|
||||
Object retval = worker.execute (method, params);
|
||||
return retval;
|
||||
} finally {
|
||||
if (workers < 50 && !worker.fault)
|
||||
pool.push (worker);
|
||||
else
|
||||
workers -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
Stack pool = new Stack ();
|
||||
int workers = 0;
|
||||
|
||||
private final Worker getWorker () throws IOException {
|
||||
try {
|
||||
return (Worker) pool.pop ();
|
||||
} catch (EmptyStackException x) {
|
||||
if (workers < 100) {
|
||||
workers += 1;
|
||||
return new Worker ();
|
||||
}
|
||||
throw new IOException ("XML-RPC System overload");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Worker extends XmlRpc {
|
||||
|
||||
boolean fault;
|
||||
Object result = null;
|
||||
HttpClient client = null;
|
||||
StringBuffer strbuf;
|
||||
|
||||
public Worker () {
|
||||
super ();
|
||||
}
|
||||
|
||||
|
||||
public Object execute (String method, Vector params) throws XmlRpcException, IOException {
|
||||
long now = System.currentTimeMillis ();
|
||||
fault = false;
|
||||
try {
|
||||
if (strbuf == null)
|
||||
strbuf = new StringBuffer ();
|
||||
else
|
||||
strbuf.setLength (0);
|
||||
XmlWriter writer = new XmlWriter (strbuf);
|
||||
writeRequest (writer, method, params);
|
||||
byte[] request = writer.getBytes();
|
||||
|
||||
// and send it to the server
|
||||
if (client == null)
|
||||
client = new HttpClient (url);
|
||||
|
||||
client.write (request);
|
||||
|
||||
InputStream in = client.getInputStream ();
|
||||
|
||||
// parse the response
|
||||
parse (in);
|
||||
|
||||
// client keepalive is always false if XmlRpc.keepalive is false
|
||||
if (!client.keepalive)
|
||||
client.closeConnection ();
|
||||
|
||||
if (debug)
|
||||
System.err.println ("result = "+result);
|
||||
|
||||
// check for errors from the XML parser
|
||||
if (errorLevel == FATAL)
|
||||
throw new Exception (errorMsg);
|
||||
} catch (IOException iox) {
|
||||
// this is a lower level problem, client could not talk to server for some reason.
|
||||
|
||||
throw iox;
|
||||
|
||||
} catch (Exception x) {
|
||||
// same as above, but exception has to be converted to IOException.
|
||||
if (XmlRpc.debug)
|
||||
x.printStackTrace ();
|
||||
|
||||
String msg = x.getMessage ();
|
||||
if (msg == null || msg.length () == 0)
|
||||
msg = x.toString ();
|
||||
throw new IOException (msg);
|
||||
}
|
||||
|
||||
if (fault) {
|
||||
// this is an XML-RPC-level problem, i.e. the server reported an error.
|
||||
// throw an XmlRpcException.
|
||||
|
||||
XmlRpcException exception = null;
|
||||
try {
|
||||
Hashtable f = (Hashtable) result;
|
||||
String faultString = (String) f.get ("faultString");
|
||||
int faultCode = Integer.parseInt (f.get ("faultCode").toString ());
|
||||
exception = new XmlRpcException (faultCode, faultString.trim ());
|
||||
} catch (Exception x) {
|
||||
throw new XmlRpcException (0, "Server returned an invalid fault response.");
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
if (debug)
|
||||
System.err.println ("Spent "+(System.currentTimeMillis () - now)+" millis in request");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the return value has been parsed.
|
||||
*/
|
||||
void objectParsed (Object what) {
|
||||
result = what;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate an XML-RPC request from a method name and a parameter vector.
|
||||
*/
|
||||
void writeRequest (XmlWriter writer, String method, Vector params) throws IOException {
|
||||
writer.startElement ("methodCall");
|
||||
|
||||
writer.startElement ("methodName");
|
||||
writer.write (method);
|
||||
writer.endElement ("methodName");
|
||||
|
||||
writer.startElement ("params");
|
||||
int l = params.size ();
|
||||
for (int i=0; i<l; i++) {
|
||||
writer.startElement ("param");
|
||||
writeObject (params.elementAt (i), writer);
|
||||
writer.endElement ("param");
|
||||
}
|
||||
writer.endElement ("params");
|
||||
writer.endElement ("methodCall");
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides method in XmlRpc to handle fault repsonses.
|
||||
*/
|
||||
public void startElement (String name, AttributeList atts) throws SAXException {
|
||||
if ("fault".equals (name))
|
||||
fault = true;
|
||||
else
|
||||
super.startElement (name, atts);
|
||||
}
|
||||
|
||||
} // end of class Worker
|
||||
|
||||
|
||||
// A replacement for java.net.URLConnection, which seems very slow on MS Java.
|
||||
class HttpClient {
|
||||
|
||||
String hostname;
|
||||
String host;
|
||||
int port;
|
||||
String uri;
|
||||
Socket socket = null;
|
||||
BufferedOutputStream output;
|
||||
BufferedInputStream input;
|
||||
boolean keepalive;
|
||||
boolean fresh;
|
||||
|
||||
|
||||
public HttpClient (URL url) throws IOException {
|
||||
hostname = url.getHost ();
|
||||
port = url.getPort ();
|
||||
if (port < 1) port = 80;
|
||||
uri = url.getFile ();
|
||||
if (uri == null || "".equals (uri))
|
||||
uri = "/";
|
||||
host = port == 80 ? hostname : hostname+":"+port;
|
||||
initConnection ();
|
||||
}
|
||||
|
||||
protected void initConnection () throws IOException {
|
||||
fresh = true;
|
||||
socket = new Socket (hostname, port);
|
||||
output = new BufferedOutputStream (socket.getOutputStream());
|
||||
input = new BufferedInputStream (socket.getInputStream ());
|
||||
}
|
||||
|
||||
protected void closeConnection () {
|
||||
try {
|
||||
socket.close ();
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
|
||||
public void write (byte[] request) throws IOException {
|
||||
try {
|
||||
output.write (("POST "+uri+" HTTP/1.0\r\n").getBytes());
|
||||
output.write (("User-Agent: "+XmlRpc.version+"\r\n").getBytes());
|
||||
output.write (("Host: "+host+"\r\n").getBytes());
|
||||
if (XmlRpc.getKeepAlive())
|
||||
output.write ("Connection: Keep-Alive\r\n".getBytes());
|
||||
output.write ("Content-Type: text/xml\r\n".getBytes());
|
||||
if (auth != null)
|
||||
output.write (("Authorization: Basic "+auth+"\r\n").getBytes());
|
||||
output.write (("Content-Length: "+request.length).getBytes());
|
||||
output.write ("\r\n\r\n".getBytes());
|
||||
output.write (request);
|
||||
output.flush ();
|
||||
fresh = false;
|
||||
} catch (IOException iox) {
|
||||
// if the connection is not "fresh" (unused), the exception may have occurred
|
||||
// because the server timed the connection out. Give it another try.
|
||||
if (!fresh) {
|
||||
initConnection ();
|
||||
write (request);
|
||||
} else {
|
||||
throw (iox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public InputStream getInputStream () throws IOException {
|
||||
String line = readLine ();
|
||||
if (XmlRpc.debug)
|
||||
System.err.println (line);
|
||||
int contentLength = -1;
|
||||
try {
|
||||
StringTokenizer tokens = new StringTokenizer (line);
|
||||
String httpversion = tokens.nextToken ();
|
||||
String statusCode = tokens.nextToken();
|
||||
String statusMsg = tokens.nextToken ("\n\r");
|
||||
keepalive = XmlRpc.getKeepAlive() && "HTTP/1.1".equals (httpversion);
|
||||
if (!"200".equals (statusCode))
|
||||
throw new IOException ("Unexpected Response from Server: "+statusMsg);
|
||||
} catch (IOException iox) {
|
||||
throw iox;
|
||||
} catch (Exception x) {
|
||||
x.printStackTrace ();
|
||||
throw new IOException ("Server returned invalid Response.");
|
||||
}
|
||||
do {
|
||||
line = readLine ();
|
||||
if (line != null) {
|
||||
if (XmlRpc.debug)
|
||||
System.err.println (line);
|
||||
line = line.toLowerCase ();
|
||||
if (line.startsWith ("content-length:"))
|
||||
contentLength = Integer.parseInt (line.substring (15).trim ());
|
||||
if (line.startsWith ("connection:"))
|
||||
keepalive = XmlRpc.getKeepAlive() && line.indexOf ("keep-alive") > -1;
|
||||
}
|
||||
} while (line != null && ! line.equals(""));
|
||||
return new ServerInputStream (input, contentLength);
|
||||
}
|
||||
|
||||
|
||||
byte[] buffer;
|
||||
private String readLine () throws IOException {
|
||||
if (buffer == null)
|
||||
buffer = new byte[512];
|
||||
int next;
|
||||
int count = 0;
|
||||
while (true) {
|
||||
next = input.read();
|
||||
if (next < 0 || next == '\n')
|
||||
break;
|
||||
if (next != '\r')
|
||||
buffer[count++] = (byte) next;
|
||||
if (count >= 512)
|
||||
throw new IOException ("HTTP Header too long");
|
||||
}
|
||||
return new String (buffer, 0, count);
|
||||
}
|
||||
|
||||
|
||||
protected void finalize () throws Throwable {
|
||||
closeConnection ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Just for testing.
|
||||
*/
|
||||
public static void main (String args[]) throws Exception {
|
||||
// XmlRpc.setDebug (true);
|
||||
try {
|
||||
String url = args[0];
|
||||
String method = args[1];
|
||||
Vector v = new Vector ();
|
||||
for (int i=2; i<args.length; i++) try {
|
||||
v.addElement (new Integer (Integer.parseInt (args[i])));
|
||||
} catch (NumberFormatException nfx) {
|
||||
v.addElement (args[i]);
|
||||
}
|
||||
XmlRpcClient client = new XmlRpcClientLite (url);
|
||||
try {
|
||||
System.err.println (client.execute (method, v));
|
||||
} catch (Exception ex) {
|
||||
System.err.println ("Error: "+ex.getMessage());
|
||||
}
|
||||
} catch (Exception x) {
|
||||
System.err.println (x);
|
||||
System.err.println ("Usage: java helma.xmlrpc.XmlRpcClient <url> <method> <arg> ....");
|
||||
System.err.println ("Arguments are sent as integers or strings.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
24
src/helma/xmlrpc/XmlRpcException.java
Normal file
24
src/helma/xmlrpc/XmlRpcException.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* Copyright 1999 Hannes Wallnoefer
|
||||
*/
|
||||
|
||||
package helma.xmlrpc;
|
||||
|
||||
/**
|
||||
* This is thrown by the XmlRpcClient if the remote server reported an error. If something
|
||||
* went wrong at a lower level (e.g. no http connection) an IOException will be thrown instead.
|
||||
*/
|
||||
public class XmlRpcException extends Exception {
|
||||
|
||||
/**
|
||||
* The fault code of the exception. For servers based on this library, this will always be 0.
|
||||
* (If there are predefined error codes, they should be in the XML-RPC spec.)
|
||||
*/
|
||||
public final int code;
|
||||
|
||||
public XmlRpcException (int code, String message) {
|
||||
super (message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
}
|
23
src/helma/xmlrpc/XmlRpcHandler.java
Normal file
23
src/helma/xmlrpc/XmlRpcHandler.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 1999 Hannes Wallnoefer
|
||||
*/
|
||||
|
||||
package helma.xmlrpc;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* The XML-RPC server uses this interface to call a method of an RPC handler. This should
|
||||
* be implemented by any class that wants to directly take control when it is called over RPC. Classes
|
||||
* not implementing this interface will be wrapped into an Invoker
|
||||
* object that tries to find the matching method for an XML-RPC request.
|
||||
*/
|
||||
|
||||
public interface XmlRpcHandler {
|
||||
|
||||
/**
|
||||
* Return the result, or throw an Exception if something went wrong.
|
||||
*/
|
||||
public Object execute (String method, Vector params) throws Exception;
|
||||
|
||||
}
|
42
src/helma/xmlrpc/XmlRpcProxyServlet.java
Normal file
42
src/helma/xmlrpc/XmlRpcProxyServlet.java
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2000 Hannes Wallnöfer
|
||||
|
||||
package helma.xmlrpc;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.*;
|
||||
import java.io.*;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* A Servlet that acts as a XML-RPC Proxy . <p>
|
||||
*
|
||||
* The URL of the server to connect to is taken from the init parameter <tt>url</tt>.
|
||||
*/
|
||||
|
||||
public class XmlRpcProxyServlet extends HttpServlet {
|
||||
|
||||
private XmlRpcServer xmlrpc;
|
||||
|
||||
public void init (ServletConfig config) throws ServletException {
|
||||
if ("true".equalsIgnoreCase (config.getInitParameter ("debug")))
|
||||
XmlRpc.setDebug (true);
|
||||
String url = config.getInitParameter ("url");
|
||||
xmlrpc = new XmlRpcServer ();
|
||||
try {
|
||||
xmlrpc.addHandler ("$default", new XmlRpcClientLite (url));
|
||||
} catch (Exception x) {
|
||||
throw new ServletException ("Invalid URL: "+url+" ("+x.toString ()+")");
|
||||
}
|
||||
}
|
||||
|
||||
public void doPost(HttpServletRequest req, HttpServletResponse res)
|
||||
throws ServletException, IOException {
|
||||
byte[] result = xmlrpc.execute (req.getInputStream ());
|
||||
res.setContentType("text/xml");
|
||||
res.setContentLength (result.length);
|
||||
OutputStream output = res.getOutputStream();
|
||||
output.write (result);
|
||||
output.flush ();
|
||||
}
|
||||
|
||||
}
|
284
src/helma/xmlrpc/XmlRpcServer.java
Normal file
284
src/helma/xmlrpc/XmlRpcServer.java
Normal file
|
@ -0,0 +1,284 @@
|
|||
/**
|
||||
* Copyright 1999 Hannes Wallnoefer
|
||||
* Implements an XML-RPC server. See http://www.xmlrpc.com/
|
||||
*/
|
||||
|
||||
package helma.xmlrpc;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.lang.reflect.*;
|
||||
|
||||
/**
|
||||
* A multithreaded, reusable XML-RPC server object. The name may be misleading because this does not open any
|
||||
* server sockets. Instead it is fed by passing an XML-RPC input stream to the execute method.
|
||||
* If you want to open a HTTP listener, use the WebServer class instead.
|
||||
*/
|
||||
public class XmlRpcServer {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
Hashtable handlers;
|
||||
|
||||
/**
|
||||
* Construct a new XML-RPC server. You have to register handlers to make it
|
||||
* do something useful.
|
||||
*/
|
||||
public XmlRpcServer () {
|
||||
handlers = new Hashtable ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a handler object with this name. Methods of this objects will be
|
||||
* callable over XML-RPC as "handlername.methodname". For more information
|
||||
* about XML-RPC handlers see the <a href="../index.html#1a">main documentation page</a>.
|
||||
*/
|
||||
public void addHandler (String handlername, Object handler) {
|
||||
if (handler instanceof XmlRpcHandler || handler instanceof AuthenticatedXmlRpcHandler)
|
||||
handlers.put (handlername, handler);
|
||||
else if (handler != null)
|
||||
handlers.put (handlername, new Invoker (handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a handler object that was previously registered with this server.
|
||||
*/
|
||||
public void removeHandler (String handlername) {
|
||||
handlers.remove (handlername);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the request and execute the handler method, if one is found. Returns the result as XML.
|
||||
* The calling Java code doesn't need to know whether the call was successful or not since this is all
|
||||
* packed into the response.
|
||||
*/
|
||||
public byte[] execute (InputStream is) {
|
||||
return execute (is, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the request and execute the handler method, if one is found. If the invoked handler is
|
||||
* AuthenticatedXmlRpcHandler, use the credentials to authenticate the user.
|
||||
*/
|
||||
public byte[] execute (InputStream is, String user, String password) {
|
||||
Worker worker = getWorker ();
|
||||
byte[] retval = worker.execute (is, user, password);
|
||||
pool.push (worker);
|
||||
return retval;
|
||||
}
|
||||
|
||||
Stack pool = new Stack ();
|
||||
int workers = 0;
|
||||
|
||||
private final Worker getWorker () {
|
||||
try {
|
||||
return (Worker) pool.pop ();
|
||||
} catch (EmptyStackException x) {
|
||||
if (workers < 100) {
|
||||
workers += 1;
|
||||
return new Worker ();
|
||||
}
|
||||
throw new RuntimeException ("System overload");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Worker extends XmlRpc {
|
||||
|
||||
Vector inParams;
|
||||
Object outParam;
|
||||
byte[] result;
|
||||
StringBuffer strbuf;
|
||||
|
||||
public byte[] execute (InputStream is, String user, String password) {
|
||||
inParams = new Vector ();
|
||||
if (strbuf == null)
|
||||
strbuf = new StringBuffer ();
|
||||
else
|
||||
strbuf.setLength (0);
|
||||
long now = System.currentTimeMillis ();
|
||||
|
||||
try {
|
||||
parse (is);
|
||||
if (debug) {
|
||||
System.err.println ("method name: "+methodName);
|
||||
System.err.println ("inparams: "+inParams);
|
||||
}
|
||||
// check for errors from the XML parser
|
||||
if (errorLevel > NONE)
|
||||
throw new Exception (errorMsg);
|
||||
|
||||
Object handler = null;
|
||||
|
||||
String handlerName = null;
|
||||
int dot = methodName.indexOf (".");
|
||||
if (dot > -1) {
|
||||
handlerName = methodName.substring (0, dot);
|
||||
handler = handlers.get (handlerName);
|
||||
if (handler != null)
|
||||
methodName = methodName.substring (dot+1);
|
||||
}
|
||||
|
||||
if (handler == null) {
|
||||
handler = handlers.get ("$default");
|
||||
}
|
||||
|
||||
if (handler == null) {
|
||||
if (dot > -1)
|
||||
throw new Exception ("RPC handler object \""+handlerName+"\" not found and no default handler registered.");
|
||||
else
|
||||
throw new Exception ("RPC handler object not found for \""+methodName+"\": no default handler registered.");
|
||||
}
|
||||
|
||||
if (handler instanceof AuthenticatedXmlRpcHandler)
|
||||
outParam = ((AuthenticatedXmlRpcHandler) handler).execute (methodName, inParams, user, password);
|
||||
else
|
||||
outParam = ((XmlRpcHandler) handler).execute (methodName, inParams);
|
||||
if (debug)
|
||||
System.err.println ("outparam = "+outParam);
|
||||
|
||||
XmlWriter writer = new XmlWriter (strbuf);
|
||||
writeResponse (outParam, writer);
|
||||
result = writer.getBytes ();
|
||||
|
||||
} catch (Exception x) {
|
||||
if (debug)
|
||||
x.printStackTrace ();
|
||||
XmlWriter writer = new XmlWriter (strbuf);
|
||||
String message = x.toString ();
|
||||
// check if XmlRpcException was thrown so we can get an error code
|
||||
int code = x instanceof XmlRpcException ? ((XmlRpcException) x).code : 0;
|
||||
writeError (code, message, writer);
|
||||
try {
|
||||
result = writer.getBytes ();
|
||||
} catch (UnsupportedEncodingException encx) {
|
||||
System.err.println ("XmlRpcServer.execute: "+encx);
|
||||
result = writer.toString().getBytes();
|
||||
}
|
||||
}
|
||||
if (debug)
|
||||
System.err.println ("Spent "+(System.currentTimeMillis () - now)+" millis in request");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an object to be added to the argument list has been parsed.
|
||||
*/
|
||||
void objectParsed (Object what) {
|
||||
inParams.addElement (what);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an XML-RPC response to the XML writer.
|
||||
*/
|
||||
void writeResponse (Object param, XmlWriter writer) {
|
||||
writer.startElement ("methodResponse");
|
||||
// if (param == null) param = ""; // workaround for Frontier bug
|
||||
writer.startElement ("params");
|
||||
writer.startElement ("param");
|
||||
writeObject (param, writer);
|
||||
writer.endElement ("param");
|
||||
writer.endElement ("params");
|
||||
writer.endElement ("methodResponse");
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an XML-RPC error response to the XML writer.
|
||||
*/
|
||||
void writeError (int code, String message, XmlWriter writer) {
|
||||
// System.err.println ("error: "+message);
|
||||
Hashtable h = new Hashtable ();
|
||||
h.put ("faultCode", new Integer (code));
|
||||
h.put ("faultString", message);
|
||||
writer.startElement ("methodResponse");
|
||||
writer.startElement ("fault");
|
||||
writeObject (h, writer);
|
||||
writer.endElement ("fault");
|
||||
writer.endElement ("methodResponse");
|
||||
}
|
||||
|
||||
} // end of inner class Worker
|
||||
|
||||
} // XmlRpcServer
|
||||
|
||||
// This class uses Java Reflection to call methods matching an XML-RPC call
|
||||
class Invoker implements XmlRpcHandler {
|
||||
|
||||
private Object invokeTarget;
|
||||
private Class targetClass;
|
||||
|
||||
public Invoker(Object target) {
|
||||
invokeTarget = target;
|
||||
targetClass = invokeTarget instanceof Class ?
|
||||
(Class) invokeTarget : invokeTarget.getClass();
|
||||
if (XmlRpc.debug)
|
||||
System.err.println("Target object is " + targetClass);
|
||||
}
|
||||
|
||||
|
||||
// main method, sucht methode in object, wenn gefunden dann aufrufen.
|
||||
public Object execute (String methodName, Vector params) throws Exception {
|
||||
|
||||
|
||||
// Array mit Classtype bilden, ObjectAry mit Values bilden
|
||||
Class[] argClasses = null;
|
||||
Object[] argValues = null;
|
||||
if(params != null){
|
||||
argClasses = new Class[params.size()];
|
||||
argValues = new Object[params.size()];
|
||||
for(int i = 0; i < params.size(); i++){
|
||||
argValues[i] = params.elementAt(i);
|
||||
if (argValues[i] instanceof Integer)
|
||||
argClasses[i] = Integer.TYPE;
|
||||
else if (argValues[i] instanceof Double)
|
||||
argClasses[i] = Double.TYPE;
|
||||
else if (argValues[i] instanceof Boolean)
|
||||
argClasses[i] = Boolean.TYPE;
|
||||
else
|
||||
argClasses[i] = argValues[i].getClass();
|
||||
}
|
||||
}
|
||||
|
||||
// Methode da ?
|
||||
Method method = null;
|
||||
|
||||
if (XmlRpc.debug) {
|
||||
System.err.println("Searching for method: " + methodName);
|
||||
for(int i = 0; i < argClasses.length; i++)
|
||||
System.err.println("Parameter " + i + ": " + argClasses[i] + " = " + argValues[i]);
|
||||
}
|
||||
|
||||
try {
|
||||
method = targetClass.getMethod(methodName, argClasses);
|
||||
}
|
||||
// Wenn nicht da dann entsprechende Exception returnen
|
||||
catch(NoSuchMethodException nsm_e){
|
||||
throw nsm_e;
|
||||
}
|
||||
catch (SecurityException s_e){
|
||||
throw s_e;
|
||||
}
|
||||
|
||||
// invoke
|
||||
Object returnValue = null;
|
||||
try {
|
||||
returnValue = method.invoke (invokeTarget, argValues);
|
||||
}
|
||||
catch (IllegalAccessException iacc_e){
|
||||
throw iacc_e;
|
||||
}
|
||||
catch (IllegalArgumentException iarg_e){
|
||||
throw iarg_e;
|
||||
}
|
||||
catch (InvocationTargetException it_e) {
|
||||
if (XmlRpc.debug)
|
||||
it_e.getTargetException ().printStackTrace ();
|
||||
throw new Exception (it_e.getTargetException ().toString ());
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
}
|
46
src/helma/xmlrpc/XmlRpcServlet.java
Normal file
46
src/helma/xmlrpc/XmlRpcServlet.java
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 1999 Hannes Wallnöfer, Raphael Spannocchi
|
||||
|
||||
package helma.xmlrpc;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.*;
|
||||
import java.io.*;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* A prototype servlet to run XML-RPC. <p>
|
||||
*
|
||||
* Note that some clients like the one in Frontier 5 and the first version of XmlRpcApplet
|
||||
* had XML-RPC requests hard-coded to URI /RPC2. To work with these clients, you have
|
||||
* to configure your servlet environment to respond to /RPC2. This has been fixed in the
|
||||
* new version of the XmlRpcApplet.
|
||||
*
|
||||
*/
|
||||
|
||||
public class XmlRpcServlet extends HttpServlet implements XmlRpcHandler {
|
||||
|
||||
public XmlRpcServer xmlrpc;
|
||||
|
||||
public void init(ServletConfig config) throws ServletException {
|
||||
xmlrpc = new XmlRpcServer ();
|
||||
xmlrpc.addHandler ("example", this);
|
||||
}
|
||||
|
||||
public void doPost(HttpServletRequest req, HttpServletResponse res)
|
||||
throws ServletException, IOException {
|
||||
byte[] result = xmlrpc.execute (req.getInputStream ());
|
||||
res.setContentType("text/xml");
|
||||
res.setContentLength (result.length);
|
||||
OutputStream output = res.getOutputStream();
|
||||
output.write (result);
|
||||
output.flush ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for XML-RPC server
|
||||
*/
|
||||
public Object execute (String methodname, Vector params) {
|
||||
return params;
|
||||
}
|
||||
|
||||
}
|
170
src/helma/xmlrpc/fesi/FesiRpcExtension.java
Normal file
170
src/helma/xmlrpc/fesi/FesiRpcExtension.java
Normal file
|
@ -0,0 +1,170 @@
|
|||
// RpcXtension.java
|
||||
// Copyright (c) Hannes Wallnöfer, 1999 - All rights reserved
|
||||
|
||||
package helma.xmlrpc.fesi;
|
||||
|
||||
import helma.xmlrpc.*;
|
||||
|
||||
import FESI.Interpreter.*;
|
||||
import FESI.Exceptions.*;
|
||||
import FESI.Extensions.*;
|
||||
import FESI.Data.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.net.*;
|
||||
|
||||
|
||||
/**
|
||||
* An extension to transparently call and serve XML-RPC from the
|
||||
* <a href=http://home.worldcom.ch/jmlugrin/fesi/>FESI EcmaScript</a> interpreter.
|
||||
* The extension adds constructors for XML-RPC clients and servers to the Global Object.
|
||||
* For more information on how to use this please look at the files <tt>server.es</tt> and
|
||||
* <tt>client.es</tt> in the src/fesi directory of the distribution.
|
||||
*
|
||||
* All argument conversion is done automatically. Currently the following argument and return
|
||||
* types are supported:
|
||||
* <ul>
|
||||
* <li> plain objects (with all properties returned by ESObject.getProperties ())
|
||||
* <li> arrays
|
||||
* <li> strings
|
||||
* <li> date objects
|
||||
* <li> booleans
|
||||
* <li> integer and float numbers (long values are not supported!)
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
public class FesiRpcExtension extends Extension {
|
||||
|
||||
Evaluator evaluator;
|
||||
ESObject op;
|
||||
|
||||
public void initializeExtension (Evaluator evaluator) throws EcmaScriptException {
|
||||
// XmlRpc.setDebug (true);
|
||||
this.evaluator = evaluator;
|
||||
GlobalObject go = evaluator.getGlobalObject();
|
||||
FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype();
|
||||
|
||||
op = evaluator.getObjectPrototype();
|
||||
|
||||
go.putHiddenProperty ("Remote", new GlobalObjectRemote ("Remote", evaluator, fp)); // the Remote constructor
|
||||
go.putHiddenProperty ("RemoteServer", new GlobalObjectRemoteServer ("RemoteServer", evaluator, fp)); // the RemoteServer constructor
|
||||
|
||||
}
|
||||
|
||||
|
||||
class GlobalObjectRemote extends BuiltinFunctionObject {
|
||||
|
||||
GlobalObjectRemote (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
|
||||
public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
return doConstruct(thisObject, arguments);
|
||||
}
|
||||
|
||||
public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
ESObject remote = null;
|
||||
String url = null;
|
||||
String robj = null;
|
||||
if (arguments.length >= 1)
|
||||
url = arguments[0].toString ();
|
||||
if (arguments.length >= 2)
|
||||
robj = arguments[1].toString ();
|
||||
try {
|
||||
remote = new ESRemote (op, this.evaluator, url, robj);
|
||||
} catch (MalformedURLException x) {
|
||||
throw new EcmaScriptException (x.toString ());
|
||||
}
|
||||
return remote;
|
||||
}
|
||||
}
|
||||
|
||||
class GlobalObjectRemoteServer extends BuiltinFunctionObject {
|
||||
|
||||
|
||||
|
||||
GlobalObjectRemoteServer (String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||
super(fp, evaluator, name, 1);
|
||||
}
|
||||
|
||||
public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
return doConstruct(thisObject, arguments);
|
||||
}
|
||||
|
||||
public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||
ESObject remotesrv = null;
|
||||
String globalname = null;
|
||||
if (arguments.length < 1 || arguments.length > 2)
|
||||
throw new EcmaScriptException ("Wrong number of arguments for constructor RemoteServer");
|
||||
int port = arguments[0].toInt32 ();
|
||||
if (arguments.length == 2)
|
||||
globalname = arguments[1].toString ();
|
||||
try {
|
||||
remotesrv = new FesiRpcServer (port, op, this.evaluator);
|
||||
if (globalname != null)
|
||||
this.evaluator.getGlobalObject ().putProperty (globalname, remotesrv, globalname.hashCode ());
|
||||
} catch (IOException x) {
|
||||
throw new EcmaScriptException (x.toString ());
|
||||
}
|
||||
return remotesrv;
|
||||
}
|
||||
}
|
||||
|
||||
class ESRemote extends ObjectPrototype {
|
||||
|
||||
URL url;
|
||||
String remoteObject;
|
||||
|
||||
public ESRemote (ESObject prototype, Evaluator evaluator, String urlstring, String robj) throws MalformedURLException {
|
||||
super (prototype, evaluator);
|
||||
this.url = new URL (urlstring);
|
||||
remoteObject = robj;
|
||||
}
|
||||
|
||||
public ESRemote (ESObject prototype, Evaluator evaluator, URL url, String robj) {
|
||||
super (prototype, evaluator);
|
||||
this.url = url;
|
||||
remoteObject = robj;
|
||||
}
|
||||
|
||||
public ESValue doIndirectCall(Evaluator evaluator, ESObject target, String functionName, ESValue arguments[])
|
||||
throws EcmaScriptException, NoSuchMethodException {
|
||||
// System.out.println ("doIndirectCall called with "+functionName);
|
||||
XmlRpcClient client = new XmlRpcClient (url);
|
||||
long now = System.currentTimeMillis ();
|
||||
Object retval = null;
|
||||
int l = arguments.length;
|
||||
Vector v = new Vector ();
|
||||
for (int i=0; i<l; i++) {
|
||||
Object arg = FesiRpcUtil.convertE2J (arguments[i]);
|
||||
// System.out.println ("converted to J: "+arg.getClass ());
|
||||
v.addElement (arg);
|
||||
}
|
||||
// System.out.println ("spent "+(System.currentTimeMillis ()-now)+" millis in argument conversion");
|
||||
ESObject esretval = ObjectObject.createObject (evaluator);
|
||||
try {
|
||||
String method = remoteObject == null ? functionName : remoteObject+"."+functionName;
|
||||
retval = client.execute (method, v);
|
||||
esretval.putProperty ("error", ESNull.theNull, "error".hashCode());
|
||||
esretval.putProperty ("result", FesiRpcUtil.convertJ2E (retval, this.evaluator), "result".hashCode());
|
||||
} catch (Exception x) {
|
||||
String msg = x.getMessage();
|
||||
if (msg == null || msg.length() == 0)
|
||||
msg = x.toString ();
|
||||
esretval.putProperty ("error", new ESString(msg), "error".hashCode());
|
||||
esretval.putProperty ("result", ESNull.theNull, "result".hashCode());
|
||||
}
|
||||
return esretval;
|
||||
}
|
||||
|
||||
public ESValue getProperty (String name, int hash) throws EcmaScriptException {
|
||||
ESValue sprop = super.getProperty (name, hash);
|
||||
if (sprop != ESUndefined.theUndefined && sprop != ESNull.theNull)
|
||||
return sprop;
|
||||
String newRemoteObject = remoteObject == null ? name : remoteObject+"."+name;
|
||||
return new ESRemote (op, this.evaluator, url, newRemoteObject);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
97
src/helma/xmlrpc/fesi/FesiRpcServer.java
Normal file
97
src/helma/xmlrpc/fesi/FesiRpcServer.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 1999 Hannes Wallnoefer
|
||||
*/
|
||||
|
||||
package helma.xmlrpc.fesi;
|
||||
|
||||
import helma.xmlrpc.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import FESI.Data.*;
|
||||
import FESI.Interpreter.*;
|
||||
import FESI.Exceptions.*;
|
||||
|
||||
/**
|
||||
* An ESObject that makes its properties (sub-objects) callable via XML-RPC.
|
||||
* For example, if Server is an instance of FesiRpcServer, the following would make the
|
||||
* functions defined for someObject available to XML-RPC clients:
|
||||
* <pre>
|
||||
* Server.someObject = new SomeObject ();
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
|
||||
public class FesiRpcServer extends ObjectPrototype {
|
||||
|
||||
// This is public (for now) to be able to set access restrictions from the outside.
|
||||
public WebServer srv;
|
||||
Evaluator evaluator;
|
||||
|
||||
/**
|
||||
* Create an XML-RPC server with an already existing WebServer.
|
||||
*/
|
||||
public FesiRpcServer (WebServer srv, ESObject op, Evaluator eval) throws IOException, EcmaScriptException {
|
||||
super (op, eval);
|
||||
this.evaluator = eval;
|
||||
this.srv = srv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an XML-RPC server listening on a specific port.
|
||||
*/
|
||||
public FesiRpcServer (int port, ESObject op, Evaluator eval) throws IOException, EcmaScriptException {
|
||||
super (op, eval);
|
||||
this.evaluator = eval;
|
||||
srv = new WebServer (port);
|
||||
}
|
||||
|
||||
public void putProperty(String propertyName, ESValue propertyValue, int hash) throws EcmaScriptException {
|
||||
if (propertyValue instanceof ESObject)
|
||||
srv.addHandler (propertyName, new FesiInvoker ((ESObject) propertyValue));
|
||||
super.putProperty (propertyName, propertyValue, hash);
|
||||
}
|
||||
|
||||
public boolean deleteProperty (String propertyName, int hash) throws EcmaScriptException {
|
||||
srv.removeHandler (propertyName);
|
||||
super.deleteProperty (propertyName, hash);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
class FesiInvoker implements XmlRpcHandler {
|
||||
|
||||
ESObject target;
|
||||
|
||||
public FesiInvoker (ESObject target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public Object execute (String method, Vector argvec) throws Exception {
|
||||
// convert arguments
|
||||
int l = argvec.size ();
|
||||
|
||||
ESObject callTarget = target;
|
||||
if (method.indexOf (".") > -1) {
|
||||
StringTokenizer st = new StringTokenizer (method, ".");
|
||||
int cnt = st.countTokens ();
|
||||
for (int i=1; i<cnt; i++) {
|
||||
String next = st.nextToken ();
|
||||
try {
|
||||
callTarget = (ESObject) callTarget.getProperty (next, next.hashCode ());
|
||||
} catch (Exception x) {
|
||||
throw new EcmaScriptException ("The property \""+next+"\" is not defined in the remote object.");
|
||||
}
|
||||
}
|
||||
method = st.nextToken ();
|
||||
}
|
||||
|
||||
ESValue args[] = new ESValue[l];
|
||||
for (int i=0; i<l; i++) {
|
||||
args[i] = FesiRpcUtil.convertJ2E (argvec.elementAt (i), evaluator);
|
||||
}
|
||||
Object retval = FesiRpcUtil.convertE2J (callTarget.doIndirectCall (evaluator, callTarget, method, args));
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
85
src/helma/xmlrpc/fesi/FesiRpcUtil.java
Normal file
85
src/helma/xmlrpc/fesi/FesiRpcUtil.java
Normal file
|
@ -0,0 +1,85 @@
|
|||
// RpcXtension.java
|
||||
// Copyright (c) Hannes Wallnöfer, 1999 - All rights reserved
|
||||
|
||||
package helma.xmlrpc.fesi;
|
||||
|
||||
|
||||
import helma.xmlrpc.*;
|
||||
|
||||
import FESI.Exceptions.*;
|
||||
import FESI.Data.*;
|
||||
import FESI.Interpreter.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class FesiRpcUtil {
|
||||
|
||||
// convert a generic Java object to a JavaScript Object.
|
||||
public static ESValue convertJ2E (Object what, Evaluator evaluator) throws Exception {
|
||||
if (what == null)
|
||||
return ESNull.theNull;
|
||||
if (what instanceof Vector) {
|
||||
Vector v = (Vector) what;
|
||||
ArrayPrototype retval = new ArrayPrototype (evaluator.getArrayPrototype (), evaluator);
|
||||
int l = v.size ();
|
||||
for (int i=0; i<l; i++)
|
||||
retval.putProperty (i, convertJ2E (v.elementAt (i), evaluator));
|
||||
return retval;
|
||||
}
|
||||
if (what instanceof Hashtable) {
|
||||
Hashtable t = (Hashtable) what;
|
||||
ESObject retval = new ObjectPrototype (evaluator.getObjectPrototype (), evaluator);
|
||||
for (Enumeration e=t.keys(); e.hasMoreElements(); ) {
|
||||
String next = (String) e.nextElement ();
|
||||
retval.putProperty (next, convertJ2E (t.get (next), evaluator), next.hashCode ());
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
if (what instanceof String)
|
||||
return new ESString (what.toString ());
|
||||
if (what instanceof Number)
|
||||
return new ESNumber (new Double (what.toString ()).doubleValue ());
|
||||
if (what instanceof Boolean)
|
||||
return ESBoolean.makeBoolean (((Boolean) what).booleanValue ());
|
||||
if (what instanceof Date)
|
||||
return new DatePrototype (evaluator, (Date) what);
|
||||
return ESLoader.normalizeValue (what, evaluator);
|
||||
}
|
||||
|
||||
|
||||
// convert a JavaScript Object object to a generic Java.
|
||||
public static Object convertE2J (ESValue what) throws EcmaScriptException {
|
||||
if (XmlRpc.debug)
|
||||
System.out.println ("converting e-2-j: "+what.getClass ());
|
||||
if (what instanceof ESNull)
|
||||
return null;
|
||||
if (what instanceof ArrayPrototype) {
|
||||
ArrayPrototype a = (ArrayPrototype) what;
|
||||
int l = a.size ();
|
||||
Vector v = new Vector ();
|
||||
for (int i=0; i<l; i++) {
|
||||
Object nj = convertE2J (a.getProperty (i));
|
||||
v.addElement (nj);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
if (what instanceof ObjectPrototype) {
|
||||
ObjectPrototype o = (ObjectPrototype) what;
|
||||
Hashtable t = new Hashtable ();
|
||||
for (Enumeration e=o.getProperties (); e.hasMoreElements (); ) {
|
||||
String next = (String) e.nextElement ();
|
||||
if (XmlRpc.debug) System.out.println ("converting object member "+next);
|
||||
Object nj = convertE2J (o.getProperty (next, next.hashCode ()));
|
||||
if (nj != null) // can't put null as value in hashtable
|
||||
t.put (next, nj);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
if (what instanceof ESUndefined || what instanceof ESNull)
|
||||
return null;
|
||||
Object jval = what.toJavaObject ();
|
||||
if (jval instanceof Byte || jval instanceof Short)
|
||||
jval = new Integer (jval.toString ());
|
||||
return jval;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue