blob: cd9830df4b5b0350101cd694b3384af2e406b421 [file] [log] [blame]
/*
* Copyright (c) 2014, 2015 Eike Stepper (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.oomph.internal.util;
import org.eclipse.oomph.util.HexUtil;
import org.eclipse.oomph.util.IOUtil;
import org.eclipse.oomph.util.OomphPlugin.BundleFile;
import org.eclipse.oomph.util.StringUtil;
import org.eclipse.core.runtime.Platform;
import org.osgi.framework.Bundle;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author Eike Stepper
*/
public final class HTTPServer
{
private static final boolean DEBUG = false;
private static final boolean DEBUG_REQUEST = false;
private static final boolean DEBUG_RESPONSE = false;
private static final Map<String, String> CONTENT_TYPES = new HashMap<String, String>();
private static final ImageContext IMAGE_CONTEXT = new ImageContext();
private static final String PATH_SEPARATOR = "/";
private static final String STATUS_OK = "200 OK";
private static final String STATUS_SEE_OTHER = "303 See Other";
private static final String STATUS_BAD_REQUEST = "400 Bad Request";
private static final String STATUS_FORBIDDEN = "403 Forbidden";
private static final String STATUS_NOT_FOUND = "404 Not Found";
private static final String STATUS_INTERNAL_SERVER_ERROR = "500 Internal Server Error";
private static final String STATUS_NOT_IMPLEMENTED = "501 Not Implemented";
private final List<Context> contexts = new ArrayList<Context>();
private final ExecutorService threadPool = Executors.newCachedThreadPool();
private Acceptor acceptor;
public HTTPServer() throws IOException
{
this(15000, 50000);
}
public HTTPServer(int minPort, int maxPort) throws IOException
{
addContext(IMAGE_CONTEXT);
for (int port = minPort; port <= maxPort; port++)
{
try
{
ServerSocket serverSocket = new ServerSocket(port, 500);
acceptor = new Acceptor(serverSocket);
return;
}
catch (BindException ex)
{
// Try next port.
}
catch (InterruptedException ex)
{
throw new IOException("Start interrupted");
}
}
throw new IOException("No port available between " + minPort + " and " + maxPort);
}
public int getPort()
{
if (acceptor != null)
{
return acceptor.getPort();
}
return 0;
}
public String getURL()
{
int port = acceptor != null ? acceptor.getPort() : 0;
return "http://127.0.0.1:" + port;
}
public synchronized void addContext(Context context)
{
contexts.add(context);
Collections.sort(contexts);
}
public synchronized void removeContext(Context context)
{
contexts.remove(context);
}
public synchronized Context getContext(String path)
{
for (Context context : contexts)
{
if (path.startsWith(context.getPath()))
{
return context;
}
}
return null;
}
public void stop() throws IOException
{
if (acceptor != null)
{
acceptor.interrupt();
threadPool.shutdown();
}
}
@Override
public String toString()
{
return getURL();
}
private static void registerContentType(String contentType, String... extensions)
{
for (String extension : extensions)
{
CONTENT_TYPES.put(extension, contentType);
}
}
static
{
registerContentType("application/java-archive", "jar");
registerContentType("application/javascript", "js");
registerContentType("application/json", "json");
registerContentType("application/jsonml+json", "jsonml");
registerContentType("application/pdf", "pdf");
registerContentType("application/xaml+xml", "xaml");
registerContentType("application/xhtml+xml", "xhtml", "xht");
registerContentType("application/xml", "xml", "xsl");
registerContentType("application/xml-dtd", "dtd");
registerContentType("application/xslt+xml", "xslt");
registerContentType("application/zip", "zip");
registerContentType("image/bmp", "bmp");
registerContentType("image/gif", "gif");
registerContentType("image/jpeg", "jpeg", "jpg", "jpe");
registerContentType("image/png", "png");
registerContentType("image/svg+xml", "svg", "svgz");
registerContentType("image/tiff", "tiff", "tif");
registerContentType("image/x-icon", "ico");
registerContentType("text/css", "css");
registerContentType("text/html", "html", "htm");
registerContentType("text/plain", "txt", "text", "conf", "def", "list", "log", "in");
registerContentType("text/x-java-source", "java");
}
public static void main(String[] args) throws Exception
{
HTTPServer server = new HTTPServer(80, 100);
server.addContext(new FileContext("/file/c", true, new File("C:")));
server.addContext(new FileContext("/file/e", true, new File("E:")));
System.out.println("http://localhost:" + server.getPort());
System.out.println();
while (System.in.available() == 0)
{
Thread.sleep(50);
}
server.stop();
}
/**
* @author Eike Stepper
*/
private final class Acceptor extends Thread
{
private final ServerSocket serverSocket;
private final CountDownLatch started = new CountDownLatch(1);
public Acceptor(ServerSocket serverSocket) throws InterruptedException
{
super("Httpd");
this.serverSocket = serverSocket;
setDaemon(true);
start();
started.await();
}
public int getPort()
{
return serverSocket.getLocalPort();
}
@Override
public void run()
{
started.countDown();
while (true)
{
try
{
Socket socket = serverSocket.accept();
RequestHandler requestHandler = new RequestHandler(socket);
threadPool.execute(requestHandler);
}
catch (Exception ex)
{
if (interrupted())
{
return;
}
UtilPlugin.INSTANCE.log(ex);
}
}
}
@Override
public void interrupt()
{
try
{
super.interrupt();
}
finally
{
IOUtil.closeSilent(serverSocket);
}
}
}
/**
* @author Eike Stepper
*/
private final class RequestHandler implements Runnable
{
private final Socket socket;
public RequestHandler(Socket socket)
{
this.socket = socket;
}
public void run()
{
InputStream inputStream = null;
OutputStream outputStream = null;
try
{
try
{
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
}
catch (Exception ex)
{
if (isBadState())
{
return;
}
UtilPlugin.INSTANCE.log(ex);
}
if (inputStream != null && outputStream != null)
{
BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));
DataOutputStream output = new DataOutputStream(outputStream);
try
{
handleRequest(input, output);
}
catch (Exception ex)
{
if (isBadState())
{
return;
}
if (DEBUG || !(ex instanceof SocketException))
{
UtilPlugin.INSTANCE.log(ex);
}
Context.sendResponse(output, STATUS_INTERNAL_SERVER_ERROR, null, 0, true);
}
try
{
output.flush();
}
catch (IOException ex)
{
if (DEBUG)
{
UtilPlugin.INSTANCE.log(ex);
}
}
}
}
finally
{
IOUtil.closeSilent(outputStream);
IOUtil.closeSilent(inputStream);
IOUtil.closeSilent(socket);
}
}
private void handleRequest(BufferedReader input, DataOutputStream output) throws Exception
{
String line = input.readLine();
if (line == null)
{
return;
}
if (DEBUG_REQUEST)
{
System.out.println();
String l = line;
while (!StringUtil.isEmpty(l))
{
System.out.println(l);
l = input.readLine();
}
}
line = line.replace(" ", " ");
String[] tokens = line.split(" ");
if (tokens.length < 2)
{
Context.sendResponse(output, STATUS_BAD_REQUEST, null, 0, false);
return;
}
String method = tokens[0];
boolean head = "HEAD".equalsIgnoreCase(method);
if (!head && !"GET".equalsIgnoreCase(method))
{
Context.sendResponse(output, STATUS_NOT_IMPLEMENTED, null, 0, false);
return;
}
URI uri = new URI("xxx:" + tokens[1]);
String path = Context.decodePath(uri.getPath());
Context context = getContext(path);
if (context == null)
{
if (PATH_SEPARATOR.equals(path))
{
Context.sendResponse(output, STATUS_OK, "index.html", 0, false);
for (Context c : contexts)
{
if (c != IMAGE_CONTEXT)
{
String href = c.getPath();
output.writeBytes("<img src=\"" + ImageContext.CONTEXT_PATH + ImageContext.NAME_CONTEXT + "\" valign=\"middle\"/> <a href=\"" + href
+ PATH_SEPARATOR + "\">" + href + "</a><br>\r\n");
}
}
return;
}
Context.sendResponse(output, STATUS_NOT_FOUND, null, 0, false);
return;
}
path = path.substring(context.getPath().length());
if (!path.startsWith(PATH_SEPARATOR))
{
path = PATH_SEPARATOR + path;
}
if (DEBUG)
{
System.out.println(context + " " + path);
}
context.handleRequest(path, output, !head);
}
private boolean isBadState()
{
return socket.isClosed() || !socket.isConnected() || socket.isInputShutdown() || socket.isOutputShutdown();
}
}
/**
* @author Eike Stepper
*/
public static abstract class Context implements Comparable<Context>
{
private static final String URL_ENCODING = "UTF-8";
private final String path;
private final boolean allowDirectory;
protected Context(String path, boolean allowDirectory)
{
if (!path.startsWith(PATH_SEPARATOR))
{
throw new IllegalArgumentException("Path must start with a slash: " + path);
}
this.path = path;
this.allowDirectory = allowDirectory;
}
public final String getPath()
{
return path;
}
public final boolean isAllowDirectory()
{
return allowDirectory;
}
public final int compareTo(Context o)
{
return o.path.length() - path.length();
}
public Object getRoot()
{
return null;
}
public String getURL(HTTPServer server)
{
return server.getURL() + path;
}
@Override
public final String toString()
{
String string = getClass().getSimpleName() + "[" + path;
Object root = getRoot();
if (root != null)
{
string += " --> " + root;
}
return string + "]";
}
protected void handleRequest(String path, DataOutputStream output, boolean responseBody) throws IOException
{
if (isDirectory(path))
{
if (!allowDirectory)
{
Context.sendResponse(output, STATUS_FORBIDDEN, null, 0, false);
}
else
{
if (!path.endsWith(PATH_SEPARATOR))
{
path += PATH_SEPARATOR;
Context.sendResponse(output, STATUS_SEE_OTHER, path, 0, false);
}
else
{
Context.sendResponse(output, STATUS_OK, "index.html", 0, false);
}
if (path.length() > 1)
{
output
.writeBytes("<img src=\"" + ImageContext.CONTEXT_PATH + ImageContext.NAME_FOLDER_UP + "\" valign=\"middle\"/> <a href=\"../\">..</a><br>\r\n");
}
String[] children = getChildren(path);
if (children != null)
{
final String finalPath = path;
Arrays.sort(children, new Comparator<String>()
{
public int compare(String n1, String n2)
{
int t1 = getType(n1);
int t2 = getType(n2);
int result = t1 - t2;
if (result == 0)
{
result = n1.toLowerCase().compareTo(n2.toLowerCase());
}
return result;
}
private int getType(String child)
{
return isDirectory(finalPath + child) ? 1 : 2;
}
});
for (String child : children)
{
boolean directory = isDirectory(path + child);
output.writeBytes(
"<img src=\"" + ImageContext.CONTEXT_PATH + (directory ? ImageContext.NAME_FOLDER : ImageContext.NAME_FILE) + "\" valign=\"middle\"/> ");
String trailingSlash = directory ? PATH_SEPARATOR : "";
output.writeBytes("<a href=\"" + encodePath(child) + trailingSlash + "\">" + child + trailingSlash + "</a><br>\r\n");
}
}
}
return;
}
if (!isFile(path))
{
Context.sendResponse(output, STATUS_NOT_FOUND, null, 0, false);
return;
}
long lastModified = getLastModified(path);
Context.sendResponse(output, STATUS_OK, path, lastModified, false);
if (responseBody)
{
InputStream stream = null;
try
{
stream = getContents(path);
IOUtil.copy(stream, output);
}
finally
{
IOUtil.close(stream);
}
}
}
protected abstract boolean isDirectory(String path);
protected abstract boolean isFile(String path);
protected abstract String[] getChildren(String path) throws IOException;
protected abstract InputStream getContents(String path) throws IOException;
protected long getLastModified(String path) throws IOException
{
return System.currentTimeMillis();
}
protected static String encodePath(String path) throws UnsupportedEncodingException
{
StringBuilder builder = new StringBuilder();
StringTokenizer tokenizer = new StringTokenizer(path, PATH_SEPARATOR);
while (tokenizer.hasMoreTokens())
{
if (builder.length() != 0)
{
builder.append(PATH_SEPARATOR);
}
String token = tokenizer.nextToken();
builder.append(URLEncoder.encode(token, URL_ENCODING));
}
return builder.toString();
}
protected static String decodePath(String path) throws UnsupportedEncodingException
{
return URLDecoder.decode(path, URL_ENCODING);
}
@SuppressWarnings({ "deprecation", "restriction" })
protected static String formatDate(long lastModified)
{
return org.apache.http.impl.cookie.DateUtils.formatDate(new Date(lastModified));
}
protected static void sendResponse(DataOutputStream output, String status, String fileName, long lastModified, boolean ignoreExceptions)
{
try
{
output.writeBytes("HTTP/1.0 ");
output.writeBytes(status);
output.writeBytes("\r\nConnection: close\r\nServer: ");
output.writeBytes(HTTPServer.class.getName());
output.writeBytes("\r\n");
String location = null;
if (status == STATUS_SEE_OTHER)
{
location = fileName;
fileName = "index.html";
}
int lastDot = fileName == null ? -1 : fileName.lastIndexOf('.');
String extension = lastDot == -1 ? "txt" : fileName.substring(lastDot + 1);
String contentType = CONTENT_TYPES.get(extension);
if (contentType == null)
{
contentType = CONTENT_TYPES.get("txt");
}
output.writeBytes("Content-Type: ");
output.writeBytes(contentType);
output.writeBytes("\r\n");
if (lastModified != 0)
{
output.writeBytes("Last-Modified: ");
output.writeBytes(formatDate(lastModified));
output.writeBytes("\r\n");
}
if (location != null)
{
output.writeBytes("Location: /file/c");
output.writeBytes(location);
output.writeBytes("\r\n");
output.writeBytes("\r\n");
return;
}
output.writeBytes("\r\n");
if (status == STATUS_OK)
{
return;
}
output.writeBytes(status);
output.writeBytes("\r\n");
}
catch (IOException ex)
{
if (ignoreExceptions)
{
return;
}
if (ex instanceof SocketException && ex.getMessage().equals("Software caused connection abort: socket write error"))
{
return;
}
UtilPlugin.INSTANCE.log(ex);
}
}
}
/**
* @author Eike Stepper
*/
public static class ImageContext extends Context
{
public static final String CONTEXT_PATH = PATH_SEPARATOR + "~";
public static final String NAME_CONTEXT = PATH_SEPARATOR + "context";
public static final String NAME_FOLDER_UP = PATH_SEPARATOR + "folderup";
public static final String NAME_FOLDER = PATH_SEPARATOR + "folder";
public static final String NAME_FILE = PATH_SEPARATOR + "file";
private static final byte[] ICON_CONTEXT = HexUtil.hexToBytes(
"47494638396110001000e600002d2b4f3c4967f2f6ff3c4a672e4162304262394a68002e77002b723146672f4363344a6d304362384d6f374c6d384c6d394b6a384a683d5070b3bdcd002f77003177344a6c324767304463354b6c384e6eb2bdcdb4bfceb4bcc7b3bfceb8c3d0246ebd2770be2870be2971be4e69874f69874f6a87b4bdc7f2f8ff256fbd2973c14e6c8af1f8fff3f9fff7fbff4e6d8aeef7fff0f8fff4faffeff8fff1f9fff6fbff448fc3448ec2448fc2f2fafff4fbfff7fcff529bbf519abe579cbff9fdfffcfeff539cbf549cbf579dbff8fefffdfffffeffffa1c3bea2c3bea1c2bda3c3bea2c2bdd1e8e4a3c3bdcbefc2cbeec2cbecbeccedbeeaefe7e9eee5eaeee5e9ede3e6eae0eaeee3ffffc4ffffcbffffd2ffffd7ffffdcebebe2c8c498c7c498cac59aecebe2e6e3d2e6e2cfe6e2d0e5e1cfe7e3d2e5e1d0f4f0e3f3efe3ffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021f9040100006b002c00000000100010000007b3806b8283848583001f524c2a2636244169861f1a19604f62106749213e8552195c0b1b6a096a6a50013d844c600b5b161e5fa60447295d832a4e130b5a0d0e5346456a493c8326666a1c0d59120755406a6524833810175e0f12582b08450a1137832464443b54142f151d353fd2833c49322839570827562d2ec4835d2050053066e8a011830192146108f508a084c50c0c33044419a0aad090114dc61818b344c4104382d0082971a30424902853aa141408003b");
private static final byte[] ICON_FOLDER_UP = HexUtil.hexToBytes(
"47494638396110001000841d009c6a3c41913b4a9045ac7a44ac7a4cac825cb48a54bc9264bc926cc4a274ccaa74d4b27cdcb2745edb63e4c27c72e57f93e596ecd28cfcd27cf4da949df3a7fcda8cfce29cfce2a4aff8b7fcea9cfceab4fcf2ccfcfefcffffffffffffffffff21f904010a001f002c000000001000100000056de0278e64699ee7b2280c3a1a5ccc19ae925951e42809df93898d7023180a13408de62280082e4b0d0178b1081e108ae0c0e5220e1f4345d0c05230024bc53289800d128120e094572a39f067203938e80212120e126e230007824d81827d2505908090902e8b2e2502979a2421003b");
private static final byte[] ICON_FOLDER = HexUtil.hexToBytes(
"47494638396110001000c40000f8e898f8f0c8f8e8b0e8d088f0d890f8e098f8e0a0f8d888f8d078e0c078f8d890f8d080d8b070bc8532c38b36b47f32a56c24ad722bbc7f32c38536ad6c249e6627ad722f9e5f1d9e5f208f5219ffffffffffff00000000000000000000000021f9040100001b002c0000000010001000000555e0268e64699ea7a33ae84869b046b513001043c24c3c4f3681a070d8204904c8a412437a189e068b744add4414584b61cbdd0e2c1bc8e160199bcd5fd1056159b8dfeeb4286341d8ef76f9a8c2effb5b8081828021003b");
private static final byte[] ICON_FILE = HexUtil.hexToBytes(
"47494638396110001000a529007b744f7e754e7e764e84784c867b4c8b7c4a8d7e4a9280489b83459d85449e8544a38842a38843a98a41ad8c3fb18f3eb4903dd4b268d4b269d5b269dabd7ce0c88fdde8f7e1ebf8e4edf9e8effaecf1faedf2faeef3faf0f4fbf3f7fbf5f7fbf6f7fcf5f8fbf5f8fcf6f8fbf7f9fbf8f9fbf8f9fcf7fafcf9fafcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff21f904010a003f002c0000000010001000000669c09f10422c1285482404c56c4a2049e4c3442d9d4894c923fa73843e20d04854a1441cd18667cd6637a28c8e7c3e5f44159bfc86a3df24ee1a8182827f490819888989075106188f909005510417969797035102169d9e9e019b9fa3a14900a7a8a9005cacadae4941003b");
private static final byte[] ICON_UNKNOWN = HexUtil.hexToBytes(
"47494638396110001000d50000f7f8fcf3f5fbf8f9fcf5f7fcf3f5faf1f4fbeef2faf2f5fbf5f7fbebf0f9f0f4fbe5ecf8e7eef9e9eff9edf2faf4f7fce5edf9e4ecf8e8eff9ebf1faf0f4faf6f8fbe3ecf8e6eef9e5edf8ebf1f9eef3fae4edf8e6eef8eaf1faf2f6fbf1f5fadbe4eee8f0f9edf3faf0f5fa2862964476a45d89b15f8ab16a92b76c94b8799dbe95b2cca2bbd35080aa7b745081774e928048897a4c85784d9782478e7d4aa68942a287439d8445b4903db18e3eaf8d3fab8b40ffffff00000000000000000021f9040100003c002c000000001000100000068d409e70482c0e71c8a41249cc099ed0688ea88b5a053ae20e5001954860107747ac211e27526a05460c6a44dbe1c0521d022d12c16323de280a2305232624261f0a3744331a06060e852822061a3344301913131d609a09133044342112a52c2c210d120d3444311c171c0c60b017173144320b18181110bf181b1132442f16c7c8c92f442ecdcecfcd46d24541003b");
public ImageContext()
{
super(CONTEXT_PATH, false);
}
@Override
protected boolean isDirectory(String path)
{
return false;
}
@Override
protected boolean isFile(String path)
{
return true;
}
@Override
protected String[] getChildren(String path) throws IOException
{
return null;
}
@Override
protected InputStream getContents(String path) throws IOException
{
byte[] bytes = getImage(path);
return new ByteArrayInputStream(bytes);
}
private byte[] getImage(String path)
{
if (NAME_CONTEXT.equals(path))
{
return ICON_CONTEXT;
}
if (NAME_FOLDER_UP.equals(path))
{
return ICON_FOLDER_UP;
}
if (NAME_FOLDER.equals(path))
{
return ICON_FOLDER;
}
if (NAME_FILE.equals(path))
{
return ICON_FILE;
}
return ICON_UNKNOWN;
}
public static void main(String[] args)
{
System.out.println("private static final byte[] ICON_CONTEXT = HexUtil.hexToBytes(\""
+ HexUtil.bytesToHex(IOUtil.readFile(new File("/develop/icons/configuration_obj.gif"))) + "\");");
System.out.println("private static final byte[] ICON_FOLDER_UP = HexUtil.hexToBytes(\""
+ HexUtil.bytesToHex(IOUtil.readFile(new File("/develop/icons/FolderUp.gif"))) + "\");");
System.out.println("private static final byte[] ICON_FOLDER = HexUtil.hexToBytes(\""
+ HexUtil.bytesToHex(IOUtil.readFile(new File("/develop/icons/folder7.gif"))) + "\");");
System.out.println(
"private static final byte[] ICON_FILE = HexUtil.hexToBytes(\"" + HexUtil.bytesToHex(IOUtil.readFile(new File("/develop/icons/file.gif"))) + "\");");
System.out.println("private static final byte[] ICON_UNKNOWN = HexUtil.hexToBytes(\""
+ HexUtil.bytesToHex(IOUtil.readFile(new File("/develop/icons/unknown_obj.gif"))) + "\");");
}
}
/**
* @author Eike Stepper
*/
public static class FileContext extends Context
{
private final File root;
public FileContext(String path, boolean allowDirectory, File root)
{
super(path, allowDirectory);
this.root = root;
}
@Override
public final File getRoot()
{
return root;
}
@Override
protected boolean isDirectory(String path)
{
return getFile(path).isDirectory();
}
@Override
protected boolean isFile(String path)
{
return getFile(path).isFile();
}
@Override
protected String[] getChildren(String path) throws IOException
{
return getFile(path).list();
}
@Override
protected InputStream getContents(String path) throws IOException
{
File file = getFile(path);
if (DEBUG_RESPONSE /* && path.endsWith(".html") */)
{
try
{
String contents = IOUtil.readUTF8(file);
System.out.println(contents);
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
return new FileInputStream(file);
}
@Override
protected long getLastModified(String path) throws IOException
{
File file = getFile(path);
return file.lastModified();
}
private File getFile(String path)
{
if (root == null)
{
return new File(path);
}
return new File(root, path);
}
}
/**
* @author Eike Stepper
*/
public static class PluginContext extends Context
{
public PluginContext(String path, boolean allowDirectory)
{
super(path, allowDirectory);
}
@Override
protected boolean isDirectory(String path)
{
if (path.length() == 1)
{
// The context root is a directory of the resolved plugins.
return true;
}
try
{
BundleFile file = getFile(path);
return file.isDirectory();
}
catch (FileNotFoundException ex)
{
return false;
}
}
@Override
protected boolean isFile(String path)
{
try
{
BundleFile file = getFile(path);
return !file.isDirectory();
}
catch (FileNotFoundException ex)
{
return false;
}
}
@Override
protected String[] getChildren(String path) throws IOException
{
String[] result;
if (path.length() == 1)
{
// The context root is a directory of the resolved plugins.
Bundle[] bundles = UtilPlugin.INSTANCE.getBundleContext().getBundles();
result = new String[bundles.length];
for (int i = 0; i < bundles.length; i++)
{
result[i] = bundles[i].getSymbolicName();
}
}
else
{
BundleFile file = getFile(path);
List<BundleFile> children = file.getChildren();
result = new String[children.size()];
for (int i = 0; i < result.length; i++)
{
result[i] = children.get(i).getName();
}
}
return result;
}
@Override
protected InputStream getContents(String path) throws IOException
{
BundleFile file = getFile(path);
if (DEBUG_RESPONSE /* && path.endsWith(".html") */)
{
try
{
String contents = file.getContentsString();
System.out.println(contents);
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
return file.getContents();
}
@Override
protected long getLastModified(String path) throws IOException
{
BundleFile file = getFile(path);
return file.getBundle().getLastModified();
}
private BundleFile getFile(String path) throws FileNotFoundException
{
String[] segments = path.split(PATH_SEPARATOR);
Bundle bundle = Platform.getBundle(segments[1]);
BundleFile file = new RootBundleFile(bundle);
for (int i = 2; file != null && i < segments.length; i++)
{
String segment = segments[i];
file = file.getChild(segment);
}
if (file == null)
{
throw new FileNotFoundException(path);
}
return file;
}
/**
* @author Eike Stepper
*/
private static class RootBundleFile extends BundleFile
{
private Bundle bundle;
public RootBundleFile(Bundle bundle)
{
super("", true, null);
this.bundle = bundle;
}
@Override
public Bundle getBundle()
{
return bundle;
}
@Override
public String getPath()
{
return "";
}
}
}
}