package zeta;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import zeta.handler.GetHandler;
import zeta.handler.PostHandler;
import zeta.handler.database.ConnectionDriver;
import zeta.handler.statistic.AbstractHandler;
import zeta.util.DatabaseUtils;
import zeta.util.Parameter;
import zeta.util.Properties;
import zeta.util.StreamUtils;
public class ZetaServlet extends HttpServlet {
public ZetaServlet() {
getHandlers = new ArrayList(20);
postHandlers = new ArrayList(5);
}
private Hashtable parameters = new Hashtable(100);
public String getInitParameter(String name) {
String result = null;
try {
result = super.getInitParameter(name);
} catch (NullPointerException e) {
}
if (result == null) {
synchronized (parameters) {
File file = new File(ZetaConstant.INIT_PARAMETER_PATH + name);
if (file.exists()) {
Long lastModified = new Long(file.lastModified());
Object[] obj = (Object[])parameters.get(name);
if (obj == null) {
obj = new Object[2];
parameters.put(name, obj);
}
if (!lastModified.equals(obj[0])) {
obj[0] = lastModified;
try {
obj[1] = new String(StreamUtils.getFile(file.getAbsolutePath(), false, false));
} catch (IOException ioe) {
obj[1] = null;
}
}
result = (String)obj[1];
}
try {
Properties properties = new Properties();
result = properties.get(name);
if (properties.get("server_id", 1) != 1) {
Object[] obj = new Object[2];
obj[0] = new Long(System.currentTimeMillis());
obj[1] = result;
parameters.put(name, obj);
}
} catch (Exception e) {
}
}
}
return result;
}
public Enumeration getInitParameterNames() {
if (hasSeparateFiles()) {
File file = new File(ZetaConstant.INIT_PARAMETER_PATH + '.');
String[] list = file.list();
if (list != null) {
for (int i = 0; i < list.length; ++i) {
parameters.put(list[i], new Object[2]);
}
}
return parameters.keys();
} else {
return super.getInitParameterNames();
}
}
public int getInitParameter(String name, int defaultValue) {
String value = getInitParameter(name);
if (value != null) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException nfe) {
}
}
return defaultValue;
}
public boolean hasSeparateFiles() {
return (parameters.size() > 0);
}
public void init() throws ServletException {
try {
serverId = Integer.parseInt(getInitParameter("server.id"));
String driver = getInitParameter("database.connection.driver");
try {
connectionDriver = new ConnectionDriver(driver,
getInitParameter("database.connection.url"),
getInitParameter("database.connection.username"),
getInitParameter("database.connection.password"),
getInitParameter("database.connection.poolsize", 10),
getInitParameter("database.connection.total.connections", 50),
getInitParameter("database.connection.check.query"));
poolsize = 0;
} catch (Throwable t) {
throw new ServletException(t);
}
normalLogging = true;
Connection con = null;
Statement stmt = null;
FileWriter fout = null;
try {
con = getConnection();
stmt = con.createStatement();
String pathLog = Parameter.getValue(stmt, "path_log", Parameter.GLOBAL_PARAMETER, null, 3600000);
if (pathLog != null && pathLog.length() > 0) {
synchronized (stdLogFilename) {
errorLogFilename = pathLog + "error_servlet.log";
stdLogFilename = pathLog + "servlet.log";
if (new File(stdLogFilename).length() >= MAX_LOG_FILESIZE) {
rollOver(errorLogFilename, MAX_BACKUP_INDEX);
rollOver(stdLogFilename, MAX_BACKUP_INDEX);
}
errorLog = new FileWriter(errorLogFilename, true);
stdLog = new FileWriter(stdLogFilename, true);
normalLogging = false;
fout = new FileWriter(pathLog + "init.log", true);
fout.write(logFormat.format(new Date())+'\n');
}
}
} catch (Exception e) {
normalLogging = false;
} finally {
StreamUtils.close(fout);
DatabaseUtils.close(stmt);
DatabaseUtils.close(con);
}
Runtime runtime = Runtime.getRuntime();
log("\n\n\n===========\ninit config, free:" + runtime.freeMemory() + " total=" + runtime.totalMemory() + " max=" + runtime.maxMemory());
numberOfStatisticHandlers = 0;
try {
List statisticHandlers = new ArrayList(20);
Enumeration enum = getInitParameterNames();
while (enum.hasMoreElements()) {
String name = (String)enum.nextElement();
addStatisticHandler(name, getInitParameter(name), statisticHandlers);
log("add statistic handler " + name + ", free:" + runtime.freeMemory() + " total=" + runtime.totalMemory() + " max=" + runtime.maxMemory());
}
final int l = statisticHandlers.size();
for (int i = 0; i < l; ++i) {
getHandlers.add(statisticHandlers.get(i));
}
log("init " + numberOfStatisticHandlers + " statistic handlers, free:" + runtime.freeMemory() + " total=" + runtime.totalMemory() + " max=" + runtime.maxMemory());
} catch (Throwable t) {
throw new ServletException(t);
}
} catch (ServletException se) {
log(se);
throw se;
}
}
public void destroy() {
super.destroy();
}
public Connection getConnection() throws ServletException {
try {
String url = getInitParameter("database.connection.url");
synchronized (ZetaServlet.class) {
if (url != null && !url.equals(connectionDriver.getURL())) {
connectionDriver.setURL(url);
poolsize = 0;
}
int sz = connectionDriver.getPoolsize();
if (sz != poolsize) {
log("current poolsize " + sz);
poolsize = sz;
}
}
return DriverManager.getConnection("jdbc:pool:zetagrid");
} catch (Exception e) {
throw new ServletException(e);
}
}
public int getServerId() {
return serverId;
}
public void setServerId(int serverId) {
this.serverId = serverId;
}
public int getNumberOfStatisticHandlers() {
return numberOfStatisticHandlers;
}
public Class getStatisticHandlerClass(int i) {
int idx = -1;
String handlerStatistic = "zeta.handler.statistic"; final int l = getHandlers.size();
for (int j = 0; j < l; ++j) {
Object[] obj = (Object[])getHandlers.get(j);
if (obj[2].getClass().getName().startsWith(handlerStatistic) && ++idx == i) {
return obj[2].getClass();
}
}
return null;
}
public String getStatisticHandlerName(Class handler) {
Iterator iter = getHandlers.iterator();
while (iter.hasNext()) {
Object[] obj = (Object[])iter.next();
if (handler == obj[2].getClass()) {
return (String)obj[1];
}
}
return null;
}
public AbstractHandler getStatisticHandler(Class handler) {
Iterator iter = getHandlers.iterator();
while (iter.hasNext()) {
Object[] obj = (Object[])iter.next();
if (handler == obj[2].getClass()) {
return (AbstractHandler)obj[2];
}
}
return null;
}
public String getHandlerAddress(Class handler) {
Iterator iter = getHandlers.iterator();
while (iter.hasNext()) {
Object[] obj = (Object[])iter.next();
if (handler == obj[2].getClass()) {
return (hasSeparateFiles())? "/servlet/service" + obj[0] : "/zeta/service" + obj[0]; }
}
return null;
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Runtime runtime = Runtime.getRuntime();
int localId = ++id;
try {
GetHandler handler = (GetHandler)findHandler(getHandlers, req.getPathInfo());
if (handler != null) {
log("start " + localId + ':' + req.getPathInfo() + ' ' + req.getRemoteHost() + " (" + req.getRemoteAddr() + ") free:" + runtime.freeMemory() + " total=" + runtime.totalMemory() + " max=" + runtime.maxMemory());
handler.doGet(req, resp);
log("stop " + localId + " free:" + runtime.freeMemory() + " total=" + runtime.totalMemory() + " max=" + runtime.maxMemory());
}
} catch (IOException ioe) {
log("exception " + localId + " free:" + runtime.freeMemory() + " total=" + runtime.totalMemory() + " max=" + runtime.maxMemory());
log(ioe);
throw ioe;
} catch (ServletException se) {
log("exception " + localId + " free:" + runtime.freeMemory() + " total=" + runtime.totalMemory() + " max=" + runtime.maxMemory());
log(se);
throw se;
} catch (Throwable t) {
log("exception " + localId + " free:" + runtime.freeMemory() + " total=" + runtime.totalMemory() + " max=" + runtime.maxMemory());
log(t);
throw new ServletException(t);
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int localId = ++id;
Runtime runtime = Runtime.getRuntime();
try {
PostHandler handler = (PostHandler)findHandler(postHandlers, req.getPathInfo());
if (handler != null) {
log("start " + localId + ':' + req.getPathInfo() + ' ' + req.getRemoteHost() + " (" + req.getRemoteAddr() + ") free:" + runtime.freeMemory() + " total=" + runtime.totalMemory() + " max=" + runtime.maxMemory());
handler.doPost(req, resp);
log("stop " + localId + " free:" + runtime.freeMemory() + " total=" + runtime.totalMemory() + " max=" + runtime.maxMemory());
}
} catch (IOException ioe) {
log("exception " + localId + " free:" + runtime.freeMemory() + " total=" + runtime.totalMemory() + " max=" + runtime.maxMemory());
log(ioe);
throw ioe;
} catch (ServletException se) {
log("exception " + localId + " free:" + runtime.freeMemory() + " total=" + runtime.totalMemory() + " max=" + runtime.maxMemory());
log(se);
throw se;
} catch (Throwable t) {
log("exception " + localId + " free:" + runtime.freeMemory() + " total=" + runtime.totalMemory() + " max=" + runtime.maxMemory());
log(t);
throw new ServletException(t);
}
}
public void addStatisticHandler(String name, String parameter, List statisticHandlers) throws Exception {
String handlerGet = "handler.get.";
String handlerPost = "handler.post.";
String handlerStatistic = "zeta.handler.statistic."; int idx1 = name.indexOf(' ');
int idx2 = name.indexOf(' ', idx1+1);
if (idx1 <= 0) {
idx1 = name.length();
}
if (name.startsWith(handlerGet)) {
Object[] handler = { "/" + name.substring(handlerGet.length(), idx1), (idx1 == name.length())? "" : name.substring(idx2+1), getInstance(Class.forName(parameter)) };
if (parameter.startsWith(handlerStatistic) && idx1 > 0 && idx1+1 < idx2) {
++numberOfStatisticHandlers;
int posHandlerStatistic = Integer.parseInt(name.substring(idx1+1, idx2));
if (statisticHandlers == null) {
getHandlers.add(handler);
} else {
while (posHandlerStatistic >= statisticHandlers.size()) {
statisticHandlers.add(null);
}
statisticHandlers.set(posHandlerStatistic, handler);
}
} else {
getHandlers.add(handler);
}
} else if (name.startsWith(handlerPost)) {
Object[] handler = { "/" + name.substring(handlerPost.length(), idx1), (idx1 == name.length())? "" : name.substring(idx2+1), getInstance(Class.forName(parameter)) };
postHandlers.add(handler);
}
}
public void log(String msg) {
if (normalLogging) {
super.log(msg);
} else {
synchronized (stdLogFilename) {
if (new File(stdLogFilename).length() >= MAX_LOG_FILESIZE) {
StreamUtils.close(stdLog);
stdLog = null;
StreamUtils.close(errorLog);
errorLog = null;
rollOver(stdLogFilename, MAX_BACKUP_INDEX);
rollOver(errorLogFilename, MAX_BACKUP_INDEX);
initLogFile();
}
if (stdLog != null) {
try {
stdLog.write(logFormat.format(new Date()) + ' ' + msg + '\n');
} catch (Exception e) {
super.log(msg);
}
} else {
initLogFile();
if (stdLog != null) {
try {
stdLog.write(logFormat.format(new Date()) + ' ' + msg + '\n');
} catch (Exception e) {
super.log(msg);
}
} else {
super.log(msg);
}
}
}
}
}
public void log(Throwable t) {
if (!normalLogging) {
synchronized (stdLogFilename) {
if (new File(errorLogFilename).length() >= MAX_LOG_FILESIZE) {
StreamUtils.close(stdLog);
stdLog = null;
StreamUtils.close(errorLog);
errorLog = null;
rollOver(stdLogFilename, MAX_BACKUP_INDEX);
rollOver(errorLogFilename, MAX_BACKUP_INDEX);
initLogFile();
}
if (errorLog != null) {
try {
errorLog.write(logFormat.format(new Date())+'\n');
t.printStackTrace(new PrintWriter(errorLog));
errorLog.write("\n");
} catch (Exception e) {
}
} else {
initLogFile();
if (errorLog != null) {
try {
errorLog.write(logFormat.format(new Date())+'\n');
t.printStackTrace(new PrintWriter(errorLog));
errorLog.write("\n");
} catch (Exception e) {
}
}
}
}
}
}
private void initLogFile() {
Connection con = null;
Statement stmt = null;
try {
if (errorLogFilename == null || errorLogFilename.length() == 0) {
con = getConnection();
stmt = con.createStatement();
String pathLog = Parameter.getValue(stmt, "path_log", Parameter.GLOBAL_PARAMETER, null, 3600000);
if (pathLog != null && pathLog.length() > 0) {
stdLogFilename = pathLog + "servlet.log";
errorLogFilename = pathLog + "error_servlet.log";
}
}
if (errorLogFilename != null && errorLogFilename.length() > 0 && errorLog == null) {
errorLog = new FileWriter(errorLogFilename, true);
}
if (stdLogFilename != null && stdLogFilename.length() > 0 && stdLog == null) {
stdLog = new FileWriter(stdLogFilename, true);
}
} catch (IOException e) {
normalLogging = true;
} catch (Exception e) {
} finally {
DatabaseUtils.close(stmt);
DatabaseUtils.close(con);
}
}
private void rollOver(String filename, int maxBackupIndex) {
if (maxBackupIndex > 0) {
File file = new File(filename + '.' + maxBackupIndex);
if (file.exists()) {
file.delete();
}
for (int i = maxBackupIndex-1; i >= 1; --i) {
File f = new File(filename + '.' + i);
if (f.exists()) {
f.renameTo(file);
}
file = f;
}
new File(filename).renameTo(file);
}
}
private Object findHandler(List handlers, String name) {
if (name != null) {
Iterator iter = handlers.iterator();
while (iter.hasNext()) {
Object[] obj = (Object[])iter.next();
if (name.equals((String)obj[0])) {
return obj[2];
}
}
}
return null;
}
private Object getInstance(Class handlerClass) throws Exception, Error {
try {
Constructor c = handlerClass.getDeclaredConstructor(new Class[] { ZetaServlet.class });
return c.newInstance(new Object[] { this });
} catch(NoSuchMethodException e) {
return handlerClass.newInstance();
}
}
private List getHandlers;
private List postHandlers;
private int numberOfStatisticHandlers = 0;
private int serverId = 0;
private int id = 0;
private int poolsize = 0;
private ConnectionDriver connectionDriver = null;
private String stdLogFilename = "";
private Writer stdLog = null;
private String errorLogFilename = "";
private Writer errorLog = null;
private SimpleDateFormat logFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss", Locale.GERMANY);
private boolean normalLogging = true;
private final static long MAX_LOG_FILESIZE = 5*1024*1024;
private final static int MAX_BACKUP_INDEX = 3;
}