/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.venky.swf.routing;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import com.venky.core.log.ExtendedLevel;
import com.venky.core.log.SWFLogger;
import com.venky.core.log.TimerStatistics;
import com.venky.core.log.TimerStatistics.Timer;
import com.venky.core.util.PackageUtil;
import com.venky.extension.Registry;
import com.venky.swf.db._IDatabase;
import com.venky.swf.exceptions.MultiException;
import com.venky.swf.path._IPath;
import com.venky.swf.views._IView;
/**
*
* @author venky
*/
public class Router extends AbstractHandler {
protected Router() {
}
private static Router router = new Router();
public static Router instance(){
return router;
}
private ClassLoader loader = null;
public ClassLoader getLoader() {
synchronized (this) {
return loader;
}
}
private void callShutdownExtensions(){
Registry.instance().callExtensions("com.venky.swf.routing.Router.shutdown");
}
private void shutDown(){
callShutdownExtensions();
}
public void setLoader(ClassLoader loader) {
synchronized (this) {
if (this.loader != loader) {
shutDown();
clearExtensions();
if (this.loader != null){
disposeDatabase();
}
this.loader = loader;
if (loader != null){
try {
ExtendedLevel.TIMER.intValue();
InputStream is = this.loader.getResourceAsStream("config/logger.properties");
if (is != null){
LogManager.getLogManager().readConfiguration(is);
}else {
Config.instance().getLogger(Router.class.getName()).info("Logging not configured! using defaults");
}
} catch (Exception e1) {
Config.instance().getLogger(Router.class.getName()).info("config/logger.properties not configured! using defaults");
}
_IDatabase db = getDatabase(true);
loadExtensions();
db.loadFactorySettings();
db.close();
try {
getPathClass();
getExceptionViewClass();
} catch (Exception e) {
Config.instance().getLogger(Router.class.getName()).log(Level.SEVERE,e.getMessage(),e);
}
}
}
}
}
public void clearExtensions(){
Registry.instance().clearExtensions();
}
public void loadExtensions(){
for (String root : Config.instance().getExtensionPackageRoots()){
for (URL url:Config.instance().getResourceBaseUrls()){
for (String extnClassName : PackageUtil.getClasses(url, root.replace('.', '/'))){
try {
getClass(extnClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
}
}
private _IPath createPath(String target){
try {
Class<?> ipc = getPathClass();
_IPath path = (_IPath)(ipc.getConstructor(String.class).newInstance(target));
return path;
} catch (Exception e){
throw new RuntimeException(e);
}
}
public Class<?> getClass(String className) throws ClassNotFoundException{
return Class.forName(className,true,getLoader());
}
private Class<?> getPathClass() throws ClassNotFoundException{
return getClass("com.venky.swf.path.Path");
}
private Class<?> getExceptionViewClass() throws ClassNotFoundException {
return getClass("com.venky.swf.views.ExceptionView");
}
private Class<?> getRedirectorViewClass() throws ClassNotFoundException {
return getClass("com.venky.swf.views.RedirectorView");
}
private Class<?> getDatabaseClass() throws ClassNotFoundException{
return getClass("com.venky.swf.db.Database");
}
private _IView createRedirectorView(_IPath p,String url){
try {
Class<?> evc = getRedirectorViewClass();
_IView ev = (_IView) evc.getConstructor(_IPath.class).newInstance(p);
Method m = evc.getMethod("setRedirectUrl", String.class);
m.invoke(ev, url);
return ev;
}catch (Exception e){
throw new RuntimeException(e);
}
}
private _IView createExceptionView(_IPath p, Throwable th){
try {
Class<?> evc = getExceptionViewClass();
_IView ev = (_IView) evc.getConstructor(_IPath.class,Throwable.class).newInstance(p,th);
return ev;
}catch (Exception e){
throw new RuntimeException(e);
}
}
private _IDatabase getDatabase(){
return getDatabase(false);
}
@SuppressWarnings("unchecked")
private _IDatabase getDatabase(boolean migrate){
try {
//Database.getInstance()
Class<_IDatabase> c = (Class<_IDatabase>)getDatabaseClass();
_IDatabase idb = (_IDatabase)(c.getMethod("getInstance",boolean.class).invoke(c,migrate));
return idb;
}catch(Exception ex){
throw new RuntimeException(ex);
}
}
@SuppressWarnings("unchecked")
private void disposeDatabase(){
try {
//Database.getInstance()
Class<_IDatabase> c = (Class<_IDatabase>)getDatabaseClass();
c.getMethod("dispose").invoke(c);
c = null;
}catch(Exception ex){
throw new RuntimeException(ex);
}
}
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
if (target.equals("/favicon.ico")){
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
SWFLogger cat = Config.instance().getLogger(getClass().getName());
Timer timer = cat.startTimer("handleRequest",true);
try {
_IView view = null;
_IView ev = null ;
_IPath p = createPath(target);
p.setSession(request.getSession(false));
p.setRequest(request);
p.setResponse(response);
Logger logger = Config.instance().getLogger(getClass().getName());
_IDatabase db = null ;
try {
db = getDatabase();
view = p.invoke();
if (db != getDatabase()){
// If Class Loader is reset.
db = getDatabase();
}
if (view.isBeingRedirected()){
db.getCurrentTransaction().commit();
}
Timer viewWriteTimer = cat.startTimer(target+".view_write", Config.instance().isTimerAdditive());
try {
view.write();
}finally{
viewWriteTimer.stop();
}
db.getCurrentTransaction().commit();
}catch(Exception e){
try {
logger.log(Level.INFO, "Request failed", e);
db.getCurrentTransaction().rollback(e);
}catch (Exception ex){
logger.log(Level.INFO, "Rollback failed", ex);
}
if (p.isForwardedRequest()){
if (e instanceof RuntimeException){
throw (RuntimeException)e;
}else {
MultiException ex = new MultiException();
ex.add(e);
throw ex;
}
}
if (p.getSession() != null){
p.addErrorMessage(e.getMessage());
Config.instance().getLogger(Router.class.getName()).log(Level.INFO, "Request failed", e);
if (p.getTarget().equals(p.getBackTarget())){
ev = createRedirectorView(p, "/dashboard");
}else {
ev = createRedirectorView(p,p.getBackTarget());
}
}else {
ev = createExceptionView(p, e);
}
ev.write();
}finally {
if (db != null ){
db.close();
}
p.autoInvalidateSession();
}
}finally{
timer.stop();
TimerStatistics.dumpStatistics();
baseRequest.setHandled(true);
}
}
public void reset() {
setLoader(new SWFClassLoader(getClass().getClassLoader()));
}
}