Initial version based on previous work. (Version alpha 7)

This commit is contained in:
Jindra Pet��k
2010-09-04 13:57:22 +02:00
commit a34ee7364c
1147 changed files with 84884 additions and 0 deletions
@@ -0,0 +1,134 @@
package com.jpexs.proxy;
import java.io.IOException;
import java.io.OutputStream;
/**
* Class used to represent an array of bytes as an Object.
*
* @author Mark Boyns
*/
public class ByteArray {
public byte bytes[];
public int offset = 0;
/**
* Create a ByteArray with the default size.
*/
public ByteArray() {
this(512);
}
/**
* Create a ByteArray with a specific default size.
*/
public ByteArray(int size) {
bytes = new byte[size];
}
/**
* Create a ByteArray from a String.
*/
public ByteArray(String s) {
this(s.length());
append(s);
}
/**
* Create a ByteArray from an array of bytes.
*/
public ByteArray(byte b[]) {
this(b.length);
append(b);
}
/**
* Append a byte.
*/
public void append(byte ch) {
if (offset == bytes.length) {
byte tmpbytes[] = bytes;
bytes = new byte[tmpbytes.length * 2];
System.arraycopy(tmpbytes, 0, bytes, 0, offset);
}
bytes[offset++] = ch;
}
/**
* Append a ByteArray.
*/
public void append(ByteArray b) {
if (bytes.length - offset < b.length()) {
byte tmpbytes[] = bytes;
bytes = new byte[tmpbytes.length + b.length()];
System.arraycopy(tmpbytes, 0, bytes, 0, offset);
}
System.arraycopy(b.bytes, 0, bytes, offset, b.length());
offset += b.length();
}
/**
* Append an array of bytes.
*/
public void append(byte b[]) {
if (bytes.length - offset < b.length) {
byte tmpbytes[] = bytes;
bytes = new byte[tmpbytes.length + b.length];
System.arraycopy(tmpbytes, 0, bytes, 0, offset);
}
System.arraycopy(b, 0, bytes, offset, b.length);
offset += b.length;
}
/**
* Append a String.
*/
public void append(String s) {
append(s.getBytes());
}
/**
* Convert to String.
*/
public String toString() {
return new String(bytes, 0, offset);
}
/**
* Return the bytes.
*/
public byte[] getBytes() {
return bytes;
}
public void writeTo(OutputStream out)
throws IOException {
out.write(bytes, 0, offset);
}
public byte get(int i) {
return bytes[i];
}
/**
* Return the number of bytes.
*/
public int length() {
return offset;
}
public void erase() {
offset = 0;
}
public void chop() {
chop(1);
}
public void chop(int i) {
offset -= i;
if (offset < 0) {
offset = 0;
}
}
}
@@ -0,0 +1,19 @@
package com.jpexs.proxy;
import java.io.InputStream;
/**
* Interface to catch contentTypes
*
* @author JPEXS
*/
public interface CatchedListener {
/**
* Method called when specified contentType is received
*
* @param contentType Content type
* @param url URL of the method
* @param data Data stream
*/
public void catched(String contentType, String url, InputStream data);
}
@@ -0,0 +1,43 @@
package com.jpexs.proxy;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.Socket;
/**
* Client which reads a Request and writes a Reply.
*
* @author Mark Boyns
*/
public class Client extends Connection {
/**
* Create a Client from a Socket.
*/
Client(Socket s) throws IOException {
super(s);
in = new BufferedInputStream(in);
//out = new DebugOutputStream(new BufferedOutputStream(out));
out = new BufferedOutputStream(out);
}
/**
* Read a Request.
*
* @returns a Request.
*/
Request read() throws IOException {
Request request = new Request(this);
request.read(getInputStream());
return request;
}
/**
* Write a Reply
*/
void write(Reply reply) throws IOException {
reply.write(getOutputStream());
}
}
@@ -0,0 +1,102 @@
package com.jpexs.proxy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
/**
* Create a TCP connection from a Socket or hostname/port
* with buffered IO.
*
* @author Mark Boyns
* @see java.net.Socket
*/
class Connection {
Socket socket = null;
InputStream in = null;
OutputStream out = null;
/**
* Create a Connection from a Socket.
*
* @param socket a socket
*/
Connection(Socket socket) throws IOException {
this.socket = socket;
in = socket.getInputStream();
out = socket.getOutputStream();
}
/**
* Create a Connection from a hostname and port.
*
* @param host remote hostname
* @param port remote port
*/
Connection(String host, int port) throws IOException {
this(new Socket(host, port));
}
Connection() {
}
/**
* Return the input stream.
*/
InputStream getInputStream() {
return in;
}
/**
* Return the output stream.
*/
OutputStream getOutputStream() {
return out;
}
void setInputStream(InputStream in) {
this.in = in;
}
void setOutputStream(OutputStream out) {
this.out = out;
}
/**
* Close the connection.
*/
void close() {
if (socket != null) {
try {
socket.close();
}
catch (IOException e) {
System.out.println("Connection: " + e);
}
}
}
public Socket getSocket() {
return socket;
}
public InetAddress getInetAddress() {
return socket.getInetAddress();
}
public int getPort() {
return socket.getPort();
}
public String toString() {
return getInetAddress().getHostAddress() + ":" + getPort();
}
public void setTimeout(int timeout)
throws SocketException {
socket.setSoTimeout(timeout);
}
}
@@ -0,0 +1,35 @@
package com.jpexs.proxy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
class Copy implements Runnable {
InputStream in = null;
OutputStream out = null;
Copy(InputStream in, OutputStream out) {
this.in = in;
this.out = out;
}
public void run() {
int n;
byte buffer[] = new byte[8192];
try {
while ((n = in.read(buffer, 0, buffer.length)) > 0) {
out.write(buffer, 0, n);
out.flush();
}
out.flush();
}
catch (IOException e) {
String s = e.toString();
// ignore socket closed exceptions
if (s.toLowerCase().indexOf("socket closed") == -1) {
e.printStackTrace();
}
}
}
}
@@ -0,0 +1,575 @@
package com.jpexs.proxy;
import java.io.*;
import java.net.Socket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.zip.GZIPInputStream;
/**
* HTTP transaction handler. A handler is created by Server for
* each HTTP transaction. Given a socket, the handler will deal with
* the request, reply, and invoke request, reply, and content filters.
*
* @author Mark Boyns
*/
class Handler implements Runnable {
static final boolean DEBUG = false;
Socket socket = null;
Request request = null;
Reply reply = null;
HttpRelay http = null;
int currentLength = -1;
int contentLength = -1;
long idle = 0;
double bytesPerSecond = 0;
Client client;
int MAXTIMEOUT = 1000 * 60 * 5;
List<Replacement> replacements;
CatchedListener catchedListener;
List<String> catchedContentTypes;
ReplacedListener replacedListener;
/**
* Create a Handler.
*/
Handler(Socket socket, List<Replacement> replacements, List<String> catchedContentTypes, CatchedListener catchedListener, ReplacedListener replacedListener) {
this.socket = socket;
this.replacements = replacements;
this.catchedListener = catchedListener;
this.catchedContentTypes = catchedContentTypes;
this.replacedListener = replacedListener;
}
/**
* Close all connections associated with this handler.
*/
synchronized void close() {
if (client != null) {
client.close();
client = null;
}
if (http != null) {
http.close();
http = null;
}
}
/**
* Flush all data to the client.
*/
void flush() {
if (client != null) {
try {
client.getOutputStream().flush();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
public void run() {
boolean keepAlive = false;
Exception reason = null;
Thread.currentThread().setName("Handler("
+ socket.getInetAddress().getHostAddress()
+ ")");
try {
client = new Client(socket);
} catch (IOException e) {
}
do {
request = null;
reply = null;
idle = System.currentTimeMillis();
try {
request = client.read();
if (Main.DEBUG_MODE) {
System.out.println("REQUEST: " + request.getURL());
}
}
catch (IOException e) {
e.printStackTrace();
break;
}
idle = 0;
try {
keepAlive = processRequest();
}
catch (IOException ioe) {
reason = ioe;
keepAlive = false;
}
if (request != null && reply != null) {
// XXX insert the number of bytes read into the
// reply content-length for logging.
if (reply != null && currentLength > 0) {
reply.setHeaderField("Content-length", currentLength);
}
}
}
while (keepAlive);
if (reason != null && reason.getMessage().indexOf("Broken pipe") == -1) {
}
close();
}
boolean processRequest() throws IOException {
boolean keepAlive = false;
while (reply == null) {
boolean secure = false;
boolean uncompress = false;
if (request.getCommand().equals("CONNECT")) {
secure = true;
} else if (request.getURL().startsWith("https://")) {
System.out.println("Netscape keep-alive bug: " + request.getURL());
return false;
} else if (!request.getURL().startsWith("http://")) {
System.out.println("Unknown URL: " + request.getURL());
return false;
}
keepAlive = (request.containsHeaderField("Proxy-Connection")
&& request.getHeaderField("Proxy-Connection").equals("Keep-Alive"));
/* First look for any HttpFilters */
/* None found. Use http or https relay. */
if (secure) {
http = createHttpsRelay();
} else {
http = createHttpRelay();
}
try {
http.sendRequest(request);
if (http instanceof Http) {
((Http) http).setTimeout(MAXTIMEOUT);
}
reply = http.recvReply(request);
}
catch (RetryRequestException e) {
http.close();
http = null;
continue; /* XXX */
}
/* Guess content-type if there aren't any headers.
Probably an upgraded HTTP/0.9 reply. */
if (reply.headerCount() == 0) {
String url = request.getURL();
if (url.endsWith("/")
|| url.endsWith(".html") || url.endsWith(".htm")) {
reply.setHeaderField("Content-type", "text/html");
}
}
String encoding = reply.getHeaderField("Content-Encoding");
if (encoding != null && encoding.indexOf("gzip") != -1) {
reply.removeHeaderField("Content-Encoding");
reply.removeHeaderField("Content-length");
uncompress = true;
}
/* update reply */
reply.removeHeaderField("Proxy-Connection");
if (keepAlive && reply.containsHeaderField("Content-length")) {
reply.setHeaderField("Proxy-Connection", "Keep-Alive");
} else {
keepAlive = false;
}
currentLength = -1;
contentLength = -1;
try {
contentLength = Integer.parseInt(reply.getHeaderField("Content-length"));
}
catch (NumberFormatException e) {
}
/* update content-length reply */
if (secure) {
Https https = (Https) http;
int timeout = MAXTIMEOUT;
client.write(reply);
try {
https.setTimeout(timeout);
Copy cp = new Copy(client.getInputStream(), https.getOutputStream());
WorkerThread.assignThread(cp, "");
flushCopy(https.getInputStream(), client.getOutputStream(), -1, true);
client.close();
}
catch (InterruptedIOException iioe) {
// ignore socket timeout exceptions
}
} else if (reply.hasContent()) {
try {
processContent(uncompress);
}
catch (IOException e) {
if (http instanceof Http) {
((Http) http).reallyClose();
} else {
http.close();
}
http = null;
client.close();
client = null;
throw e;
//return false; /* XXX */
}
/* Document contains no data. */
if (contentLength == 0) {
client.close();
}
} else {
client.write(reply);
}
http.close();
}
return keepAlive;
}
HttpRelay createHttpsRelay() throws IOException {
HttpRelay http;
http = new Https(request.getHost(), request.getPort());
return http;
}
HttpRelay createHttpRelay() throws IOException {
HttpRelay http;
http = Http.open(request.getHost(), request.getPort());
return http;
}
InputStream readChunkedTransfer(InputStream in) throws IOException {
ByteArrayOutputStream chunks = new ByteArrayOutputStream(8192);
int size = 0;
contentLength = 0;
while ((size = reply.getChunkSize(in)) > 0) {
contentLength += size;
copy(in, chunks, size, true);
reply.readLine(in);
}
reply.getChunkedFooter(in);
reply.removeHeaderField("Transfer-Encoding");
reply.setHeaderField("Content-length", contentLength);
return new ByteArrayInputStream(chunks.toByteArray());
}
private static DateFormat httpDateFormat() {
DateFormat httpDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
httpDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
return httpDateFormat;
}
void disableReplyCaching() {
reply.removeHeaderField("Cache-Control");
reply.removeHeaderField("Last-Modified");
reply.removeHeaderField("Expires");
reply.removeHeaderField("Date");
reply.removeHeaderField("ETag");
reply.removeHeaderField("Pragma");
reply.setHeaderField("Pragma", "no-cache");
reply.setHeaderField("Cache-Control", "no-cache, must-revalidate");
Calendar now = Calendar.getInstance();
reply.setHeaderField("Expires", httpDateFormat().format(now.getTime()));//"Sat, 26 Jul 1997 05:00:00 GMT");
reply.setHeaderField("Last-Modified", "Sat, 26 Jul 1997 05:00:00 GMT");
}
void processContent(boolean uncompress) throws IOException {
InputStream in;
boolean chunked = false;
if (reply.containsHeaderField("Transfer-Encoding")
&& reply.getTransferEncoding().equals("chunked")) {
in = readChunkedTransfer(reply.getContent());
chunked = true;
} else {
in = reply.getContent();
}
if (in == null) {
System.out.println("No inputstream");
return;
} else if (uncompress) {
in = new GZIPInputStream(in);
}
String url = request.getURL();
boolean replaced = false;
for (Replacement r : replacements) {
if (r.matches(url)) {
r.lastAccess = Calendar.getInstance();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
FileInputStream fis = new FileInputStream(r.targetFile);
byte buf[] = new byte[2048];
int pos = 0;
while ((pos = fis.read(buf)) > 0) {
buffer.write(buf, 0, pos);
}
fis.close();
buffer.close();
} catch (IOException ex) {
if (Main.DEBUG_MODE) {
System.err.println("ERROR:Cannot read file: " + r.targetFile);
}
}
byte bytes[] = buffer.toByteArray();
contentLength = bytes.length;
reply.setHeaderField("Content-length", contentLength);
disableReplyCaching();
client.write(reply);
copy(new ByteArrayInputStream(bytes),
client.getOutputStream(), contentLength, false);
replaced = true;
if (replacedListener != null) {
replacedListener.replaced(r, request.getURL(), reply.getContentType());
}
break;
}
}
if (!replaced) {
String contentType = reply.getHeaderField("Content-type");
if (reply.getStatusCode() == 200) {
if (contentType != null) {
for (String ct : catchedContentTypes) {
String convContentType = contentType;
if (convContentType.contains(";"))
convContentType = convContentType.substring(0, convContentType.indexOf(";"));
if (ct.toLowerCase().equals(convContentType.toLowerCase())) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
copy(in, baos, contentLength, true);
if (catchedListener != null) {
catchedListener.catched(ct, request.getURL(), new ByteArrayInputStream(baos.toByteArray()));
}
in = new ByteArrayInputStream(baos.toByteArray());
disableReplyCaching();
break;
}
}
}
}
client.write(reply);
copy(in, client.getOutputStream(), contentLength, true);
}
}
/**
* Return the content length.
*/
int getTotalBytes() {
return contentLength > 0 ? contentLength : 0;
}
/**
* Return the number of bytes read so far.
*/
int getCurrentBytes() {
return currentLength > 0 ? currentLength : 0;
}
/**
* Copy in to out.
*
* @param in InputStream
* @param out OutputStream
* @param monitored Update the Monitor
*/
void copy(InputStream in, OutputStream out, int length, boolean monitored)
throws IOException {
if (length == 0) {
return;
}
int n;
byte buffer[] = new byte[8192];
long start = System.currentTimeMillis();
long now = 0, then = start;
bytesPerSecond = 0;
if (monitored) {
currentLength = 0;
}
for (; ;) {
n = (length > 0) ? Math.min(length, buffer.length) : buffer.length;
n = in.read(buffer, 0, n);
if (n < 0) {
break;
}
out.write(buffer, 0, n);
if (monitored) {
currentLength += n;
}
now = System.currentTimeMillis();
bytesPerSecond = currentLength / ((now - start) / 1000.0);
// flush after 1 second
if (now - then > 1000) {
out.flush();
}
if (length != -1) {
length -= n;
if (length == 0) {
break;
}
}
then = now;
}
out.flush();
if (DEBUG) {
System.out.println(currentLength + " bytes processed in "
+ ((System.currentTimeMillis() - start)
/ 1000.0) + " seconds "
+ ((int) bytesPerSecond / 1024) + " kB/s");
}
}
/**
* Copy in to out.
*
* @param in InputStream
* @param out OutputStream
* @param monitored Update the Monitor
*/
void flushCopy(InputStream in, OutputStream out, int length, boolean monitored)
throws IOException {
if (length == 0) {
return;
}
int n;
byte buffer[] = new byte[8192];
long start = System.currentTimeMillis();
bytesPerSecond = 0;
if (monitored) {
currentLength = 0;
}
for (; ;) {
n = (length > 0) ? Math.min(length, buffer.length) : buffer.length;
n = in.read(buffer, 0, n);
if (n < 0) {
break;
}
out.write(buffer, 0, n);
out.flush();
if (monitored) {
currentLength += n;
}
bytesPerSecond = currentLength / ((System.currentTimeMillis() - start) / 1000.0);
if (length != -1) {
length -= n;
if (length == 0) {
break;
}
}
}
out.flush();
if (DEBUG) {
System.out.println(currentLength + " bytes processed in "
+ ((System.currentTimeMillis() - start) / 1000.0) + " seconds "
+ ((int) bytesPerSecond / 1024) + " kB/s");
}
}
/**
* Return a string represenation of the hander's state.
*/
public String toString() {
StringBuffer str = new StringBuffer();
str.append("CLIENT ");
str.append(socket.getInetAddress().getHostAddress());
str.append(":");
str.append(socket.getPort());
str.append(" - ");
if (request == null) {
str.append("idle " + ((System.currentTimeMillis() - idle) / 1000.0) + " sec");
} else {
if (reply != null && currentLength > 0) {
str.append("(");
str.append(currentLength);
if (contentLength > 0) {
str.append("/");
str.append(contentLength);
}
str.append(" ");
str.append(((int) bytesPerSecond / 1024) + " kB/s");
str.append(") ");
}
str.append(request.getURL());
}
return str.toString();
}
}
@@ -0,0 +1,296 @@
package com.jpexs.proxy;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
/**
* @author Mark Boyns
*/
class Http extends HttpConnection {
static final boolean DEBUG = false; /* enable lots of debug output */
/* XXX - more than 1 should work now. */
static final int MAX_PENDING_REQUESTS = 1;
static Hashtable cache = new Hashtable(33);
private static Object httpLock = new Object();
String host;
int port;
boolean proxy = false;
boolean persistent = false;
boolean closed = false;
long idle = 0;
Vector queue = new Vector();
Http(String host, int port) throws IOException {
this(host, port, false);
}
Http(String host, int port, boolean isProxy) throws IOException {
super(host, port);
this.host = host;
this.port = port;
this.proxy = isProxy;
}
public synchronized void sendRequest(Request request)
throws IOException, RetryRequestException {
queue.addElement(request);
try {
send(request);
}
catch (IOException e) {
if (persistent) {
persistent = false;
if (DEBUG) System.out.println("RETRY SEND " + request.getURL());
throw new RetryRequestException();
}
throw e;
}
}
public synchronized Reply recvReply(Request request)
throws IOException, RetryRequestException {
while (queue.firstElement() != request) {
try {
wait();
}
catch (InterruptedException e) {
}
}
if (closed) {
if (DEBUG) System.out.println("RETRY CLOSED " + request.getURL());
throw new RetryRequestException();
}
try {
return recv();
}
catch (IOException e) {
if (persistent) {
persistent = false;
if (DEBUG) System.out.println("RETRY RECV " + request.getURL());
throw new RetryRequestException();
}
throw e;
}
}
public void reallyClose() {
persistent = false;
if (DEBUG)
System.out.println("REALLY CLOSE " + this);
close();
}
public synchronized void close() {
if (persistent) {
idle = System.currentTimeMillis();
} else {
cacheRemove(host, port, this);
super.close();
closed = true;
}
if (queue.size() > 0) {
queue.removeElementAt(0);
if (DEBUG) {
if (persistent)
System.out.println("DONE " + this);
else
System.out.println("CLOSE " + this);
}
notify();
}
}
private void send(Request request) throws IOException {
if (DEBUG) System.out.println("SEND " + request.getURL());
/* Prepare HTTP/1.1 request */
request.removeHeaderField("Proxy-Connection");
request.setHeaderField("Connection", "open");
if (!request.containsHeaderField("Host")) {
request.setHeaderField("Host", request.getHost());
}
if (proxy) {
request.write(getOutputStream());
} else {
String oldStatusLine = request.statusLine;
StringBuffer head = new StringBuffer();
head.append(request.getCommand());
head.append(" ");
head.append(request.getPath());
head.append(" ");
head.append("HTTP/1.0");
request.statusLine = head.toString();
request.write(getOutputStream());
/* flush? */
request.statusLine = oldStatusLine;
}
}
private Reply recv() throws IOException {
Reply reply = new Reply(getInputStream());
reply.read();
String conn = reply.getHeaderField("Connection");
if (DEBUG) System.out.println("RECV " + reply.statusLine);
if (reply.containsHeaderField("Connection")
&& reply.getHeaderField("Connection").equals("close")) {
persistent = false;
} else if (reply.getProtocol().equals("HTTP/1.1")) {
persistent = true;
} else {
persistent = false;
}
/* Received HTTP/1.1 "Continue". Read another Reply. */
if (reply.getStatusCode() == 100) {
reply = recv();
}
return reply;
}
private boolean isBusy() {
return queue.size() >= MAX_PENDING_REQUESTS;
}
private boolean isPersistent() {
return persistent;
}
private static String cacheKey(String host, int port) {
return host.toLowerCase() + ":" + port;
}
private static Vector cacheLookup(String host, int port) {
Vector v = (Vector) cache.get(cacheKey(host, port));
return v;
}
private static boolean cacheContains(Http http) {
Vector v = (Vector) cache.get(cacheKey(http.host, http.port));
return v != null ? v.contains(http) : false;
}
private static void cacheInsert(String host, int port, Http http) {
String key = cacheKey(host, port);
Vector v = (Vector) cache.get(key);
if (v == null) {
v = new Vector();
}
v.addElement(http);
cache.put(key, v);
}
private static void cacheRemove(String host, int port, Http http) {
Vector v = (Vector) cache.get(cacheKey(host, port));
if (v != null) {
v.removeElement(http);
if (v.isEmpty()) {
cache.remove(cacheKey(host, port));
}
}
}
private static void cacheClean() {
long now = System.currentTimeMillis();
Enumeration e = cache.keys();
while (e.hasMoreElements()) {
Vector v = (Vector) cache.get(e.nextElement());
for (int i = 0; i < v.size(); i++) {
Http http = (Http) v.elementAt(i);
if (http.idle > 0 && now - http.idle > 30000) /* 30 seconds */ {
if (DEBUG) System.out.println("IDLE " + http);
http.persistent = false;
http.close();
}
}
}
}
static Http open(String host, int port, boolean isProxy)
throws IOException {
Http http = null;
synchronized (httpLock) {
Vector v = cacheLookup(host, port);
if (v != null) {
for (int i = 0; i < v.size(); i++) {
Http pick = (Http) v.elementAt(i);
/* find an http connection that isn't busy */
if (pick.isPersistent() && !pick.isBusy()) {
http = pick;
break;
}
}
if (http != null) {
http.idle = 0;
if (DEBUG) System.out.println("REUSE " + http);
}
}
}
if (http == null) {
if (DEBUG) System.out.println("OPENING " + host + ":" + port);
http = new Http(host, port, isProxy);
if (DEBUG) System.out.println("OPENED " + http);
cacheInsert(host, port, http);
}
return http;
}
static Http open(String host, int port) throws IOException {
return open(host, port, false);
}
static Enumeration enumerate() {
Vector list = new Vector();
Enumeration e = cache.keys();
while (e.hasMoreElements()) {
Vector v = (Vector) cache.get(e.nextElement());
for (int i = 0; i < v.size(); i++) {
list.addElement(v.elementAt(i));
}
}
return list.elements();
}
static synchronized void clean() {
cacheClean();
}
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("SERVER ");
buf.append(super.toString());
if (isPersistent()) {
buf.append(" - ");
if (queue.size() > 0) {
buf.append(queue.size());
buf.append(" pending");
} else {
buf.append("idle " + ((System.currentTimeMillis() - idle) / 1000.0) + " sec");
}
}
return buf.toString();
}
}
@@ -0,0 +1,48 @@
package com.jpexs.proxy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
abstract class HttpConnection extends Connection implements HttpRelay {
HttpConnection(String host, int port) throws IOException {
super(host, port);
}
HttpConnection(Socket s) throws IOException {
super(s);
}
public void sendRequest(Request request)
throws IOException, RetryRequestException {
request.write(getOutputStream());
}
public Reply recvReply(Request request)
throws IOException, RetryRequestException {
Reply reply = new Reply(getInputStream());
reply.read();
return reply;
}
public void setInputStream(InputStream in) {
super.setInputStream(in);
}
public void setOutputStream(OutputStream out) {
super.setOutputStream(out);
}
public InputStream getInputStream() {
return super.getInputStream();
}
public OutputStream getOutputStream() {
return super.getOutputStream();
}
public void close() {
super.close();
}
}
@@ -0,0 +1,12 @@
package com.jpexs.proxy;
import java.io.IOException;
public interface HttpRelay {
void sendRequest(Request request) throws IOException, RetryRequestException;
Reply recvReply(Request request) throws IOException, RetryRequestException;
void close();
}
@@ -0,0 +1,40 @@
package com.jpexs.proxy;
import java.io.IOException;
/**
* @author Mark Boyns
*/
class Https extends HttpConnection {
boolean proxy = false;
Https(String host, int port) throws IOException {
super(host, port);
}
Https(String host, int port, boolean isProxy) throws IOException {
this(host, port);
proxy = isProxy;
}
public void sendRequest(Request request)
throws IOException, RetryRequestException {
if (proxy) {
super.sendRequest(request);
} else {
/* nothing */
}
}
public Reply recvReply(Request request)
throws IOException, RetryRequestException {
Reply reply = new Reply(getInputStream());
if (proxy) {
reply.read();
} else {
reply.statusLine = "HTTP/1.0 200 Connection established";
reply.setHeaderField("Proxy-agent", "ASDec");
}
return reply;
}
}
@@ -0,0 +1,41 @@
package com.jpexs.proxy;
/**
* A wrapper around java.lang.String to have case-insensitive
* hashCode and equals methods.
*
* @author Mark Boyns
* @see String
*/
class Key {
private String name = null;
/**
* Create a Key.
*/
Key(String name) {
this.name = name;
}
/**
* Return a lowercase hashCode.
*/
public int hashCode() {
String s = name.toLowerCase();
return s.hashCode();
}
/**
* Return a lowercase equals.
*/
public boolean equals(Object obj) {
return name.equalsIgnoreCase(obj.toString());
}
/**
* Return the key.
*/
public String toString() {
return name;
}
}
@@ -0,0 +1,90 @@
package com.jpexs.proxy;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
public static int port = 55555;
public static final String REPLACEMENTSFILE = "." + File.separator + "config" + File.separator + "replacements.ini";
public static boolean DEBUG_MODE = false;
public static void enterToContinue() {
Scanner keyIn = new Scanner(System.in);
System.out.print("Press the enter key to continue");
keyIn.nextLine();
}
public static void main(String args[]) {
for (int i = 0; i < args.length; i++) {
if (args[i].toLowerCase().equals("--help")) {
System.out.println("JPEXS Replacement Proxy");
System.out.println("-----------------------");
System.out.println("Replacements list is read from \"" + REPLACEMENTSFILE + "\" file.");
System.out.println("Optional commandline parameters:");
System.out.println(" -d");
System.out.println(" Print debug info to console ");
System.out.println(" -p<NUMBER>");
System.out.println(" Set proxy port to <NUMBER>. Default is 55555.");
System.exit(0);
}
if (args[i].toLowerCase().equals("-d")) {
System.out.println("DEBUG mode ON");
DEBUG_MODE = true;
}
if (args[i].toLowerCase().startsWith("-p")) {
try {
port = Integer.parseInt(args[i].substring(2));
System.out.println("PORT set to " + port);
} catch (NumberFormatException ex) {
System.out.println("Invalit port, reset to " + port);
}
}
}
List<Replacement> replacements = new ArrayList<Replacement>();
if ((new File(REPLACEMENTSFILE)).exists()) {
try {
BufferedReader br = new BufferedReader(new FileReader(REPLACEMENTSFILE));
String s = "";
while ((s = br.readLine()) != null) {
String fileName = br.readLine();
if (fileName == null) break;
fileName = fileName.replaceAll("[\\\\/]", File.separator);
Replacement r = new Replacement(s, fileName);
if (DEBUG_MODE) {
System.out.println("Added Replacement: " + r.urlPattern + " => " + r.targetFile);
}
replacements.add(r);
}
br.close();
} catch (IOException e) {
}
} else {
if (DEBUG_MODE) {
System.out.println("WARNING:REPLACEMENTS FILE NOT FOUND.");
}
}
Server.startServer(port, replacements, new ArrayList<String>(), new CatchedListener() {
/**
* Method called when specified contentType is received
*
* @param contentType Content type
* @param url URL of the method
* @param data Data stream
*/
public void catched(String contentType, String url, InputStream data) {
}
}, new ReplacedListener() {
public void replaced(Replacement replacement, String url, String contentType) {
if (DEBUG_MODE) {
System.out.println("REPLACED:" + url + " (Content-type:" + contentType + ") WITH FILE " + replacement.targetFile);
}
}
});
}
}
@@ -0,0 +1,253 @@
package com.jpexs.proxy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
/**
* Abstract class to represent message headers.
*
* @author Mark Boyns
*/
public abstract class Message {
/**
* Hashtable used to store message headers.
*/
private Hashtable headers = new Hashtable(33);
/**
*
*/
String statusLine = null;
public String readLine(InputStream in) throws IOException {
char buf[] = new char[128];
int offset = 0;
int ch;
for (; ;) {
ch = in.read();
if (ch == -1 || ch == '\n') {
break;
} else if (ch == '\r') {
int tmpch = in.read();
if (tmpch != '\n') {
if (!(in instanceof PushbackInputStream)) {
in = new PushbackInputStream(in);
}
((PushbackInputStream) in).unread(tmpch);
}
break;
} else {
if (offset == buf.length) {
char tmpbuf[] = buf;
buf = new char[tmpbuf.length * 2];
System.arraycopy(tmpbuf, 0, buf, 0, offset);
}
buf[offset++] = (char) ch;
}
}
return String.copyValueOf(buf, 0, offset);
}
/**
* Read headers and store them in the hashtable.
*/
void readHeaders(InputStream in) throws IOException {
int i;
Key key = null;
for (; ;) {
String s = readLine(in);
if (s == null) {
break;
}
i = s.indexOf(':');
if (i == -1) {
/* end of header */
if (s.length() == 0) {
break;
}
/* multi-line headers */
else if (key != null
&& (s.startsWith(" ") || s.startsWith("\t"))) {
int index = getHeaderValueCount(key.toString());
index--;
Vector v = (Vector) headers.get(key);
v.setElementAt(v.elementAt(index) + "\n" + s, index);
}
} else {
key = new Key(s.substring(0, i));
Vector v;
if (headers.containsKey(key)) {
v = (Vector) headers.get(key);
} else {
v = new Vector();
}
v.addElement(s.substring(i + 1).trim());
headers.put(key, v);
}
}
}
public int headerCount() {
return headers.size();
}
/**
* Set the Status line.
*/
public void setStatusLine(String l) {
statusLine = l;
}
public int getHeaderValueCount(String name) {
Vector v = (Vector) headers.get(new Key(name));
return v.size();
}
public String getHeaderField(String name) {
return getHeaderField(name, 0);
}
public String getHeaderField(String name, int index) {
Vector v = (Vector) headers.get(new Key(name));
if (v == null) {
return null;
}
return (String) v.elementAt(index);
}
public void setHeaderField(String name, String value) {
setHeaderField(name, value, 0);
}
public void setHeaderField(String name, String value, int index) {
Vector v;
Key key = new Key(name);
if (headers.containsKey(key)) {
v = (Vector) headers.get(key);
} else {
v = new Vector();
if (index == 0) {
v.addElement("");
}
headers.put(key, v);
}
v.setElementAt(value, index);
}
public void setHeaderField(String name, int value) {
setHeaderField(name, value, 0);
}
public void setHeaderField(String name, int value, int index) {
setHeaderField(name, new Integer(value).toString(), index);
}
/**
* Set all header fields with the give name to the
* specified value.
*/
public void setHeaderFields(String name, String value) {
Vector v;
Key key = new Key(name);
v = (Vector) headers.get(key);
if (v != null) {
for (int i = 0; i < v.size(); i++) {
v.setElementAt(value, i);
}
}
}
public void appendHeaderField(String name, String value) {
appendHeaderField(name, value, 0);
}
public void appendHeaderField(String name, String value, int index) {
setHeaderField(name, getHeaderField(name, index) + value, index);
}
public void removeHeaderField(String name) {
headers.remove(new Key(name));
}
/**
* Return whether or not a header exists.
*
* @param name header name
*/
public boolean containsHeaderField(String name) {
return headers.containsKey(new Key(name));
}
/**
* @return an Enumeration of Strings
*/
public Enumeration getHeaders() {
Vector v = new Vector();
for (Enumeration e = headers.keys(); e.hasMoreElements();) {
v.addElement(e.nextElement().toString());
}
return v.elements();
}
private final static byte[] COLON_SPACE = ": ".getBytes();
private final static byte[] CRLF = "\r\n".getBytes();
private ByteArray toByteArray(byte[] sep) {
ByteArray buf = new ByteArray();
Key key;
String value;
Vector v;
int i = 0;
buf.append(statusLine);
buf.append(sep);
for (Enumeration e = headers.keys(); e.hasMoreElements();) {
key = (Key) e.nextElement();
v = (Vector) headers.get(key);
for (i = 0; i < v.size(); i++) {
buf.append(key.toString());
buf.append(COLON_SPACE);
buf.append(v.elementAt(i).toString());
buf.append(sep);
}
}
buf.append(sep);
return buf;
}
private ByteArray toByteArray() {
return toByteArray(CRLF);
}
private ByteArray toByteArray(String sep) {
return toByteArray(sep.getBytes());
}
public String toString() {
return toByteArray().toString();
}
public String toString(String sep) {
return toByteArray(sep).toString();
}
public void write(OutputStream out)
throws IOException {
toByteArray().writeTo(out);
out.flush();
}
}
@@ -0,0 +1,5 @@
package com.jpexs.proxy;
public interface ReplacedListener {
public void replaced(Replacement replacement, String url, String contentType);
}
@@ -0,0 +1,63 @@
package com.jpexs.proxy;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.regex.Pattern;
/**
* Replacement of URL with local file
*/
public class Replacement {
/**
* URL pattern, can contain * wild-cards
*/
public String urlPattern;
/**
* Filename to replace content with
*/
public String targetFile;
/**
* Date of last accesing this url
*/
public Calendar lastAccess;
/**
* Constructor
*
* @param urlPattern URL pattern, can contain * wild-cards
* @param targetFile Filename to replace content with
*/
public Replacement(String urlPattern, String targetFile) {
this.urlPattern = urlPattern;
this.targetFile = targetFile;
}
/**
* Returns true when urlPattern matches specified url
*
* @param url Url to test match
* @return True when matches
*/
public boolean matches(String url) {
String pat = Pattern.quote(urlPattern);
pat = pat.replace("*", "\\E.*\\Q");
return Pattern.matches(pat, url);
}
/**
* Returns a string representation of the object.
*
* @return a string representation of the object.
*/
@Override
public String toString() {
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
if (lastAccess == null) {
return " " + urlPattern;
} else {
return format.format(lastAccess.getTime()) + " " + urlPattern;
}
}
}
@@ -0,0 +1,203 @@
package com.jpexs.proxy;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Hashtable;
import java.util.StringTokenizer;
/**
* @author Mark Boyns
*/
public class Reply extends Message {
InputStream in = null;
int statusCode = -1;
public Reply() {
}
public Reply(InputStream in) {
setContent(in);
}
public void setContent(InputStream in) {
this.in = in;
}
public InputStream getContent() {
return in;
}
void read() throws IOException {
if (in != null) {
read(in);
}
}
void read(InputStream in) throws IOException {
statusLine = readLine(in);
if (statusLine == null || statusLine.length() == 0) {
throw new IOException("Missing HTTP status line");
}
/* Look for HTTP/0.9 */
if (!statusLine.startsWith("HTTP")) {
/* Put back the line */
if (this.in != null) {
String putback = new String(statusLine + "\n");
this.in = new SequenceInputStream(new ByteArrayInputStream(putback.getBytes("utf8")), in);
}
/* Fake a status line and upgrade to HTTP/1.0 */
statusLine = "HTTP/1.0 200 OK";
return;
}
readHeaders(in);
int code = getStatusCode();
/* RFC 2068: 204 and 304 MUST NOT contain a message body. */
switch (code) {
case 204: /* No Content */
case 304: /* Not Modified */
/* Ignore the message body if it exists */
if (containsHeaderField("Content-length")) {
int contentLength = 0;
try {
contentLength = Integer.parseInt(getHeaderField("Content-length"));
}
catch (NumberFormatException e) {
}
int n;
byte buffer[] = new byte[8192];
while ((n = in.read(buffer, 0, buffer.length)) > 0) {
/* ignore */
}
removeHeaderField("Content-length");
}
break;
}
}
public boolean hasContent() {
switch (getStatusCode()) {
case 204:
case 304:
return false;
default:
return true;
}
}
public String getProtocol() {
StringTokenizer st = new StringTokenizer(statusLine);
String protocol = (String) st.nextToken();
return protocol;
}
public int getStatusCode() {
if (statusCode == -1) {
StringTokenizer st = new StringTokenizer(statusLine);
String protocol = (String) st.nextToken();
String status = (String) st.nextToken();
try {
statusCode = Integer.parseInt(status);
}
catch (NumberFormatException e) {
statusCode = 0;
}
}
return statusCode;
}
private Hashtable headerParser(String header) {
Hashtable table = new Hashtable();
String type = getHeaderField(header);
if (type == null) {
return table;
}
StringTokenizer st = new StringTokenizer(type, ";");
int count = 0;
while (st.hasMoreTokens()) {
String token = st.nextToken();
token = token.trim();
String name;
String value;
int i = token.indexOf('=');
if (i != -1) {
name = token.substring(0, i);
value = token.substring(i + 1);
} else {
name = token;
value = "";
}
if (count == 0) {
table.put(header, name);
} else {
table.put(name, value);
}
count++;
}
return table;
}
public String getContentType() {
Hashtable table = headerParser("Content-type");
return (String) table.get("Content-type");
}
public String getBoundary() {
Hashtable table = headerParser("Content-type");
return (String) table.get("boundary");
}
public String getTransferEncoding() {
Hashtable table = headerParser("Transfer-Encoding");
return (String) table.get("Transfer-Encoding");
}
public int getChunkSize(InputStream in) throws IOException {
String line = readLine(in);
line = line.trim(); /* apache can have trailing spaces */
int size = -1;
try {
size = Integer.valueOf(line, 16).intValue();
}
catch (NumberFormatException e) {
System.out.println(e);
}
return size;
}
public void getChunkedFooter(InputStream in) throws IOException {
for (; ;) {
String line = readLine(in);
if (line == null) {
break;
}
int i = line.indexOf(':');
if (i == -1) {
break;
}
}
}
public void setStatusLine(String line) {
this.statusLine = line;
}
public static Reply createRedirect(String url) {
Reply r = new Reply();
r.setStatusLine("HTTP/1.0 302 Moved Temporarily");
r.setHeaderField("Location", url);
return r;
}
}
@@ -0,0 +1,235 @@
package com.jpexs.proxy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Mark Boyns
*/
public class Request extends Message {
private static Pattern httpRegex;
private String command = null;
private String url = null;
private String protocol = null;
private byte[] data = null;
private Hashtable log;
private Vector logHeaders;
private Client client;
static {
httpRegex = Pattern.compile("^(http|https):", Pattern.CASE_INSENSITIVE);
}
public Request(Client client) {
this.client = client;
}
void read(InputStream in) throws IOException {
statusLine = readLine(in);
if (statusLine == null || statusLine.length() == 0) {
throw new IOException("Empty request");
}
StringTokenizer st = new StringTokenizer(statusLine);
command = (String) st.nextToken();
url = (String) st.nextToken();
protocol = (String) st.nextToken();
if (!url.startsWith("http")) {
Matcher match = httpRegex.matcher(url);
if (match.matches()) {
url = url.substring(match.start(),
match.end()).toLowerCase()
+ url.substring(match.end());
}
}
readHeaders(in);
if ("POST".equals(command) || "PUT".equals(command)) {
try {
int n = Integer.parseInt(getHeaderField("Content-length"));
data = new byte[n];
int offset = 0;
while (offset < data.length) {
n = in.read(data, offset, data.length - offset);
if (n < 0) {
throw new IOException("Not enough " + command + " data");
}
offset += n;
}
}
catch (NumberFormatException e) {
System.out.println("Malformed or missing " + command + " Content-length");
}
}
}
public void write(OutputStream out)
throws IOException {
super.write(out);
if (data != null) {
out.write(data);
out.flush();
}
}
public String getRequest() {
return statusLine;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public String getURL() {
return url;
}
public void setURL(String url) {
this.url = url;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getHost() {
String url = getURL();
String s;
if (url.startsWith("http://")) {
s = url.substring(7, url.indexOf('/', 7));
} else {
s = url;
}
int at = s.indexOf('@');
if (at != -1) {
s = s.substring(at + 1);
}
if (s.indexOf(':') != -1) {
return s.substring(0, s.indexOf(':'));
}
return s;
}
public int getPort() {
int port = 80;
String url = getURL();
String s;
if (url.startsWith("http://")) {
s = url.substring(7, url.indexOf('/', 7));
} else {
s = url;
}
int at = s.indexOf('@');
if (at != -1) {
s = s.substring(at + 1);
}
if (s.indexOf(':') != -1) {
try {
port = Integer.parseInt(s.substring(s.indexOf(':') + 1));
}
catch (NumberFormatException e) {
System.out.println("Invalid port in " + url);
}
}
return port;
}
public String getData() {
if (data == null) {
return null;
}
return new String(data);
}
public byte[] getDataBytes() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public String getPath() {
String str = getURL();
int pos = 0;
for (int i = 0; i < 3; i++) {
pos = str.indexOf('/', pos);
pos++;
}
pos--;
return str.substring(pos);
}
public String getDocument() {
String path = getPath();
int n = path.lastIndexOf('/');
if (n == path.length() - 1) {
n = path.lastIndexOf('/', n - 1);
}
if (n < 0) {
return "/";
} else {
return path.substring(n + 1);
}
}
public String getQueryString() {
String path = getPath();
int n = path.indexOf('?');
if (n < 0) {
return null;
}
return path.substring(n + 1);
}
public synchronized void addLogEntry(String header,
String message) {
if (log == null) {
log = new Hashtable();
logHeaders = new Vector();
}
Vector v = (Vector) log.get(header);
if (log.get(header) == null) {
v = new Vector();
log.put(header, v);
logHeaders.addElement(header);
}
v.addElement(message);
}
public Enumeration getLogHeaders() {
return logHeaders != null ? logHeaders.elements() : null;
}
public Enumeration getLogEntries(String header) {
return log != null ? ((Vector) log.get(header)).elements() : null;
}
}
@@ -0,0 +1,11 @@
package com.jpexs.proxy;
class RetryRequestException extends Exception {
RetryRequestException() {
super();
}
RetryRequestException(String message) {
super(message);
}
}
@@ -0,0 +1,88 @@
package com.jpexs.proxy;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
/**
* Proxy server
*
* @author JPEXS
*/
public class Server extends Thread {
private ServerSocket ssocket = null;
private boolean stoppped = false;
private List<Replacement> replacements;
private static Server server;
private List<String> catchedContentTypes;
private CatchedListener catchedListener;
private int port;
private ReplacedListener replacedListener;
private Server(int port, List<Replacement> replacements, List<String> catchedContentTypes, CatchedListener catchedListener, ReplacedListener replacedListener) {
this.replacements = replacements;
this.catchedContentTypes = catchedContentTypes;
this.catchedListener = catchedListener;
this.replacedListener = replacedListener;
this.port = port;
}
private void stopRun() {
stoppped = true;
if (ssocket != null) {
try {
ssocket.close();
} catch (IOException e) {
}
}
}
/**
* Starts proxy server
*
* @param port Listening port
* @param replacements List of replacements
* @param catchedContentTypes Content types to sniff
* @param catchedListener Catched listener
*/
public static void startServer(int port, List<Replacement> replacements, List<String> catchedContentTypes, CatchedListener catchedListener, ReplacedListener replacedListener) {
stopServer();
server = new Server(port, replacements, catchedContentTypes, catchedListener, replacedListener);
//WorkerThread.assignThread(server, "Proxy server");
server.start();
}
/**
* Stops proxy server
*/
public static void stopServer() {
if (server != null) server.stopRun();
server = null;
}
/**
* Runs the server
*/
public void run() {
try {
ssocket = new ServerSocket(port);
} catch (IOException e) {
System.err.println("Cannot bind to port");
return;
}
for (; ;) {
try {
Socket sock = ssocket.accept();
Handler handler = new Handler(sock, replacements, catchedContentTypes, catchedListener, replacedListener);
WorkerThread.assignThread(handler, "Proxy handler");
} catch (IOException e) {
}
if (stoppped)
break;
}
}
}
@@ -0,0 +1,142 @@
// Copyright (c) 1999 Brian Wellington ([email protected])
// Portions Copyright (c) 1999 Network Associates, Inc.
package com.jpexs.proxy;
import java.util.Vector;
/**
* An extension of a Thread that uses threads from a pool, rather than
* allocating a new thread for each assigned task.
*
* @author Brian Wellington
*/
public class WorkerThread extends Thread {
private Runnable task;
private String name;
private static int nactive = 0;
private static Vector list = new Vector();
private static int max = 10;
private static long lifetime = 900 * 1000; /* 15 minute default */
private WorkerThread() {
setDaemon(true);
}
/**
* Sets the lifetime of an idle WorkerThread (in ms). A WorkerThread
* will remain on the idle list for this much time before exiting. This
* does not affect WorkerThreads currently idling.
*/
synchronized static void
setLifetime(long time) {
lifetime = time;
}
/**
* Sets the maximum number of WorkerThreads that can exist at any given
* time. If this value is decreased below the current number of
* WorkerThreads, this will not take effect immediately.
*/
synchronized static void
setMaxThreads(int maxThreads) {
max = maxThreads;
}
/**
* Obtains a WorkerThread to which a task can be assigned. If an idle
* WorkerThread is present, it is removed from the idle list and returned.
* If not, and the maximum number of WorkerThreads has not been reached,
* a new WorkerThread is created. If the maximum number has been reached,
* this blocks until a WorkerThread is free.
*/
static WorkerThread
getThread() {
WorkerThread t;
synchronized (list) {
if (list.size() > 0) {
t = (WorkerThread) list.firstElement();
list.removeElement(t);
} else if (nactive >= max) {
while (true) {
try {
list.wait();
}
catch (InterruptedException e) {
}
if (list.size() == 0)
continue;
t = (WorkerThread) list.firstElement();
list.removeElement(t);
break;
}
} else
t = new WorkerThread();
nactive++;
}
return t;
}
/**
* Assigns a task to a WorkerThread
*
* @param task The task to be run
* @param name The name of the task
*/
public static void
assignThread(Runnable task, String name) {
while (true) {
try {
WorkerThread t = getThread();
synchronized (t) {
t.task = task;
t.name = name;
if (!t.isAlive())
t.start();
else
t.notify();
}
return;
}
catch (IllegalThreadStateException e) {
}
}
}
/**
* Performs the task
*/
synchronized public void
run() {
while (true) {
setName(name);
try {
task.run();
}
catch (Throwable t) {
System.err.println(t);
}
setName("idle thread");
synchronized (list) {
list.addElement(this);
if (nactive >= max)
list.notify();
nactive--;
}
task = null;
try {
wait(lifetime);
}
catch (InterruptedException e) {
}
if (task == null) {
list.removeElement(this);
return;
}
}
}
}