/*
* Copyright 2013 Simon Thiel
*
* This file is part of SitJar.
*
* SitJar is free software: you can redistribute it and/or modify
* it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SitJar 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SitJar. If not, see <http://www.gnu.org/licenses/lgpl.txt>.
*/
/**
* @author Simon Thiel <simon.thiel at gmx.de>
*/
package sit.web;
/*
* With some inspiration from
* http://java.sun.com/developer/technicalArticles/Networking/Webserver/
*/
import java.io.File;
import java.io.IOException;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import sit.web.socket.DefaultServerSocket;
import sit.web.socket.ServerSocketI;
import sit.web.socket.SocketI;
public class WebServer implements HttpConstants, Runnable {
/**
* Singleton instance
*/
private final static WebServer instance = new WebServer();
/**
* private constructor to enforce singelton
*/
private WebServer() {
}
/* print to the log file */
protected void log(String s) {
Logger.getLogger(getClass().getName()).log(Level.FINE, s);
}
private ServerSocketI serverSocket=null;
/* Where worker threads stand idle */
private final Vector<WebWorker> threads = new Vector();
/* the web server's virtual root */
private File root = new File(".");
/* timeout on client connections */
private int timeout = 5000;
/* max # worker threads */
private int workers = 5;
private int port = 8080;
private boolean permitDirectoryListing = true;
private boolean stopping = false;
private void init() throws Exception {
/* start worker threads */
for (int i = 0; i < workers; ++i) {
WebWorker w = new WebWorker();
(new Thread(w, "worker #" + i)).start();
threads.addElement(w);
}
}
public void initServerSocket(ServerSocketI serverSocket){
if (this.serverSocket!=null){
throw new RuntimeException("ServerSocket already initialized!");
}//else
this.serverSocket = serverSocket;
}
private ServerSocketI getServerSocket() throws IOException{
if (this.serverSocket==null){
this.serverSocket = new DefaultServerSocket(getPort());
}
return this.serverSocket;
}
public void run() {
ServerSocketI ss = null;
try {
log("init webserver");
init();
ss = getServerSocket();
Logger.getLogger(WebServer.class.getName()).log(Level.INFO, "Server is listening at port: " + port);
Logger.getLogger(WebServer.class.getName()).log(Level.INFO, "Root is set to: " + root.getAbsolutePath());
} catch (IOException ex) {
Logger.getLogger(WebServer.class.getName()).log(Level.SEVERE, null, ex);
stopping = true;
} catch (Exception ex) {
Logger.getLogger(WebServer.class.getName()).log(Level.SEVERE, null, ex);
stopping = true;
}
while (!stopping) {
try {
SocketI s = ss.accept(); //wait for incoming connection
WebWorker w = null;
synchronized (this) {
if (threads.isEmpty()) {
//all threads are busy - create new one
WebWorker ws = new WebWorker();
ws.setSocket(s);
(new Thread(ws, "additional worker")).start();
Logger.getLogger(WebServer.class.getName()).log(Level.FINE, "started new WebWorker");
} else {
//use thread from thread pool
w = threads.elementAt(0);
threads.removeElementAt(0);
w.setSocket(s);
}
}
} catch (IOException ex) {
Logger.getLogger(WebServer.class.getName()).log(Level.SEVERE, null, ex);
stopping = true;
}
}
Logger.getLogger(WebServer.class.getName()).log(Level.INFO, "Server stopped.");
}
public synchronized void setRoot(File root) {
this.root = root;
}
/**
* should only be called from WebWorker Threads
* to add them to the thread pool
* @param worker
*/
synchronized void addThreadToPool(WebWorker worker) {
if (threads.size() < workers) {
threads.add(worker);
} else {
worker.stop();
}
}
public synchronized int getTimeOut() {
return timeout;
}
public synchronized File getRoot() {
return root;
}
public synchronized static WebServer getInstance() {
return instance;
}
/**
* @return the port
*/
public int getPort() {
return port;
}
/**
* @param port the port to set
*/
public void setPort(int port) {
this.port = port;
}
/**
* @return the permitDirectoryListing
*/
public boolean isPermitDirectoryListing() {
return permitDirectoryListing;
}
/**
* @param permitDirectoryListing the permitDirectoryListing to set
*/
public synchronized void setPermitDirectoryListing(boolean permitDirectoryListing) {
this.permitDirectoryListing = permitDirectoryListing;
}
}