io.soluble.pjb.bridge.JavaBridgeRunner Maven / Gradle / Ivy
Show all versions of php-java-bridge Show documentation
/*
* Copyright (C) 2003-2007 Jost Boekemeier
*
* 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.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import io.soluble.pjb.bridge.http.ChunkedInputStream;
import io.soluble.pjb.bridge.http.ChunkedOutputStream;
import io.soluble.pjb.bridge.http.ContextFactory;
import io.soluble.pjb.bridge.http.ContextServer;
import io.soluble.pjb.bridge.http.HttpRequest;
import io.soluble.pjb.bridge.http.HttpResponse;
import io.soluble.pjb.bridge.http.HttpServer;
import io.soluble.pjb.bridge.http.RemoteHttpContextFactory;
/**
* This is the main entry point for the PHP/Java Bridge library.
* Example:
* public MyClass {
* public static void main(String s[]) {
* JavaBridgeRunner runner = JavaBridgeRunner.getInstance();
* // connect to port 9267 and send protocol requests ...
* runner.destroy();
* }
* }
*
* @author jostb
* @see io.soluble.pjb.script.PhpScriptContext
*/
public class JavaBridgeRunner extends HttpServer {
protected static JavaBridgeRunner runner;
protected final ContextServer contextServer;
private boolean directoryIndexEnabled = true;
protected JavaBridgeRunner(String serverPort, boolean isSecure) throws IOException {
super(serverPort, isSecure);
contextServer = new ContextServer(ContextFactory.EMPTY_CONTEXT_NAME, Util.JAVABRIDGE_PROMISCUOUS);
}
protected JavaBridgeRunner(String serverPort) throws IOException {
this(serverPort, false);
}
/**
* Create a new JavaBridgeRunner and ContextServer.
*
* @throws IOException
* @see ContextServer
*/
protected JavaBridgeRunner() throws IOException {
this(null);
}
/**
* Return the ContextServer
*
* @return the ContextServer
*/
public ContextServer getContextServer() {
return contextServer;
}
/**
* Return a instance.
*
* @param serverPort The server port name
* @param isSecure use https instead of http
* @return a standalone runner
* @throws IOException
*/
public static synchronized JavaBridgeRunner getRequiredInstance(String serverPort, boolean isSecure) throws IOException {
if (runner != null) return runner;
runner = new JavaBridgeRunner(serverPort, isSecure);
return runner;
}
/**
* Return a instance.
*
* @param serverPort The server port name
* @param isSecure use https instead of http
* @return a standalone runner
*/
public static synchronized JavaBridgeRunner getInstance(String serverPort, boolean isSecure) {
if (runner != null) return runner;
try {
runner = new JavaBridgeRunner(serverPort, isSecure);
} catch (IOException e) {
Util.printStackTrace(e);
}
return runner;
}
/**
* Return a instance.
*
* @param serverPort The server port name
* @return a standalone runner
* @throws IOException
*/
public static synchronized JavaBridgeRunner getRequiredInstance(String serverPort) throws IOException {
if (runner != null) return runner;
runner = new JavaBridgeRunner(serverPort);
return runner;
}
/**
* Return a instance.
*
* @param serverPort The server port name
* @return a standalone runner
*/
public static synchronized JavaBridgeRunner getInstance(String serverPort) {
if (runner != null) return runner;
try {
runner = new JavaBridgeRunner(serverPort);
} catch (IOException e) {
Util.printStackTrace(e);
}
return runner;
}
/**
* Return a instance.
*
* @return a standalone runner
* @throws IOException
*/
public static synchronized JavaBridgeRunner getRequiredInstance() throws IOException {
if (runner != null) return runner;
runner = new JavaBridgeRunner();
return runner;
}
/**
* Enable listing of directory index, if a directory is served.
*/
public void enableDirectoryIndex() {
directoryIndexEnabled = true;
}
/**
* Disable listing of directory index, if a directory is served.
*/
public void disableDirectoryIndex() {
directoryIndexEnabled = false;
}
/**
* Create a server socket.
*
* @param addr The host address, either INET:port or INET_LOCAL:port
* @return The server socket.
* @throws IOException
*/
@Override
public ISocketFactory bind(String addr) throws IOException {
socket = JavaBridge.bind(addr);
return socket;
}
/**
* Create a server socket.
*
* @param addr The host address, either INET:port or INET_LOCAL:port
* @return The server socket.
* @throws IOException
*/
@Override
public ISocketFactory bindSecure(String addr) throws IOException {
boolean isLocal = true;
if (addr.startsWith("INET_LOCAL:")) {
addr = addr.substring(11);
} else if (addr.startsWith("INET:")) {
isLocal = false;
addr = addr.substring(5);
}
return SSLServerSocketHelper.bind(Integer.parseInt(addr), Util.BACKLOG, isLocal);
}
private static String getHeader(String key, HttpRequest req) {
String val = req.getHeader(key);
if (val == null) return null;
if (val.length() == 0) val = null;
return val;
}
/**
* Handles both, override-redirect and redirect, see
* see io.soluble.pjb.servlet.PhpJavaServlet#handleSocketConnection(HttpServletRequest, HttpServletResponse, String, boolean)
* see io.soluble.pjb.servlet.PhpJavaServlet#handleRedirectConnection(HttpServletRequest, HttpServletResponse)
* @param req
* @param res
* @throws java.io.IOException
*/
@Override
protected void doPut(HttpRequest req, HttpResponse res) throws IOException {
ChunkedInputStream sin;
ChunkedOutputStream sout;
String transferEncoding = getHeader("Transfer-Encoding", req);
boolean isChunked = "chunked".equals(transferEncoding);
if (!isChunked) throw new IllegalStateException("Please use a JEE server or servlet engine.");
sin = new ChunkedInputStream(req.getInputStream());
sout = new ChunkedOutputStream(res.getOutputStream());
RemoteHttpContextFactory ctx = new RemoteHttpContextFactory(req, res);
res.setHeader(Util.X_JAVABRIDGE_CONTEXT, ctx.getId());
res.setHeader("Pragma", "no-cache");
res.setHeader("Keep-Alive", "timeout=-1, max=-1");
try {
ctx.getBridge().handleRequests(sin, sout);
sin.eof();
sout.eof();
} finally {
ctx.destroy();
}
}
/**
* Return the current directory to the browser
*
* @param fullName The full name of the file
* @param f The full name as a file
* @param length The length of the file
* @param req The HTTP request object
* @param res The HTTP response object
* @return true if the runner could show the directory, false otherwise
* @throws IOException
*/
protected boolean showDirectory(String fullName, File f, int length, HttpRequest req, HttpResponse res) throws IOException {
// assert f.isDirectory()
ByteArrayOutputStream xout = new ByteArrayOutputStream();
try (PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(xout, Util.UTF8)))) {
out.println("");
out.println("");
out.println("Directory Listing for " + fullName + "/ ");
out.println(" ");
File parentFile = f.getParentFile();
String parentName = parentFile == null ? "/" : parentFile.getName();
out.println("Directory Listing for " + fullName + " - Up To " + parentName + "
");
out.println("");
out.println("Filename ");
out.println("Type ");
out.println("Size ");
out.println("Last Modified ");
out.println("");
out.println(" ");
File[] dir = f.listFiles();
int count = 0;
StringBuilder b = new StringBuilder();
for (File file : dir) {
if (file.isHidden()) continue;
boolean even = count++ % 2 == 0;
if (even)
out.println("");
else
out.println(" ");
// mozilla replaces everything after the last slash:
// foo/bar baz becomes foo/baz and foo/bar/ baz becomes foo/bar/baz
if (fullName.length() != 0 && !fullName.endsWith("/")) {
b.append(f.getName());
b.append("/");
}
b.append(file.getName());
if (file.isDirectory()) b.append("/");
out.println(" ");
if (file.isDirectory()) {
out.println("" + file.getName() + "/ ");
out.println(" ");
out.println("directory ");
} else {
out.println("" + file.getName() + "");
out.println(" ");
out.println("file ");
}
out.println("" + file.length() + " ");
out.println("" + Util.formatDateTime(file.lastModified()) + " ");
out.println(" ");
b.setLength(0);
}
out.println("
");
out.println("
Simple JSR 223 enabled web server version 0.0.1
");
out.println("Available script engines
");
try {
Class c = Class.forName("javax.script.ScriptEngineManager");
Object o = c.newInstance();
Method ex = c.getMethod("getEngineByExtension", new Class[]{String.class});
if (ex.invoke(o, (Object[]) new String[]{"php"}) == null) {
out.println("Warning: php-script.jar not found. Please copy it to the directory containing JavaBridge.jar before starting JavaBridge.
");
}
Method e = c.getMethod("getEngineFactories", new Class[]{});
List factories = (List) e.invoke(o, new Object[]{});
StringBuffer buf = new StringBuffer();
for (Iterator ii = factories.iterator(); ii.hasNext(); ) {
o = ii.next();
Method getName = o.getClass().getMethod("getEngineName", new Class[]{});
Method getVersion = o.getClass().getMethod("getEngineVersion", new Class[]{});
Method getNames = o.getClass().getMethod("getNames", new Class[]{});
Method getExtensions = o.getClass().getMethod("getExtensions", new Class[]{});
buf.append("- ");
buf.append(getName.invoke(o, new Object[]{}));
buf.append(", ");
buf.append("ver.: ");
buf.append(getVersion.invoke(o, new Object[]{}));
buf.append(", ");
buf.append("alias: ");
buf.append(getNames.invoke(o, new Object[]{}));
buf.append(", ");
buf.append(".ext: ");
buf.append(getExtensions.invoke(o, new Object[]{}));
buf.append("
");
out.println(buf);
buf.setLength(0);
}
} catch (Exception e) {
e.printStackTrace();
}
out.println("
");
out.println("");
out.println("");
res.addHeader("Content-Type", "text/html; charset=UTF-8");
res.addHeader("Last-Modified", Util.formatDateTime(f.lastModified()));
}
int outLength = xout.size();
res.setContentLength(outLength);
xout.writeTo(res.getOutputStream());
return true;
}
/**
* Evaluate the script engine. The engine is searched through the discovery mechanism. Add the "php-script.jar" or some other
* JSR223 script engine to the java ext dirs (usually /usr/share/java/ext or /usr/java/packages/lib/ext) and start the HTTP server:
* java -jar JavaBridge.jar HTTP_LOCAL:8080. Browse to http://localhost:8080/test.php.
*
* @param name
* @param f The full name as a file
* @param params The request parameter
* @param length The length of the file
* @param req The HTTP request object
* @param res The HTTP response object
* @return true if the runner could evaluate the script, false otherwise.
* @throws IOException
*/
protected boolean handleScriptContent(String name, String params, File f, int length, HttpRequest req, HttpResponse res) throws IOException {
return true;
}
/**
* Display a simple text file.
*
* @param name
* @param f The full name as a file
* @param params The request parameter
* @param length The length of the file
* @param req The HTTP request object
* @param res The HTTP response object
* @param show
* @throws IOException
*/
protected void showTextFile(String name, String params, File f, int length, HttpRequest req, HttpResponse res, boolean show) throws IOException {
byte[] buf;
OutputStream out;
int c;
if (Util.logLevel > 4) Util.logDebug("web server: show text file:" + name);
res.addHeader("Last-Modified", Util.formatDateTime(f.lastModified()));
if (show) res.addHeader("Content-Type", "text/plain");
res.setContentLength(length);
try (InputStream in = new FileInputStream(f)) {
buf = new byte[Util.BUF_SIZE];
out = res.getOutputStream();
while ((c = in.read(buf)) != -1) out.write(buf, 0, c);
}
}
/**
* Handle doGet requests. For example java_require("http://localhost:8080/JavaBridge/java/Java.inc");
*
* Since each script may also consume a HttpServer continuation (PUT ...), there
* cannot be more than THREAD_POOL_MAX_SIZE/2 GET requests.
*
* @param req The HttpRequest
* @param res The HttpResponse
* @throws java.io.IOException
*/
@Override
protected void doGet(HttpRequest req, HttpResponse res) throws IOException {
handleDoGet(req, res);
}
private byte[] cache;
/**
* Handle doGet requests. For example java_require("http://localhost:8080/JavaBridge/java/Java.inc");
*
* @param req The HttpRequest
* @param res The HttpResponse
* @throws java.io.IOException
*/
protected void handleDoGet(HttpRequest req, HttpResponse res) throws IOException {
byte[] buf;
OutputStream out;
int c;
String name = req.getRequestURI();
if (name == null) {
super.doGet(req, res);
return;
}
if (!name.startsWith("/JavaBridge")) {
if (name.startsWith("/")) name = name.substring(1);
String params = null;
int idx = name.indexOf('?');
if (idx != -1) {
params = name.substring(idx + 1);
name = name.substring(0, idx);
}
File f = Standalone.getCanonicalWindowsFile(name);
if (f == null || !f.exists()) f = new File(Util.HOME_DIR, name);
if (!f.exists()) return;
if (f.isHidden()) return;
long l = f.length();
if (l >= Integer.MAX_VALUE) throw new IOException("file " + name + " too large");
int length = (int) l;
if (f.isDirectory()) {
if (directoryIndexEnabled) {
showDirectory(name, f, length, req, res);
} else {
// TODO: What now? 404? 403? setStatus is not implemented in the HttpResponse object
// of this internal http server
}
return;
} else { // assert !f.isDirectory()
// TODO: Here comes a BUG: handleScriptContent(...) does nothing but return true,
// hence showTextFile(...) is never called; will comment it out for now
// if (handleScriptContent(name, params, f, length, req, res)) return;
showTextFile(name, params, f, length, req, res, (!name.endsWith(".html")) || "show".equals(params));
return;
}
}
if (cache != null && name.endsWith("Java.inc")) {
res.setContentLength(cache.length);
res.getOutputStream().write(cache);
return;
}
if (Util.JAVA_INC != null && name.endsWith("Java.inc")) {
try {
Field f = Util.JAVA_INC.getField("bytes");
cache = buf = (byte[]) f.get(Util.JAVA_INC);
res.setContentLength(buf.length);
out = res.getOutputStream();
out.write(buf);
return;
} catch (SecurityException e) {/*ignore*/
} catch (IOException | IllegalAccessException | IllegalArgumentException | NoSuchFieldException e) {
Util.printStackTrace(e);
}
}
name = name.replaceFirst("/JavaBridge", "META-INF");
InputStream in = JavaBridgeRunner.class.getClassLoader().getResourceAsStream(name);
if (in == null) { // Java.inc may not exist in the source download, use JavaBridge.inc instead.
name = name.replaceFirst("Java\\.inc", "JavaBridge.inc");
in = JavaBridgeRunner.class.getClassLoader().getResourceAsStream(name);
if (in == null) {
res.setContentLength(ERROR.length);
res.getOutputStream().write(ERROR);
return;
} else {
if (Util.logLevel > 4)
Util.logDebug("Java.inc not found, using JavaBridge.inc instead");
}
}
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
buf = new byte[Util.BUF_SIZE];
while ((c = in.read(buf)) > 0) bout.write(buf, 0, c);
res.addHeader("Last-Modified", "Wed, 17 Jan 2007 19:52:43 GMT"); // bogus
res.setContentLength(bout.size());
out = res.getOutputStream();
out.write(bout.toByteArray());
} catch (IOException e) {
/* may happen when the client is not interested, see require_once() */
} finally {
try {
in.close();
} catch (IOException e) {/*ignore*/}
}
}
/**
* Wait for the runner to finish
*
* @throws InterruptedException
*/
public void waitFor() throws InterruptedException {
runner.httpServer.join();
}
/**
* Destroy the JavaBridgeRunner instance
*/
public static void destroyRunner() {
if (runner != null) runner.destroy();
}
}