io.soluble.pjb.bridge.JavaBridge Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of php-java-bridge Show documentation
Show all versions of php-java-bridge Show documentation
PHPJavaBridge server (soluble fork)
/*
* Copyright (C) 2003-2007 Jost Boekemeier and others.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package io.soluble.pjb.bridge;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* This is the main interface of the PHP/Java Bridge. It contains utility methods which can be used by clients.
*
* @author Sam Ruby (methods coerce and select)
* @author Kai Londenberg
* @author Jost Boekemeier
* @see io.soluble.pjb.bridge.Standalone
* @see io.soluble.pjb.servlet.PhpJavaServlet
*/
public final class JavaBridge implements Runnable {
static final int DISPLAY_MAX_ELEMENTS = 10;
static final int DISPLAY_MAX_CHARS = 80;
static final String PHPSESSION = "PHPSESSION";
static final String INTERNAL_PHPSESSION = "INTERNAL_PHPSESSION";
static final HashMap SESSION_HASH = new HashMap();
private final MethodCache methodCache = new MethodCache();
private final ConstructorCache constructorCache = new ConstructorCache();
boolean canModifySecurityPermission = true; // false if we detect that setAccessible is not possible
StringCache stringCache = new StringCache(this);
Options options;
/**
* For PHP4's last_exception_get.
*/
Throwable lastException = null;
Throwable lastAsyncException; // reported by end_document()
// array of objects in use in the current script
GlobalRef globalRef = new GlobalRef();
/**
* For internal use only. The input stream for the current channel.
*/
public InputStream in;
/**
* For internal use only. The output stream for the current channel.
*/
public OutputStream out;
/**
* For internal use only. The request log level.
*/
public int logLevel = Util.logLevel;
/**
* For internal use only. The current request (if any)
*/
public Request request;
/**
* For internal use only.
*/
private IJavaBridgeFactory sessionFactory;
private Object o[] = new Object[1];
private Class c[] = new Class[1];
/**
* Return the log level:
*
* 0: log off
* 1: log fatal
* 2: log messages/exceptions
* 3: log verbose
* 4: log debug
* 5: log method invocations
*
* @return The request log level.
*/
public int getLogLevel() {
return logLevel;
}
/**
* Handle requests from the InputStream, write the responses to OutputStream
*
* @param in the InputStream
* @param out the OutputStream
* @param logger the default logger can be obtained via
* getServletContext().getAttribute(ContextLoaderListener.LOGGER)
* @throws IOException
* @deprecated Example:
*
*
* protected void doPut (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
*
* IContextFactory ctx = new RemoteHttpServletContextFactory(this, getServletContext(), req, req,
* resr);
* res.setHeader("X_JAVABRIDGE_CONTEXT", ctx.getId());
* res.setHeader("Pragma", "no-cache");
* res.setHeader("Cache-Control", "no-cache");
* try { ctx.getBridge().handleRequests(req.getInputStream(), res.getOutputStream(), myLogge); } finally
* { ctx.destroy(); }
* }
*
*
*/
public void handleRequests(InputStream in, OutputStream out, ILogger logger) throws IOException {
handleRequests(in, out);
}
/**
* Handle requests from the InputStream, write the responses to OutputStream
*
* @param in the InputStream
* @param out the OutputStream
* @throws IOException Example:
*
*
* protected void doPut (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
*
* IContextFactory ctx = new RemoteHttpServletContextFactory(this, getServletContext(), req, req,
* resr);
* res.setHeader("X_JAVABRIDGE_CONTEXT", ctx.getId());
* res.setHeader("Pragma", "no-cache");
* res.setHeader("Cache-Control", "no-cache");
* try { ctx.getBridge().handleRequests(req.getInputStream(), res.getOutputStream(), myLogge); } finally
* { ctx.destroy(); }
* }
*
*
*/
public void handleRequests(InputStream in, OutputStream out) throws IOException {
this.request = new Request(this);
this.in = in;
this.out = out;
if (this.request.init(this.in, this.out)) {
this.request.handleRequests();
} else {
Util.warn("handleHttpConnection init failed");
}
}
/**
* Return the session/jsr223 factory associated with this bridge
*
* @return The session/jsr223 factory
*/
public IJavaBridgeFactory getFactory() {
if (sessionFactory == null) {
throw new NullPointerException("session factory");
}
return sessionFactory;
}
/**
* Returns the connection options
*
* @return The Options.
*/
public Options getOptions() {
return options;
}
/**
* Communication with client in a new thread
*/
public void run() {
try {
logDebug("START: JavaBridge.run()");
request = new Request(this);
try {
if (!request.init(in, out)) {
return;
}
} catch (Throwable e) {
printStackTrace(e);
return;
}
try {
request.handleRequests();
} catch (Exception e) {
printStackTrace(e);
}
globalRef = null;
logDebug("END: JavaBridge.run()");
} catch (Exception t) {
printStackTrace(t);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
printStackTrace(e1);
}
}
if (out != null) {
try {
out.close();
} catch (IOException e2) {
printStackTrace(e2);
}
}
sessionFactory.destroy();
}
}
/**
* Create a new server socket and return it.
*
* @param sockname the socket name
* @return the server socket
* @throws IOException
*/
public static ISocketFactory bind(String sockname) throws IOException {
return Standalone.bind(Util.logLevel, sockname);
}
/**
* Global init. Redirects System.out and System.err to the server log file(s) or to System.err and creates and opens
* the communcation channel. Note: Do not write anything to System.out, this stream is connected with a pipe which
* waits for the channel name.
*
* @param s an array of [socketname, level, logFile]
*/
public static void init(String s[]) {
(new Standalone()).init(s);
}
// called by Standalone.init()
static void initLog(String socket, int logLevel, String s[]) {
String logFile = null, rawLogFile = null;
if (logLevel == -1) {
logLevel = Util.DEFAULT_LOG_LEVEL;
}
Util.logLevel = logLevel;
try {
try {
rawLogFile = logFile = s.length > 0 ? "" : Util.DEFAULT_LOG_FILE;
if (s.length > 2) {
rawLogFile = logFile = s[2];
if (Util.setConfiguredLogger(logFile)) {
logFile = null; // when log4j is used, System.out and System.err are not redirected
} else {
Util.setDefaultLogger(new FileLogger()); // use specified log file
}
if (Util.logLevel > 3) {
System.err.println(Util.EXTENSION_NAME + " log: " + rawLogFile);
}
}
} catch (Throwable t) {
t.printStackTrace();
}
Util.redirectOutput(logFile);
Util.logMessage("VM : " + Util.VM_NAME);
if (Util.VERSION != null) {
Util.logMessage(Util.EXTENSION_NAME + " version : " + Util.VERSION);
}
Util.logMessage("logFile : " + rawLogFile);
Util.logMessage("default logLevel : " + Util.logLevel);
Util.logMessage("socket : " + socket);
Util.logMessage("java.ext.dirs : " + System.getProperty("java.ext.dirs"));
Util.logMessage("io.soluble.pjb.bridge.base: " + Util.JAVABRIDGE_BASE);
Util.logMessage("thread pool size : " + Util.THREAD_POOL_MAX_SIZE);
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
// called by Standalone.init()
static void init(ISocketFactory socket, int logLevel, String s[]) {
try {
AppThreadPool pool = Util.createThreadPool(Util.EXTENSION_NAME + "ThreadPool");
try {
String policy = System.getProperty("java.security.policy");
String base = Util.JAVABRIDGE_BASE;
if (policy != null && base != null) {
SecurityManager manager = new JavaBridgeSecurityManager();
System.setSecurityManager(manager);
Util.logMessage(Util.EXTENSION_NAME + " policy base : " + base);
Util.logMessage(Util.EXTENSION_NAME + " security policy : " + policy);
}
} catch (Exception e) {
Util.logMessage("Cannot install security manager: " + e);
}
Util.logDebug("Starting to accept Socket connections");
while (true) {
Socket sock = socket.accept();
Util.logDebug("Socket connection accepted");
JavaBridge bridge = new SessionFactory().getBridge();
bridge.in = sock.getInputStream();
bridge.out = sock.getOutputStream();
if (pool != null) {
Util.logDebug("Starting bridge thread from thread pool");
pool.start(bridge); // Uses thread pool
} else {
Util.logDebug("Starting new bridge thread");
Thread t = new Util.Thread(bridge);
t.start();
}
}
} catch (IOException t) {
throw new RuntimeException(t);
}
}
/**
* Start the PHP/Java Bridge.
* Example:
* java -Djava.awt.headless=true -jar JavaBridge.jar INET:9656 5 /var/log/php-java-bridge.log
* Note: Do not write anything to System.out, this stream is connected with a pipe which waits for the channel name.
*
* @param s an array of [socketname, level, logFile] Use Standalone.main()
* @see io.soluble.pjb.bridge.Standalone#main(String[])
*/
public static void main(String s[]) {
Standalone.main(s);
}
/**
* Print a stack trace to the log file.
*
* @param t the throwable
*/
public void printStackTrace(Throwable t) {
if (logLevel > 0 && ((t instanceof Error) || logLevel > 1)) {
Util.getLogger().printStackTrace(t);
}
}
private String getId() {
return Integer.toHexString(System.identityHashCode(this)) + "@" + Integer.toHexString(System.identityHashCode(Thread.currentThread()));
}
/**
* Write a debug message
*
* @param msg The message
*/
public void logDebug(String msg) {
if (logLevel > 3) {
Util.println(4, getId() + " " + msg);
}
}
/**
* Write a fatal message
*
* @param msg The message
*/
public void logFatal(String msg) {
if (logLevel > 0) {
Util.println(1, getId() + " " + msg);
}
}
/**
* Write an error message.
*
* @param msg The message
*/
public void logError(String msg) {
if (logLevel > 1) {
Util.println(2, getId() + " " + msg);
}
}
/**
* Write a notice.
*
* @param msg The message
*/
public void logMessage(String msg) {
if (logLevel > 2) {
Util.println(3, getId() + " " + msg);
}
}
/**
* Write a warning.
*
* @param msg The warning.
*/
public void warn(String msg) {
if (logLevel > 0) {
Util.warn(getId() + " " + msg);
}
}
void setException(Response response, Throwable e, String method, Object obj, String name, Object args[], Class params[], boolean hasDeclaredExceptions) {
if (e instanceof InvocationTargetException) {
Throwable t = ((InvocationTargetException) e).getTargetException();
if (t != null) {
e = t;
}
if (logLevel > 3 || (!options.preferValues() && !hasDeclaredExceptions)) {
printStackTrace(e);
}
} else {
printStackTrace(e);
}
StringBuffer buf = new StringBuffer(method);
buf.append(" failed: ");
if (obj != null) {
buf.append("[");
Util.appendShortObject(obj, buf);
buf.append("]->");
} else {
buf.append("new ");
}
buf.append(name);
String arguments = Util.argsToString(args, params);
if (arguments.length() > 0) {
buf.append("(");
Util.appendArgs(args, params, buf);
buf.append(")");
}
buf.append(".");
buf.append(" Cause: ");
buf.append(String.valueOf(e));
buf.append(" VM: ");
buf.append(Util.VM_NAME);
lastException = new Exception(buf.toString(), e);
StackTraceElement[] trace = e.getStackTrace();
if (trace != null) {
lastException.setStackTrace(trace);
}
response.setResultException(lastException, hasDeclaredExceptions);
}
private Exception getUnresolvedExternalReferenceException(Throwable e, String what) {
return new ClassNotFoundException("Unresolved external reference: " + e + ". -- "
+ "Unable to " + what + ", see the README section \"Java platform issues\" "
+ "for details and DO NOT REPORT THIS PROBLEM TO THE PHP/Java Bridge MAILING LIST!", e);
}
/**
* Create an new instance of a given class, to be called by clients.
*
* @param name The class name
* @param createInstance true if we should create an instance, false otherwise
* @param args The argument array
* @param response The response writer
*/
public void CreateObject(String name, boolean createInstance,
Object args[], Response response) {
Class params[] = null;
LinkedList candidates = new LinkedList();
LinkedList matches = new LinkedList();
boolean hasDeclaredExceptions = true;
try {
Constructor selected = null;
ConstructorCache.Entry entry = null;
Class clazz = Util.classForName(name);
if (createInstance) {
entry = constructorCache.getEntry(name, args);
selected = constructorCache.get(entry);
if (selected == null) {
Constructor cons[] = clazz.getConstructors();
for (Constructor con : cons) {
candidates.add(con);
if (con.getParameterTypes().length == args.length) {
matches.add(con);
}
}
selected = (Constructor) select(matches, args);
if (selected != null) {
constructorCache.put(entry, selected);
}
}
}
if (selected == null) {
if (args.length > 0) {
throw createInstance
? (Exception) new InstantiationException("No matching constructor found. " + "Candidates: " + String.valueOf(candidates))
: (Exception) new JavaBridgeIllegalArgumentException("ReferenceClass must be called w/o arguments; either write new JavaClass(\"" + name + "\") or new Java(\"" + name + "\", args...).");
} else {
// for classes which have no visible constructor, return the class
// useful for classes like java.lang.System and java.util.Calendar.
if (createInstance && logLevel > 2) {
logMessage("No visible constructor found in: " + name + ", returning the class instead of an instance; this may not be what you want. Please correct this error or please use the function java(\"" + name + "\") instead.");
}
response.setResultClass(clazz);
return;
}
}
Object coercedArgs[] = coerce(params = entry.getParameterTypes(selected), args, response); //TODO possible null pointer
// If we have a logLevel of 5 or above, do very detailed invocation logging
hasDeclaredExceptions = selected.getExceptionTypes().length != 0;
if (this.logLevel > 4) {
Object result = selected.newInstance(coercedArgs);
logInvoke(result, name, coercedArgs);
response.setResult(result, clazz, hasDeclaredExceptions);
} else {
response.setResult(selected.newInstance(coercedArgs), clazz, hasDeclaredExceptions);
}
} catch (Throwable e) {
Throwable e1 = e;
if (e1 instanceof InvocationTargetException) {
e1 = ((InvocationTargetException) e1).getTargetException();
}
if (e1 instanceof Request.AbortException) {
throw (Request.AbortException) e1;
}
if (e1 instanceof OutOfMemoryError) {
Util.logFatal("OutOfMemoryError");
throw (OutOfMemoryError) e1; // abort
}
if (e1 instanceof NoClassDefFoundError) {
e = getUnresolvedExternalReferenceException(e1, "call constructor");
}
setException(response, e, createInstance ? "CreateInstance" : "ReferenceClass", null, name, args, params, hasDeclaredExceptions);
}
}
private static final Iterator EMPTY_ITERATOR = (new LinkedList()).iterator();
//
// Select the best match from a list of methods
//
private int weight(Class param, Class arg, Object phpArrayValue) {
int w = 0;
if (param.isAssignableFrom(arg)) {
for (Class clazz = arg; (clazz = clazz.getSuperclass()) != null;) {
if (!param.isAssignableFrom(clazz)) {
break;
}
w += 16; // prefer more specific arg, for
// example AbstractMap hashMap
// over Object hashMap.
}
} else if (param == java.lang.String.class) {
if (!(String.class.isAssignableFrom(arg)) && !(PhpString.class.isAssignableFrom(arg))) {
if (byte[].class.isAssignableFrom(arg)) {
w += 32;
} else {
w += 8000; // conversion to string is always possible
}
}
} else if (param.isArray()) {
if (PhpString.class.isAssignableFrom(arg)) {
Class clazz = param.getComponentType();
if (clazz == byte.class) {
w += 32;
} else {
w += 9999;
}
} else if (arg == PhpArray.class) {
Iterator iterator = phpArrayValue == null ? EMPTY_ITERATOR : ((Map) phpArrayValue).values().iterator();
if (iterator.hasNext()) {
Object elem = iterator.next();
Class ptype = param.getComponentType(), atype = elem.getClass();
if (ptype != atype) {
w += (ptype == Object.class ? 10 : 8200) + weight(ptype, atype, null);
}
}
} else if (arg.isArray()) {
Class ptype = param.getComponentType(), atype = arg.getComponentType();
if (ptype != atype) {
w += (ptype == Object.class ? 10 : 8200) + weight(ptype, atype, null);
}
} else {
w += 9999;
}
} else if ((java.util.Collection.class).isAssignableFrom(param)) {
if (java.util.Map.class.isAssignableFrom(arg)) {
w += 8100; // conversion to Collection is always possible
} else if (!(PhpArray.class.isAssignableFrom(arg))) {
w += 9999;
}
} else if (param.isPrimitive()) {
Class clazz = param;
if (Number.class.isAssignableFrom(arg)) {
if (Double.class.isAssignableFrom(arg)) {
if (clazz == Float.TYPE) {
w += 1;
} else if (clazz == Double.TYPE) {
w += 0;
} else {
w += 256;
}
} else {
if (clazz == Boolean.TYPE) {
w += 5;
} else if (clazz == Character.TYPE) {
w += 4;
} else if (clazz == Byte.TYPE) {
w += 3;
} else if (clazz == Short.TYPE) {
w += 2;
} else if (clazz == Integer.TYPE) {
w += 1;
} else if (clazz == Long.TYPE) {
w += 0;
} else {
w += 256;
}
}
} else if (Boolean.class.isAssignableFrom(arg)) {
if (clazz != Boolean.TYPE) {
w += 9999;
}
} else if (Character.class.isAssignableFrom(arg)) {
if (clazz != Character.TYPE) {
w += 9999;
}
} else if ((String.class.isAssignableFrom(arg)) || (PhpString.class.isAssignableFrom(arg))) {
w += 64;
} else {
w += 9999;
}
} else if (Number.class.isAssignableFrom(param)) {
if (param == Float.class || param == Double.class) {
if (!(Double.class.isAssignableFrom(arg))) {
w += 9999;
}
} else if (!(PhpExactNumber.class.isAssignableFrom(arg))) {
w += 9999;
}
} else {
w += 9999;
}
if (logLevel > 4) {
logDebug("weight " + param + " " + arg + ": " + w);
}
return w;
}
private Object select(LinkedList methods, Object args[]) {
if (methods.size() == 1) {
return methods.getFirst();
}
Object similar = null, selected = null;
int best = Integer.MAX_VALUE;
for (Iterator e = methods.iterator(); e.hasNext();) {
Object element = e.next();
int w = 0;
Class parms[] = (element instanceof Method)
? ((Method) element).getParameterTypes()
: ((Constructor) element).getParameterTypes();
for (int i = 0; i < parms.length; i++) {
Object arg = args[i];
if (arg != null) {
w += weight(parms[i], arg.getClass(), arg);
}
}
if (w < best) {
if (w == 0) {
if (logLevel > 4) {
logDebug("Selected: " + element + " " + w);
}
return element;
}
best = w;
selected = element;
if (logLevel > 2) {
similar = null;
if (logLevel > 4) {
logDebug("best: " + selected + " " + w);
}
}
} else {
if (logLevel > 2) {
if (w == best) {
similar = element;
}
if (logLevel > 4) {
logDebug("skip: " + element + " " + w);
}
}
}
}
if (logLevel > 2 && similar != null) {
StringBuffer buf = new StringBuffer();
for (Object arg : args) {
Util.appendParam(arg, buf);
}
logMessage("Portability warning: " + selected + " and " + similar + " both match " + buf.toString());
}
if (logLevel > 4) {
logDebug("Selected: " + selected + " " + best);
}
return selected;
}
Object coerce(Class param, Object arg, Response response) {
o[0] = arg;
c[0] = param;
return coerce(c, o, response)[0];
}
//
// Coerce arguments when possible to conform to the argument list.
// Java's reflection will automatically do widening conversions,
// unfortunately PHP only supports wide formats, so to be practical
// some (possibly lossy) conversions are required.
//
Object[] coerce(Class parms[], Object args[], Response response) {
Object arg;
Object result[] = args;
int size = 0;
for (int i = 0; i < args.length; i++) {
if ((arg = args[i]) == null) {
continue;
}
if (parms[i] == String.class) {
if (arg instanceof PhpString) {
result[i] = ((PhpString) arg).getString();
} else {
result[i] = arg.toString();
}
} else if (arg instanceof PhpString || arg instanceof String) {
if (!parms[i].isArray()) {
Class clazz = parms[i];
String s = (arg instanceof String) ? (String) arg : ((PhpString) arg).getString();
result[i] = s;
try {
if (clazz == Boolean.TYPE) {
result[i] = Boolean.valueOf(s);
} else if (clazz == Byte.TYPE) {
result[i] = new Byte(s);
} else if (clazz == Short.TYPE) {
result[i] = new Short(s);
} else if (clazz == Integer.TYPE) {
result[i] = new Integer(s);
} else if (clazz == Float.TYPE) {
result[i] = new Float(s);
} else if (clazz == Double.TYPE) {
result[i] = new Double(s);
} else if (clazz == Long.TYPE) {
result[i] = new Long(s);
} else if (clazz == Character.TYPE && s.length() > 0) {
result[i] = s.charAt(0);
}
} catch (NumberFormatException n) {
printStackTrace(n);
// oh well, we tried!
}
} else {
result[i] = ((PhpString) arg).getBytes();
}
} else if (arg instanceof Number) {
if (parms[i].isPrimitive()) {
Class clazz = parms[i];
Number n = (Number) arg;
if (clazz == Boolean.TYPE) {
result[i] = 0.0 != n.floatValue();
} else if (clazz == Byte.TYPE) {
result[i] = n.byteValue();
} else if (clazz == Short.TYPE) {
result[i] = n.shortValue();
} else if (clazz == Integer.TYPE) {
result[i] = n.intValue();
} else if (clazz == Float.TYPE) {
result[i] = n.floatValue();
} else if (clazz == Double.TYPE) {
result[i] = n.doubleValue();
} else if (clazz == Long.TYPE && !(n instanceof Long)) {
result[i] = n.longValue();
}
} else {
if (arg.getClass() == PhpExactNumber.class) {
{
Class clazz = parms[i];
if (clazz.isAssignableFrom(Integer.class)) {
result[i] = ((Number) arg).intValue();
} else {
result[i] = ((Number) arg).longValue();
}
}
}
}
} else if (arg instanceof PhpArray) {
if (parms[i].isArray()) {
Map.Entry e = null;
Object tempArray = null;
PhpArray ht = null;
Class targetType = parms[i].getComponentType();
try {
ht = (PhpArray) arg;
size = ht.arraySize();
// flatten hash into an array
targetType = parms[i].getComponentType();
tempArray = Array.newInstance(targetType, size);
} catch (Exception ex) {
//logError("Could not create array from Map: " + objectDebugDescription(arg) + ". Cause: " + ex);
throw new JavaBridgeIllegalArgumentException("Could not create array from Map: " + firstChars(arg), ex);
}
try {
for (Iterator ii = ht.entrySet().iterator(); ii.hasNext();) {
e = (Entry) ii.next();
Array.set(tempArray, ((Number) (e.getKey())).intValue(), coerce(targetType, e.getValue(), response));
}
result[i] = tempArray;
} catch (Exception ex) {
//logError("Could not create array of type: " + targetType + ", size: " + size + ", " + " failed entry at: " + e + ", from Map: " + objectDebugDescription(arg) + ". Cause: " + ex);
throw new JavaBridgeIllegalArgumentException("Could not create array of type: " + targetType + ", size: " + size + ", " + " failed entry at: " + e, ex);
}
} else if ((java.util.Collection.class).isAssignableFrom(parms[i])) {
try {
Map m = (Map) arg;
Collection coll = m.values();
if (!parms[i].isInstance(coll)) {
try { // could be a concrete class, for example LinkedList.
Collection collection = (Collection) parms[i].newInstance();
collection.addAll(coll);
coll = collection;
} catch (Exception e) { // it was an interface, try some concrete class
try {
coll = new ArrayList(coll);
} catch (Exception ex) {/*we've tried*/
}
}
}
result[i] = coll;
} catch (Exception ex) {
//logError("Could not create Collection from Map: " +objectDebugDescription(arg) + ". Cause: " + ex);
throw new JavaBridgeIllegalArgumentException("Could not create Collection from Map: " + firstChars(arg), ex);
}
} else if ((java.util.Map.class).isAssignableFrom(parms[i])) {
result[i] = arg;
} else if ((java.util.Hashtable.class).isAssignableFrom(parms[i])) {
try {
Map ht = (Map) arg;
Hashtable res;
res = (Hashtable) parms[i].newInstance();
res.putAll(ht);
result[i] = res;
} catch (Exception ex) {
logError("Could not create Hashtable from Map: " + objectDebugDescription(arg) + ". Cause: " + ex);
throw new JavaBridgeIllegalArgumentException("Could not create Hashtable from Map: " + firstChars(arg), ex);
}
} else if (arg instanceof PhpString) {
result[i] = ((PhpString) arg).getString(); // always prefer strings over byte[]
}
}
}
return result;
}
static abstract class FindMatchingInterface {
JavaBridge bridge;
String name;
Object args[];
boolean ignoreCase;
public FindMatchingInterface(JavaBridge bridge, String name, Object args[], boolean ignoreCase) {
this.bridge = bridge;
this.name = name;
this.args = args;
this.ignoreCase = ignoreCase;
}
abstract Class findMatchingInterface(Class jclass);
public boolean checkAccessible(AccessibleObject o) {
return true;
}
}
static final FindMatchingInterfaceVoid MATCH_VOID_ICASE = new FindMatchingInterfaceVoid(true);
static final FindMatchingInterfaceVoid MATCH_VOID_CASE = new FindMatchingInterfaceVoid(false);
static class FindMatchingInterfaceVoid extends FindMatchingInterface {
public FindMatchingInterfaceVoid(boolean b) {
super(null, null, null, b);
}
@Override
Class findMatchingInterface(Class jclass) {
return jclass;
}
@Override
public boolean checkAccessible(AccessibleObject o) {
if (!o.isAccessible()) {
try {
o.setAccessible(true);
} catch (java.lang.SecurityException ex) {
return false;
}
}
return true;
}
}
static class FindMatchingInterfaceForInvoke extends FindMatchingInterface {
protected FindMatchingInterfaceForInvoke(JavaBridge bridge, String name, Object args[], boolean ignoreCase) {
super(bridge, name, args, ignoreCase);
}
public static FindMatchingInterface getInstance(JavaBridge bridge, String name, Object args[], boolean ignoreCase, boolean canModifySecurityPermission) {
if (canModifySecurityPermission) {
return ignoreCase ? MATCH_VOID_ICASE : MATCH_VOID_CASE;
} else {
return new FindMatchingInterfaceForInvoke(bridge, name, args, ignoreCase);
}
}
@Override
Class findMatchingInterface(Class jclass) {
if (jclass == null) {
return jclass;
}
if (bridge.logLevel > 3) {
bridge.logDebug("searching for matching interface for Invoke for class " + jclass);
}
while (!Modifier.isPublic(jclass.getModifiers())) {
// OK, some joker gave us an instance of a non-public class
// This often occurs in the case of enumerators
// Substitute the matching first public interface in its place,
// and barring that, try the superclass
Class interfaces[] = jclass.getInterfaces();
Class superclass = jclass.getSuperclass();
for (int i = interfaces.length; i-- > 0;) {
if (Modifier.isPublic(interfaces[i].getModifiers())) {
jclass = interfaces[i];
Method methods[] = jclass.getMethods();
for (Method method : methods) {
String nm = method.getName();
boolean eq = ignoreCase ? nm.equalsIgnoreCase(name) : nm.equals(name);
if (eq && (method.getParameterTypes().length == args.length)) {
if (bridge.logLevel > 3) {
bridge.logDebug("matching interface for Invoke: " + jclass);
}
return jclass;
}
}
}
}
jclass = superclass;
}
if (bridge.logLevel > 3) {
bridge.logDebug("interface for Invoke: " + jclass);
}
return jclass;
}
}
static class FindMatchingInterfaceForGetSetProp extends FindMatchingInterface {
protected FindMatchingInterfaceForGetSetProp(JavaBridge bridge, String name, Object args[], boolean ignoreCase) {
super(bridge, name, args, ignoreCase);
}
public static FindMatchingInterface getInstance(JavaBridge bridge, String name, Object args[], boolean ignoreCase, boolean canModifySecurityPermission) {
if (canModifySecurityPermission) {
return ignoreCase ? MATCH_VOID_ICASE : MATCH_VOID_CASE;
} else {
return new FindMatchingInterfaceForGetSetProp(bridge, name, args, ignoreCase);
}
}
@Override
Class findMatchingInterface(Class jclass) {
if (jclass == null) {
return jclass;
}
if (bridge.logLevel > 3) {
bridge.logDebug("searching for matching interface for GetSetProp for class " + jclass);
}
while (!Modifier.isPublic(jclass.getModifiers())) {
// OK, some joker gave us an instance of a non-public class
// This often occurs in the case of enumerators
// Substitute the matching first public interface in its place,
// and barring that, try the superclass
Class interfaces[] = jclass.getInterfaces();
Class superclass = jclass.getSuperclass();
for (int i = interfaces.length; i-- > 0;) {
if (Modifier.isPublic(interfaces[i].getModifiers())) {
jclass = interfaces[i];
Field jfields[] = jclass.getFields();
for (Field jfield : jfields) {
String nm = jfield.getName();
boolean eq = ignoreCase ? nm.equalsIgnoreCase(name) : nm.equals(name);
if (eq) {
if (bridge.logLevel > 3) {
bridge.logDebug("matching interface for GetSetProp: " + jclass);
}
return jclass;
}
}
}
}
jclass = superclass;
}
if (bridge.logLevel > 3) {
bridge.logDebug("interface for GetSetProp: " + jclass);
}
return jclass;
}
}
private static ClassIterator getClassClassIterator(Class clazz) {
if (clazz == Class.class) {
return new MetaClassIterator();
}
return new ClassClassIterator();
}
private static abstract class ClassIterator {
Object object;
Class current;
FindMatchingInterface match;
public static ClassIterator getInstance(Object object, FindMatchingInterface match) {
ClassIterator c;
if (object instanceof Class) {
c = getClassClassIterator((Class) object);
} else {
c = new ObjectClassIterator();
}
c.match = match;
c.object = object;
c.current = null;
return c;
}
public abstract Class getNext();
public abstract boolean checkAccessible(AccessibleObject o);
public abstract boolean isVisible(int modifier);
}
static class ObjectClassIterator extends ClassIterator {
private Class next() {
if (current == null) {
return current = object.getClass();
}
return null;
}
@Override
public Class getNext() {
return match.findMatchingInterface(next());
}
@Override
public boolean checkAccessible(AccessibleObject o) {
return match.checkAccessible(o);
}
@Override
public boolean isVisible(int modifier) {
return true;
}
}
static class ClassClassIterator extends ClassIterator {
boolean hasNext = false;
private Class next() {
// check the class first, then the class class.
if (current == null) {
hasNext = true;
return current = (Class) object;
}
if (hasNext) {
hasNext = false;
return object.getClass();
}
return null;
}
@Override
public Class getNext() {
return next();
}
@Override
public boolean checkAccessible(AccessibleObject o) {
return true;
}
@Override
public boolean isVisible(int modifier) {
// all members of the class class or only static members of the class
return !hasNext || ((modifier & Modifier.STATIC) != 0);
}
}
static class MetaClassIterator extends ClassIterator {
private Class next() {
// The ClassClass has the ClassClass as its class
if (current == null) {
return current = (Class) object;
}
return null;
}
@Override
public Class getNext() {
return next();
}
@Override
public boolean checkAccessible(AccessibleObject o) {
return true;
}
@Override
public boolean isVisible(int modifier) {
return true;
}
}
private static void logInvoke(Object obj, String method, Object args[]) {
String dmsg = "\nInvoking " + objectDebugDescription(obj) + "." + method + "(";
for (int t = 0; t < args.length; t++) {
if (t > 0) {
dmsg += ",";
}
dmsg += objectDebugDescription(args[t]);
}
dmsg += ");\n";
Util.logDebug(dmsg);
}
private static void logResult(Object obj) {
String dmsg = "\nResult " + objectDebugDescription(obj) + "\n";
Util.logDebug(dmsg);
}
/**
* Invoke a method on a given object, to be called by clients.
*
* @param object The object
* @param method The method of the object
* @param args The argument array
* @param response The response writer
* @throws NullPointerException If the object was null
*/
public void Invoke(Object object, String method, Object args[], Response response) {
Class jclass;
boolean again;
Object coercedArgs[] = null;
Class params[] = null;
LinkedList candidates = new LinkedList();
LinkedList matches = new LinkedList();
Method selected = null;
boolean hasDeclaredExceptions = true;
try {
if (object == null) {
object = Request.PHPNULL;
throw new NullPointerException("cannot call \"" + method + "()\" on a Java null object. A previous Java call has returned a null value, use java_is_null($jvalue) to check.");
}
/* PR1616498: Do not use Util.getClass(): if object is a class, we must pass the class class.
* All VM, including gcc >= 3.3.3, return the class class for class.getClass(), not null. This is okay for the cache implementation. */
MethodCache.Entry entry = methodCache.getEntry(method, object, args);
selected = (Method) methodCache.get(entry);
// gather
do {
again = false;
ClassIterator iter;
if (selected == null) {
for (iter = ClassIterator.getInstance(object, FindMatchingInterfaceForInvoke.getInstance(this, method, args, true, canModifySecurityPermission)); (jclass = iter.getNext()) != null;) {
Method methods[] = jclass.getMethods();
for (Method meth : methods) {
if (meth.getName().equalsIgnoreCase(method) && iter.isVisible(meth.getModifiers())) {
candidates.add(meth);
if (meth.getParameterTypes().length == args.length) {
matches.add(meth);
}
}
}
}
selected = (Method) select(matches, args);
if (selected == null) {
checkM(object, String.valueOf(method) + "(" + Util.argsToString(args, params) + "). " + "Candidates: " + String.valueOf(candidates));
}
methodCache.put(entry, selected);
if (!iter.checkAccessible(selected)) {
logDebug("Security restriction: Cannot use setAccessible(), reverting to interface searching.");
canModifySecurityPermission = false;
candidates.clear();
matches.clear();
again = true;
}
}
coercedArgs = coerce(params = entry.getParameterTypes(selected), args, response);
} while (again);
hasDeclaredExceptions = selected.getExceptionTypes().length != 0; //TODO possible null pointer
// If we have a logLevel of 5 or above, do very detailed invocation logging
if (this.logLevel > 4) {
logInvoke(object, method, coercedArgs);
Object result = selected.invoke(object, coercedArgs);
logResult(result);
response.setResult(result, selected.getReturnType(), hasDeclaredExceptions);
} else {
response.setResult(selected.invoke(object, coercedArgs), selected.getReturnType(), hasDeclaredExceptions);
}
} catch (Throwable e) {
Throwable e1 = e;
if (e1 instanceof InvocationTargetException) {
e1 = ((InvocationTargetException) e1).getTargetException();
}
if (e1 instanceof Request.AbortException) {
throw (Request.AbortException) e1;
}
if (e1 instanceof OutOfMemoryError) {
Util.logFatal("OutOfMemoryError");
throw (OutOfMemoryError) e1; // abort
}
if (e1 instanceof NoClassDefFoundError) {
e = getUnresolvedExternalReferenceException(e1, "call the method");
}
if (selected != null && e1 instanceof IllegalArgumentException && this.logLevel > 3) {
String errMsg = "\nInvoked " + method + " on " + objectDebugDescription(object) + "\n";
errMsg += " Expected Arguments for this Method:\n";
Class paramTypes[] = selected.getParameterTypes();
for (int k = 0; k < paramTypes.length; k++) {
errMsg += " (" + k + ") " + classDebugDescription(paramTypes[k]) + "\n";
}
errMsg += " Plain Arguments for this Method:\n";
for (int k = 0; k < args.length; k++) {
errMsg += " (" + k + ") " + objectDebugDescription(args[k]) + "\n";
}
if (coercedArgs != null) {
errMsg += " Coerced Arguments for this Method:\n";
for (int k = 0; k < coercedArgs.length; k++) {
errMsg += " (" + k + ") " + objectDebugDescription(coercedArgs[k]) + "\n";
}
}
this.logDebug(errMsg);
}
setException(response, e, "Invoke", object, method, args, params, hasDeclaredExceptions);
}
}
private void checkM(Object object, String string) throws NoSuchMethodException {
if (object instanceof Class) {
throw new NoSuchProcedureException(string);
} else {
throw new NoSuchMethodException(string);
}
}
private static String firstChars(Object o) {
String append = "";
String s = o instanceof java.lang.reflect.Proxy ? o.getClass().getName() : String.valueOf(o);
int len = s.length();
if (len > DISPLAY_MAX_CHARS) {
append = "...";
len = DISPLAY_MAX_CHARS;
}
s = s.substring(0, len) + append;
return s;
}
/**
* Only for internal use
*
* @param ob The object
* @return A debug description.
*/
public static String objectDebugDescription(Object ob) {
return objectDebugDescription(ob, 0);
}
/**
* Only for internal use
*
* @param obj The object
* @return A debug description.
*/
private static String objectDebugDescription(Object ob, int level) {
if (ob == null) {
return "[Object null]";
}
Object obj = ob;
if (obj instanceof Collection) {
obj = ((Collection) obj).toArray();
} else if (obj instanceof List) {
obj = ((List) obj).toArray();
} else if (obj instanceof Map) {
obj = ((Map) obj).values().toArray();
}
if (level < DISPLAY_MAX_ELEMENTS && obj.getClass().isArray()) {
StringBuilder buf
= new StringBuilder("[Object " + System.identityHashCode(ob)
+ " - Class: " + classDebugDescription(ob.getClass()) + "]: ");
buf.append("{\n");
int length = Array.getLength(obj);
for (int i = 0; i < length; i++) {
buf.append(objectDebugDescription(Array.get(obj, i), level + 1));
if (i >= DISPLAY_MAX_ELEMENTS) {
buf.append("...\n");
break;
}
buf.append("\n");
}
buf.append("}");
return buf.toString();
} else {
return "[Object " + System.identityHashCode(obj) + " - Class: " + classDebugDescription(obj.getClass()) + "]";
}
}
/**
* Only for internal use
*
* @param cls The class
* @return A debug description.
*/
public static String classDebugDescription(Class cls) {
return cls.getName() + ":ID" + System.identityHashCode(cls) + ":LOADER-ID" + System.identityHashCode(cls.getClassLoader());
}
/**
* Get or Set a property, to be called by clients.
*
* @param object The object
* @param prop The object property
* @param args The argument array
* @param response The response writer
* @throws NullPointerException If the object is null
*/
public void GetSetProp(Object object, String prop, Object args[], Response response) throws NullPointerException {
LinkedList matches = new LinkedList();
boolean set = (args != null && args.length > 0);
Class params[] = null;
boolean hasDeclaredExceptions = true;
try {
Class jclass;
if (object == null) {
object = Request.PHPNULL;
throw new NullPointerException("cannot get property \"" + prop + "\" from a Java null object. A previous Java call has returned a null value, use java_is_null($jvalue) to check");
}
// first search for the field *exactly*
again2:
// because of security exception
for (ClassIterator iter = ClassIterator.getInstance(object, FindMatchingInterfaceForGetSetProp.getInstance(this, prop, args, false, canModifySecurityPermission)); (jclass = iter.getNext()) != null;) {
try {
Field jfields[] = jclass.getFields();
for (Field fld : jfields) {
if (fld.getName().equals(prop) && iter.isVisible(fld.getModifiers())) {
matches.add(fld.getName());
Object res = null;
if (!(iter.checkAccessible(fld))) {
logDebug("Security restriction: Cannot use setAccessible(), reverting to interface searching.");
canModifySecurityPermission = false;
matches.clear();
break again2;
}
Class ctype = fld.getType();
if (set) {
args = coerce(params = new Class[]{ctype}, args, response);
fld.set(object, args[0]);
} else {
res = fld.get(object);
}
response.setResult(res, ctype, false);
return;
}
}
} catch (Exception ee) {/* may happen when field is not static */
}
}
matches.clear();
// search for a getter/setter, ignore case
again1:
// because of security exception
for (ClassIterator iter = ClassIterator.getInstance(object, FindMatchingInterfaceForInvoke.getInstance(this, prop, args, true, canModifySecurityPermission)); (jclass = iter.getNext()) != null;) {
try {
BeanInfo beanInfo = Introspector.getBeanInfo(jclass);
PropertyDescriptor propDesc[] = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : propDesc) {
if (pd.getName().equalsIgnoreCase(prop)) {
Method method;
if (set) {
method = pd.getWriteMethod();
args = coerce(params = method.getParameterTypes(), args, response);
} else {
method = pd.getReadMethod();
}
matches.add(method);
if (!iter.checkAccessible(method)) {
logDebug("Security restriction: Cannot use setAccessible(), reverting to interface searching.");
canModifySecurityPermission = false;
matches.clear();
break again1;
}
hasDeclaredExceptions = method.getExceptionTypes().length != 0;
response.setResult(method.invoke(object, args), method.getReturnType(), hasDeclaredExceptions);
return;
}
}
} catch (Exception ee) {/* may happen when method is not static */
}
}
matches.clear();
// search for the field, ignore case
again0:
// because of security exception
for (ClassIterator iter = ClassIterator.getInstance(object, FindMatchingInterfaceForGetSetProp.getInstance(this, prop, args, true, canModifySecurityPermission)); (jclass = iter.getNext()) != null;) {
try {
Field jfields[] = jclass.getFields();
for (Field fld : jfields) {
if (fld.getName().equalsIgnoreCase(prop) && iter.isVisible(fld.getModifiers())) {
matches.add(prop);
Object res = null;
if (!(iter.checkAccessible(fld))) {
logDebug("Security restriction: Cannot use setAccessible(), reverting to interface searching.");
canModifySecurityPermission = false;
matches.clear();
break again0;
}
Class ctype = fld.getType();
if (set) {
args = coerce(params = new Class[]{ctype}, args, response);
fld.set(object, args[0]);
} else {
res = fld.get(object);
}
response.setResult(res, ctype, false);
return;
}
}
} catch (Exception ee) {/* may happen when field is not static */
}
}
checkF(object, String.valueOf(prop) + " (with args:" + Util.argsToString(args, params) + "). " + "Candidates: " + String.valueOf(matches));
} catch (Throwable e) {
Throwable e1 = e;
if (e1 instanceof InvocationTargetException) {
e1 = ((InvocationTargetException) e1).getTargetException();
}
if (e1 instanceof Request.AbortException) {
throw (Request.AbortException) e1;
}
if (e1 instanceof OutOfMemoryError) {
Util.logFatal("OutOfMemoryError");
throw (OutOfMemoryError) e1; // abort
}
if (e1 instanceof NoClassDefFoundError) {
e = getUnresolvedExternalReferenceException(e1, "invoke a property");
}
setException(response, e, set ? "SetProperty" : "GetProperty", object, prop, args, params, hasDeclaredExceptions);
}
}
private void checkF(Object object, String string) throws NoSuchFieldException {
if (object instanceof Class) {
throw new NoSuchConstantException(string);
} else {
throw new NoSuchFieldException(string);
}
}
/**
* Convert Map or Collection into a PHP array, sends the entire array, Map or Collection to the client. This is much
* more efficient than generating round-trips while iterating over the values of an array, Map or Collection.
*
* @param ob - The object to expand
* @return The passed ob
, will be expanded by the appropriate writer.
*/
public Object getValues(Object ob) {
Response res = request.response;
res.setArrayValuesWriter();
return ob;
}
/**
* Returns the PHP object associated with a closure
*
* @param closure The closure
* @return The php object
* @throws IllegalArgumentException
*/
public long unwrapClosure(Object closure) throws IllegalArgumentException {
return PhpProcedure.unwrap(castToExact(closure));
}
/**
* Cast a object to a type
*
* @param ob - The object to cast
* @param type - The target type
* @return The passed ob
, will be coerced by the appropriate writer.
*/
public Object cast(Object ob, Class type) {
Response res = request.response;
res.setCoerceWriter().setType(type);
return ob;
}
/**
* Cast an object to a string
*
* @param ob - The object to cast
* @return The passed ob
, will be coerced by the appropriate writer.
*/
public Object castToString(Object ob) {
return cast(ob, String.class);
}
/**
* Cast a throwable to a string
*
* @param throwable The throwable to cast
* @param trace The PHP stack trace
* @return The result String object, will be coerced by the appropriate writer.
*/
public Object castToString(Exception throwable, String trace) {
StringBuffer buf = new StringBuffer();
Util.appendObject(throwable, buf);
Util.appendTrace(throwable, trace, buf);
return castToString(buf);
}
/**
* Cast an object to an exact number
*
* @param ob - The object to cast
* @return The passed ob
, will be coerced by the appropriate writer.
*/
public Object castToExact(Object ob) {
return cast(ob, Long.TYPE);
}
/**
* Cast an object to a boolean value
*
* @param ob - The object to cast
* @return The passed ob
, will be coerced by the appropriate writer.
*/
public Object castToBoolean(Object ob) {
return cast(ob, Boolean.TYPE);
}
/**
* Cast an object to a inexact value
*
* @param ob - The object to cast
* @return The passed ob
, will be coerced by the appropriate writer.
*/
public Object castToInexact(Object ob) {
return cast(ob, Double.TYPE);
}
/**
* Cast an object to an array
*
* @param ob - The object to cast
* @return The passed ob
, will be coerced by the appropriate writer.
*/
public Object castToArray(Object ob) {
Response res = request.response;
res.setArrayValueWriter();
return ob;
}
/**
* Create a new bridge using a factory.
*
* @param factory The session/context factory.
*/
protected JavaBridge(IJavaBridgeFactory factory) {
setFactory(factory);
}
/**
* Return map for the value (PHP 5 only)
*
* @param value - The value which must be an array or implement Map or Collection.
* @return The PHP map.
* @see io.soluble.pjb.bridge.PhpMap
*/
public PhpMap getPhpMap(Object value) {
return PhpMap.getPhpMap(value, this);
}
/**
* For internal use only.
*
* @param object The java object
* @return A list of all visible constructors, methods, fields and inner classes.
*/
public String inspect(Object object) {
Class jclass;
ClassIterator iter;
StringBuilder buf = new StringBuilder("[");
buf.append(String.valueOf(Util.getClass(object)));
buf.append(":\nConstructors:\n");
for (iter = ClassIterator.getInstance(object, MATCH_VOID_CASE); (jclass = iter.getNext()) != null;) {
Constructor[] constructors = jclass.getConstructors();
for (Constructor constructor : constructors) {
buf.append(String.valueOf(constructor));
buf.append("\n");
}
}
buf.append("\nFields:\n");
for (iter = ClassIterator.getInstance(object, MATCH_VOID_CASE); (jclass = iter.getNext()) != null;) {
Field jfields[] = jclass.getFields();
for (Field jfield : jfields) {
buf.append(String.valueOf(jfield));
buf.append("\n");
}
}
buf.append("\nMethods:\n");
for (iter = ClassIterator.getInstance(object, MATCH_VOID_CASE); (jclass = iter.getNext()) != null;) {
Method jmethods[] = jclass.getMethods();
for (Method jmethod : jmethods) {
buf.append(String.valueOf(jmethod));
buf.append("\n");
}
}
buf.append("\nClasses:\n");
for (iter = ClassIterator.getInstance(object, MATCH_VOID_CASE); (jclass = iter.getNext()) != null;) {
Class jclasses[] = jclass.getClasses();
for (Class jclasse : jclasses) {
buf.append(String.valueOf(jclasse.getName()));
buf.append("\n");
}
}
buf.append("]");
return (String) castToString(buf.toString());
}
/**
* Set a new file encoding, used to code and decode strings. Example: setFileEncoding("UTF-8")
*
* @param fileEncoding The file encoding.
*/
public void setFileEncoding(String fileEncoding) {
options.setEncoding(fileEncoding.intern());
}
/**
* Check if object is an instance of class.
*
* @param ob The object
* @param claz The class or an instance of a class
* @return true if ob is an instance of class, false otherwise.
*/
public boolean InstanceOf(Object ob, Object claz) {
Class clazz = Util.getClass(claz);
return clazz.isInstance(castToBoolean(ob));
}
/**
* Returns a string representation of the object
*
* @param ob The object
* @return A string representation.
*/
public Object ObjectToString(Object ob) {
if (ob == null && !options.preferValues()) {
ob = Request.PHPNULL;
}
return castToString(Util.stringValueOf(ob));
}
/**
* Returns a string representation of the object
*
* @param ob The object
* @return A string representation.
*/
public Object ObjectToString(Boolean ob) {
if (ob == null) {
return ObjectToString((Object) null);
}
return castToString(ob ? "1" : "");
}
/**
* Returns a string representation of the object
*
* @param ob The object
* @return A string representation.
*/
public Object ObjectToString(String ob) {
if (ob == null) {
return ObjectToString((Object) null);
}
return castToString(ob);
}
/**
* Returns a string representation of the object
*
* @param ob The object
* @return A string representation.
*/
public Object ObjectToString(byte[] ob) {
if (ob == null) {
return ObjectToString((Object) null);
}
return castToString(ob);
}
/**
* Returns a string representation of the object
*
* @param ob The Throwable
* @param trace The stack trace
* @return A string representation.
*/
public Object ObjectToString(Throwable ob, String trace) {
StringBuffer buf = new StringBuffer("[");
try {
Util.appendObject(ob, buf);
Util.appendTrace(ob, trace, buf);
} catch (Request.AbortException sub) {
throw sub;
} catch (Exception t) {
Util.printStackTrace(t);
buf.append("[Exception in toString(): ");
buf.append(t);
if (t.getCause() != null) {
buf.append(", Cause: ");
buf.append(t.getCause());
}
buf.append("]");
}
buf.append("]");
return castToString(buf.toString());
}
private Object contextCache = null;
/**
* Returns the JSR223 context.
*
* @return The JSR223 context.
*/
public Object getContext() {
if (contextCache != null) {
return contextCache;
}
return contextCache = sessionFactory.getContext();
}
/**
* Return a session handle shared among all JavaBridge instances. If it is a HTTP session, the session is shared
* with the servlet or jsp.
*
* @param name The session name, if any
* @param clientIsNew true, if the client wants a new session
* @param timeout session timeout in seconds. If timeout is <= 0, the session will never expire @return The session context
* @return
* @throws Exception
* @see io.soluble.pjb.bridge.ISession
*/
public ISession getSession(String name, short clientIsNew, int timeout) throws Exception {
if (timeout == 0) {
timeout = -1;
}
try {
return sessionFactory.getSession(name, clientIsNew, timeout);
} catch (Exception t) {
printStackTrace(t);
throw t;
}
}
/**
* Create a dynamic proxy proxy for calling PHP code.
* Example:
* java_closure($this, $map);
*
* @param object the PHP environment (the php instance)
* @param names maps java to php names
* @return the proxy
*/
public Object makeClosure(long object, Map names) {
if (names == null) {
return makeClosure(object);
}
return PhpProcedure.createProxy(getFactory(), null, names, Util.ZERO_PARAM, object);
}
/**
* Create a dynamic proxy proxy for calling PHP code.
* Example:
* java_closure($this, $map, $interfaces);
*
* @param object the PHP environment (the php instance)
* @param names maps java to php names
* @param interfaces list of interfaces which the PHP environment must implement
* @return the proxy
*/
public Object makeClosure(long object, Map names, Class interfaces[]) {
if (names == null) {
names = EMPTY_MAP;
}
return PhpProcedure.createProxy(getFactory(), null, names, interfaces, object);
}
/**
* Create a dynamic proxy proxy for calling PHP code.
* Example:
* java_closure($this, $map, $interfaces);
*
* @param object the PHP environment (the php instance)
* @param name maps all java names to this php name
* @param iface interface which the PHP environment must implement
* @return the proxy
*/
public Object makeClosure(long object, String name, Class iface) {
Class[] interfaces = iface == null ? null : new Class[]{iface};
return makeClosure(object, name, interfaces);
}
/**
* Create a dynamic proxy proxy for calling PHP code.
* Example:
* java_closure($this, $map, $interfaces);
*
* @param object the PHP environment (the php instance)
* @param names maps java to php names
* @param iface interface which the PHP environment must implement
* @return the proxy
*/
public Object makeClosure(long object, Map names, Class iface) {
Class[] interfaces = iface == null ? null : new Class[]{iface};
return makeClosure(object, names, interfaces);
}
/**
* Create a dynamic proxy proxy for calling PHP code.
* Example:
* java_closure($this, "clickMe");
*
* @param object the PHP environment (the php instance)
* @param name maps all java names to this php name
* @return the proxy
*/
public Object makeClosure(long object, String name) {
if (name == null) {
return makeClosure(object);
}
return PhpProcedure.createProxy(getFactory(), name, null, Util.ZERO_PARAM, object);
}
/**
* Create a dynamic proxy proxy for calling PHP code.
* Example:
* java_closure($this, "clickMe", $interfaces);
*
* @param object the PHP environment (the php instance)
* @param name maps all java names to this php name
* @param interfaces list of interfaces which the PHP environment must implement
* @return the proxy
*/
public Object makeClosure(long object, String name, Class interfaces[]) {
if (name == null) {
return makeClosure(object, EMPTY_MAP, interfaces);
}
return PhpProcedure.createProxy(getFactory(), name, null, interfaces, object);
}
private static final HashMap EMPTY_MAP = new HashMap();
/**
* Create a dynamic proxy proxy for calling PHP code.
* Example:
* java_closure();
* java_closure($this);
*
* @param object the PHP environment (the php instance)
* @return the proxy
*/
public Object makeClosure(long object) {
return PhpProcedure.createProxy(getFactory(), null, EMPTY_MAP, Util.ZERO_PARAM, object);
}
/**
* This method sets a new session factory. Used by the servlet to implement session sharing.
*
* @param sessionFactory The sessionFactory to set.
*/
void setFactory(IJavaBridgeFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Load the object from the session store. The C code requires that this method is called "deserialize" even though
* it doesn't deserialize anything. See the JSessionAdapter in the php_java_lib folder. For real
* serialization/deserialization see the JPersistenceAdapter in the php_java_lib folder.
*
* @param serialID The key
* @param timeout The timeout, usually 1400 seconds.
* @return the new object identity.
* @throws IllegalArgumentException if serialID does not exist anymore.
*/
public int deserialize(String serialID, int timeout) throws IllegalArgumentException {
ISession session = sessionFactory.getSession(JavaBridge.INTERNAL_PHPSESSION, ISession.SESSION_GET_OR_CREATE, timeout);
Object obj = session.get(serialID);
if (obj == null) {
throw new IllegalArgumentException("Session serialID " + serialID + " expired.");
}
return globalRef.append(castToExact(obj));
}
private static int counter = 0;
private static synchronized int getSerialID() {
return counter++;
}
/**
* Store the object in the session store and return the serial id. The C code requires that this method is called
* "serialize" even though it doesn't serialize anything. See the JSessionAdapter in the php_java_lib folder. For
* real serialization/deserialization see the JPersistenceAdapter in the php_java_lib folder.
*
* @param obj The object
* @param timeout The timeout, usually 1400 seconds
* @return the serialID
*/
public String serialize(Object obj, int timeout) throws IllegalArgumentException {
if (obj == null) {
obj = Request.PHPNULL;
}
ISession session = sessionFactory.getSession(JavaBridge.INTERNAL_PHPSESSION, ISession.SESSION_GET_OR_CREATE, timeout);
String id = Integer.toHexString(getSerialID());
session.put(id, obj);
return (String) castToString(id);
}
/**
* Checks if a given position exists.
*
* @param value The map.
* @param pos The position
* @return true if an element exists at this position, false otherwise.
*/
private boolean offsetExists(Map value, Object pos) {
castToBoolean(null);
return value.containsKey(pos);
}
/**
* Returns the object at the posisition.
*
* @param value The map.
* @param pos The position.
* @return The object at the given position.
*/
private Object offsetGet(Map value, Object pos) {
return value.get(pos);
}
/**
* Set an object at position.
*
* @param value The map.
* @param pos The position.
* @param val The object.
*/
private void offsetSet(Map value, Object pos, Object val) {
Class type = value.getClass().getComponentType();
if (type != null) {
val = coerce(type, val, request.response);
}
if (pos == null) {
pos = value.size();
}
value.put(pos, val);
}
/**
* Remove an object from the position.
*
* @param value The map.
* @param pos The position.
*/
private void offsetUnset(Map value, Object pos) {
value.remove(pos);
}
/**
* Checks if a given position exists.
*
* @param value The list.
* @param pos The position
* @return true if an element exists at this position, false otherwise.
*/
private boolean offsetExists(List value, int pos) {
castToBoolean(null);
try {
offsetGet(value, pos);
return true;
} catch (IndexOutOfBoundsException ex) {
return false;
}
}
/**
* Returns the object at the posisition.
*
* @param value The list.
* @param pos The position.
* @return The object at the given position.
*/
private Object offsetGet(List value, int pos) {
return value.get(pos);
}
/**
* Set an object at position.
*
* @param value The list.
* @param pos The position.
* @param val The object.
*/
private void offsetSet(List value, Number off, Object val) {
if (off == null) {
value.add(val);
} else {
int pos = off.intValue();
value.set(pos, val);
}
}
/**
* Remove an object from the position.
*
* @param value The list.
* @param pos The position.
*/
private void offsetUnset(List value, int pos) {
value.remove(pos);
}
boolean offsetExists(int length, int pos) {
castToBoolean(null);
int i = pos;
return (i > 0 && i < length);
}
/**
* Checks if a given position exists.
*
* @param value The array.
* @param pos The position
* @return true if an element exists at this position, false otherwise.
*/
private boolean offsetExists(Object value, int pos) {
return offsetExists(Array.getLength(value), pos);
}
/**
* Checks if a given position exists.
*
* @param table The table.
* @param off The offset
* @return true if an element exists at this position, false otherwise.
*/
public boolean offsetExists(Object table, Object off) {
if (table.getClass().isArray()) {
return offsetExists(table, ((Number) off).intValue());
}
if (table instanceof List) {
return offsetExists((List) table, ((Number) off).intValue());
}
return offsetExists((Map) table, off);
}
/**
* Returns the object at the posisition.
*
* @param value The array.
* @param pos The position.
* @return The object at the given position.
*/
private Object offsetGet(Object value, int pos) {
int i = pos;
Object obj = Array.get(value, i);
return obj == this ? null : obj;
}
/**
* Returns the object at the posisition.
*
* @param table The table.
* @param off The offset.
* @return The object at the given position.
*/
public Object offsetGet(Object table, Object off) {
if (table.getClass().isArray()) {
return offsetGet(table, ((Number) off).intValue());
}
if (table instanceof List) {
return offsetGet((List) table, ((Number) off).intValue());
}
return offsetGet((Map) table, off);
}
/**
* Selects the asynchronous protocol mode.
*/
public void beginDocument() {
Response res = request.response;
res.setObjectWriter();
lastAsyncException = null;
}
/**
* Back to synchronous protocol mode
*
* @throws Throwable
*/
public void endDocument() throws Throwable {
Response res = request.response;
res.setDefaultWriter();
if (lastAsyncException != null) {
throw lastAsyncException;
}
}
/**
* Set an object at position.
*
* @param value The array.
* @param pos The position.
* @param val The object.
*/
private void offsetSet(Object value, int pos, Object val) {
Array.set(value, pos, coerce(value.getClass().getComponentType(), val, request.response));
}
/**
* Set an object at position.
*
* @param table The table
* @param off The offset.
* @param val The value
*/
public void offsetSet(Object table, Object off, Object val) {
if (table.getClass().isArray()) {
offsetSet(table, ((Number) off).intValue(), val);
} else if (table instanceof List) {
offsetSet((List) table, ((Number) off), val);
} else {
offsetSet((Map) table, off, val);
}
}
/**
* Remove an object from the position.
*
* @param value The array.
* @param pos The position.
*/
private void offsetUnset(Object value, int pos) {
int i = pos;
Array.set(value, i, null);
}
/**
* Remove an object from the position.
*
* @param table The table.
* @param off The offset.
*/
public void offsetUnset(Object table, Object off) {
if (table.getClass().isArray()) {
offsetUnset(table, ((Number) off).intValue());
} else if (table instanceof List) {
offsetUnset((List) table, ((Number) off).intValue());
} else {
offsetUnset((Map) table, off);
}
}
/**
* Re-initialize the current bridge for keep-alive See php.ini option java.persistent_connections
*/
public void recycle() {
this.contextCache = null;
globalRef = new GlobalRef();
options.recycle();
/* resets the bridge: make sure to set the original bridge before calling sessionFactory.recycle() */
request.recycle();
/* resets the currentThreadContextClassLoader from the bridge's loader */
sessionFactory.recycle();
methodCache.clear();
constructorCache.clear();
stringCache.clear();
lastException = lastAsyncException = null;
}
/**
* Return a new string using the current file encoding (see java_set_file_encoding()).
*
* @param b The byte array
* @param start The start index
* @param length The number of bytes to encode.
* @return The encoded string.
*/
public String getString(byte[] b, int start, int length) {
// return stringCache.getString(b, start, length, options.getEncoding());
try {
return new String(b, start, length, options.getEncoding());
} catch (UnsupportedEncodingException e) {
printStackTrace(e);
return new String(b, start, length);
}
}
/**
* Return a cached string using the current file encoding (see java_set_file_encoding()).
*
* @param b The byte array
* @param start The start index
* @param length The number of bytes to encode.
* @return The encoded string.
*/
public String getCachedString(byte[] b, int start, int length) {
return stringCache.getString(b, start, length, options.getEncoding());
}
/**
* Create a response object for this bridge, according to options.
*
* @return The response object
*/
Response createResponse() {
return new Response(this);
}
/**
* Check if a given class exists.
*
* @param name The class name
* @return true if the type exists, false otherwise
*/
public boolean typeExists(String name) {
try {
Util.classForName(name);
castToBoolean(null);
return true;
} catch (ClassNotFoundException ex) {/*ignore*/
}
castToBoolean(null);
return false;
}
/**
* Return the first java.lang.RuntimeException/java.lang.Error in a chain or the last java.lang.Exception.
*
* @return the last stored exception or null
*/
public Throwable getLastException() {
request.response.setCoerceWriter().setType(Object.class);
if (lastAsyncException != null) {
return lastAsyncException;
}
return lastException;
}
protected Throwable setLastAsyncException(Throwable t) {
if (lastAsyncException == null) {
lastException = lastAsyncException = t;
}
return t;
}
/**
* Clear the last Exception
*/
public void clearLastException() {
lastException = lastAsyncException = null;
}
}