package org.dbwiki.web.server;
import java.net.InetSocketAddress;
import java.net.URL;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.Properties;
import java.util.Vector;
import java.util.concurrent.Executors;
import org.dbwiki.data.database.Database;
import org.dbwiki.data.io.ImportHandler;
import org.dbwiki.data.io.XMLDocumentImportReader;
import org.dbwiki.data.schema.DatabaseSchema;
import org.dbwiki.driver.rdbms.SQLVersionIndex;
import org.dbwiki.exception.WikiException;
import org.dbwiki.exception.WikiFatalException;
import org.dbwiki.user.User;
import org.dbwiki.web.html.FatalExceptionPage;
import org.dbwiki.web.html.RedirectPage;
import org.dbwiki.web.request.Exchange;
import org.dbwiki.web.request.RequestURL;
import org.dbwiki.web.request.parameter.RequestParameter;
import org.dbwiki.web.security.WikiAuthenticator;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class WikiServerHttpHandler extends WikiServer implements HttpHandler {
private Vector<DatabaseWikiHttpHandler> _wikiListing;
protected HttpServer _webServer = null;
protected int _port;
protected int _threadCount;
protected int _backlog;
/*
* Constructors
*/
public WikiServerHttpHandler(Properties properties) throws WikiException {
super(properties);
// Web Server Properties
_backlog = Integer.parseInt(properties.getProperty(propertyBacklog));
_port = Integer.parseInt(properties.getProperty(propertyPort));
_threadCount = Integer.parseInt(properties.getProperty(propertyThreadCount));
}
/**
* Initialize list of DatabaseWikis from database
* @param con
* @throws SQLException
* @throws WikiException
*/
protected void getWikiListing (Connection con) throws SQLException, WikiException {
_wikiListing = new Vector<DatabaseWikiHttpHandler>();
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM " + RelationDatabase + " " +
" WHERE " + RelDatabaseColIsActive + " = " + RelDatabaseColIsActiveValTrue + " " +
"ORDER BY " + RelDatabaseColTitle);
while (rs.next()) {
int id = rs.getInt(RelDatabaseColID);
String name = rs.getString(RelDatabaseColName);
String title = rs.getString(RelDatabaseColTitle);
int layoutVersion = rs.getInt(RelDatabaseColLayout);
int templateVersion = rs.getInt(RelDatabaseColTemplate);
int styleSheetVersion = rs.getInt(RelDatabaseColCSS);
int urlDecodingVersion = RelConfigFileColFileVersionValUnknown;
if (org.dbwiki.lib.JDBC.hasColumn(rs, RelDatabaseColURLDecoding)) {
urlDecodingVersion = rs.getInt(RelDatabaseColURLDecoding);
}
WikiAuthenticator authenticator = new WikiAuthenticator("/" + name, rs.getInt(RelDatabaseColAuthentication), _users);
int autoSchemaChanges = rs.getInt(RelDatabaseColAutoSchemaChanges);
ConfigSetting setting = new ConfigSetting(layoutVersion, templateVersion, styleSheetVersion, urlDecodingVersion);
_wikiListing.add(new DatabaseWikiHttpHandler(id, name, title, authenticator, autoSchemaChanges, setting, _connector, this));
}
rs.close();
stmt.close();
}
/*
* Getters
*/
/** Get the DatabaseWiki with index i */
public DatabaseWiki get(int index) {
return _wikiListing.get(index);
}
/**
*
* @return The number of DatabaseWikis
*/
public int size() {
return _wikiListing.size();
}
/** Starts the web server and creates an individual handler
* for each of the current wikis.
*
* @throws java.io.IOException
*/
public void start() throws java.io.IOException {
_webServer = HttpServer.create(new InetSocketAddress(_port), _backlog);
_webServer.setExecutor(Executors.newFixedThreadPool(_threadCount));
HttpContext context = _webServer.createContext("/", this);
context.setAuthenticator(new WikiAuthenticator("/", _authenticationMode, _users));
for (int iWiki = 0; iWiki < _wikiListing.size(); iWiki++) {
DatabaseWikiHttpHandler wiki = _wikiListing.get(iWiki);
context = _webServer.createContext("/" + wiki.name(), wiki);
context.setAuthenticator(wiki.authenticator());
}
System.out.println("START SERVER ON ADDRESS " + _webServer.getAddress() + " AT " + new java.util.Date().toString());
_webServer.start();
}
/** Creates new database with a given schema and import given data into it
* TODO #import Move this into a separate class, to factor out common functionality with DatabaseImport
* TODO #server Split this into server-level and data-level stuff.
* @param name - string identifying database tables
* @param title - human readable title
* @param path - path to entries in the document
* @param resource
* @param databaseSchema
* @param user
* @param authenticationMode
* @param autoSchemaChanges
* @throws NumberFormatException
* @throws WikiException
* @throws SQLException
*/
public void registerDatabase(String name, String title, String path, URL resource, DatabaseSchema databaseSchema, User user, int authenticationMode, int autoSchemaChanges)
throws NumberFormatException, WikiException, SQLException {
Connection con = _connector.getConnection();
int wikiID = -1;
SQLVersionIndex versionIndex = new SQLVersionIndex(con, name, users(), true);
CreateDatabaseRecord r = new CreateDatabaseRecord(name,title,authenticationMode,autoSchemaChanges,databaseSchema,user);
con.setAutoCommit(false);
con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
try {
wikiID = r.createDatabase(con, versionIndex);
con.commit();
WikiAuthenticator authenticator = new WikiAuthenticator("/" + name, authenticationMode, _users);
DatabaseWikiHttpHandler wiki = new DatabaseWikiHttpHandler(wikiID, name, title, authenticator, autoSchemaChanges, _connector, this,
con, versionIndex);
// this should now only be called when starting a web server
assert(_webServer != null);
String realm = wiki.database().identifier().databaseHomepage();
HttpContext context = _webServer.createContext(realm, wiki);
context.setAuthenticator(authenticator);
_wikiListing.add(wiki);
Collections.sort(_wikiListing);
//
// Import data into created database wiki if the user specified an import resource.
//
if (resource != null) {
Database database = wiki.database();
// Note that database.schema() is a copy of databaseSchema that has been read back from the database
// after being loaded in when we created new database above.
// We should really deal with the target path separately, e.g. via extra text field
XMLDocumentImportReader reader = new XMLDocumentImportReader(resource,
database.schema(),
path, user, false, false);
ImportHandler importHandler = database.createImportHandler(con);
reader.setImportHandler(importHandler);
reader.start();
}
} catch (java.sql.SQLException sqlException) {
con.rollback();
con.close();
throw new WikiFatalException(sqlException);
}
con.commit();
con.close();
}
public void sortWikiListing() {
Collections.sort(_wikiListing);
}
/** Implements HttpHandler.handle() which is called by the Web Server
* for every browser request (Note that requests for the individual
* wikis are handled by DatabaseWiki.handle())
* The HttpExchange is side-effected and the response part is eventually sent back to the client.
*/
public void handle(HttpExchange httpExchange) throws java.io.IOException {
Exchange<HttpExchange> exchange = new HttpExchangeWrapper(httpExchange);
try {
String path = exchange.getRequestURI().getPath();
if (path.equals("/")) {
if (_serverLog != null) {
_serverLog.logRequest(exchange.getRequestURI(),exchange.get().getRemoteAddress(),exchange.get().getResponseHeaders());
}
this.respondTo(exchange);
} else if ((path.startsWith(SpecialFolderDatabaseWikiStyle + "/")) && (path.endsWith(".css"))) {
this.sendCSSFile(path.substring(SpecialFolderDatabaseWikiStyle.length() + 1, path.length() - 4), exchange);
} else if (path.equals(SpecialFolderLogin)) {
//FIXME: #request This is a convoluted way of parsing the request parameter!
exchange.send(new RedirectPage(new RequestURL( exchange,"").parameters().get(RequestParameter.ParameterResource).value()));
// The following code is necessary if using only a single HttpContext
// instead of multiple ones (i.e., one per Database Wiki).
//} else if (path.length() > 1) {
// int pos = path.indexOf('/', 1);
// DatabaseWiki wiki = null;
// if (pos != -1) {
// wiki = this.get(path.substring(1, pos));
// } else {
// wiki = this.get(path.substring(1));
// }
// if (wiki != null) {
// wiki.handle(exchange);
// } else {
// this.sendFile(exchange);
// }
} else {
this.sendFile(exchange);
}
} catch (Exception exception) {
exchange.send(new FatalExceptionPage(exception));
}
}
}