/**
* NanoDoA - File based document archive
*
* Copyright (C) 2011-2012 Christian Packenius, christian.packenius@googlemail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.chris_soft.nanodoa.web;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import de.chris_soft.nanoarchive.Archive;
import de.chris_soft.utilities.FileUtils;
import de.chris_soft.utilities.web.HtmlWebsite;
import fi.iki.elonen.NanoHTTPD;
/**
* HTTP server and html page building.
* @author Christian Packenius.
*/
public class DocumentServer extends NanoHTTPD {
private Map<String, Class<? extends HtmlWebsite>> name2websiteClass = new HashMap<String, Class<? extends HtmlWebsite>>();
private String indexWebsite = null;
private final Response unknownSiteResponse = new Response(HTTP_NOTIMPLEMENTED, MIME_PLAINTEXT, "Website not found!");
private final Archive documentArchive;
/**
* Constructor.
* @param documentArchive DocumentArchive.
* @param port Port for http serving.
* @throws Exception
*/
public DocumentServer(Archive documentArchive, int port) throws Exception {
super(port, new File("web"));
this.documentArchive = documentArchive;
DocumentRetrievalWebsite.archive = this.documentArchive;
System.out.println("Document server started at port " + port + ".");
addDistinctWebsites();
}
private void addDistinctWebsites() throws NoSuchMethodException, InstantiationException, IllegalAccessException,
InvocationTargetException {
addWebsite(DocumentRetrievalWebsite.class);
addWebsite(DocumentSearchWebsite.class);
indexWebsite = new DocumentSearchWebsite().getWebsiteName();
}
/**
* Adds a specific website to the web application.
* @param websiteClass Website class for the website.
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws NoSuchMethodException
*/
public void addWebsite(Class<? extends HtmlWebsite> websiteClass) throws NoSuchMethodException,
InstantiationException, IllegalAccessException, InvocationTargetException {
String websiteName = createWebsiteObject(null, websiteClass, null).getWebsiteName();
name2websiteClass.put(websiteName, websiteClass);
}
/**
* Set the website that is shown if no site name is given in the uri.
* @param siteName Name of the initial website.
*/
public void setIndexWebsite(String siteName) {
indexWebsite = siteName;
}
/**
* @see fi.iki.elonen.NanoHTTPD#serve(java.lang.String, java.lang.String,
* java.util.Properties, java.util.Properties, java.util.Properties)
*/
@Override
public Response serve(String uri, String method, Properties header, Properties parms, Properties files) {
try {
if (new File("web" + uri).exists()) {
return getFileResponseFromURI(uri);
}
System.out.println("DocumentServer quest: " + method + " " + uri + " " + parms);
System.out.println(" header: " + header);
List<String> uriParts = getUriParts(uri);
String siteName = getSiteName(uriParts);
return getResponseByWebsiteName(parms, siteName, uriParts);
}
catch (Exception e) {
return unknownSiteResponse;
}
}
/**
* Set file in URI as HTTP response.
* @param uri URI representing the file.
* @return File-Response.
* @throws IOException
*/
public Response getFileResponseFromURI(String uri) throws IOException {
File file = new File("web" + uri);
return getFileResponseFromFile(file);
}
/**
* Set file as HTTP response.
* @param file File to send as response.
* @return File-Response.
* @throws IOException
*/
public Response getFileResponseFromFile(File file) throws IOException {
String mimeType = theMimeTypes.get(FileUtils.getFileExtension(file));
if (mimeType == null) {
mimeType = MIME_PLAINTEXT;
}
byte[] fileContent = FileUtils.readFileAsBytes(file);
return new Response(HTTP_OK, mimeType, fileContent);
}
private Response getResponseByWebsiteName(Properties parms, String siteName, List<String> uriParts)
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class<? extends HtmlWebsite> websiteClass = name2websiteClass.get(siteName);
if (websiteClass == null) {
return unknownSiteResponse;
}
HtmlWebsite website = createWebsiteObject(parms, websiteClass, uriParts);
try {
return getResponseFromWebsite(website);
}
catch (IOException e) {
return getFileNotFoundWebsiteContent();
}
}
private Response getResponseFromWebsite(HtmlWebsite website) throws IOException {
String websiteContent = website.getSiteContent();
File file = website.getResponseFile();
if (file == null) {
return new Response(HTTP_OK, MIME_HTML, websiteContent);
}
if (file.exists()) {
System.out.println("Serve file: " + file.getAbsolutePath());
// Das folgende funktioniert bei Google Chrome schlicht nicht:
// return serveFile("/" + file.getName(), new Properties(),
// file.getParentFile(), false);
return getFileResponseFromFile(file);
}
return getFileNotFoundWebsiteContent();
}
private Response getFileNotFoundWebsiteContent() {
String websiteContent = new DocumentNotFoundWebsite().getSiteContent();
return new Response(HTTP_OK, MIME_HTML, websiteContent);
}
private HtmlWebsite createWebsiteObject(Properties parms, Class<? extends HtmlWebsite> websiteClass,
List<String> uriParts) throws NoSuchMethodException, InstantiationException, IllegalAccessException,
InvocationTargetException {
Constructor<? extends HtmlWebsite> websiteConstructor = websiteClass.getConstructor();
HtmlWebsite website = websiteConstructor.newInstance();
website.setParameters(parms);
return website;
}
private String getSiteName(List<String> uriParts) {
final String siteName;
if (uriParts.isEmpty()) {
siteName = indexWebsite;
}
else {
siteName = uriParts.remove(0);
}
return siteName;
}
private List<String> getUriParts(String uri) {
List<String> uriParts = new ArrayList<String>();
int k;
while (uri != null && uri.length() > 0) {
if (uri.startsWith("/")) {
uri = uri.substring(1);
}
else if ((k = uri.indexOf('/')) >= 0) {
uriParts.add(uri.substring(0, k));
uri = uri.substring(k + 1).trim();
}
else {
uriParts.add(uri);
break;
}
}
return uriParts;
}
}