package railo.runtime.listener;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.http.Cookie;
import railo.commons.io.DevNullOutputStream;
import railo.commons.io.res.Resource;
import railo.commons.io.res.util.ResourceUtil;
import railo.commons.lang.StringUtil;
import railo.commons.lang.mimetype.MimeType;
import railo.commons.lang.types.RefBoolean;
import railo.commons.lang.types.RefBooleanImpl;
import railo.runtime.CFMLFactory;
import railo.runtime.Component;
import railo.runtime.ComponentPage;
import railo.runtime.PageContext;
import railo.runtime.PageContextImpl;
import railo.runtime.PageSource;
import railo.runtime.component.ComponentLoader;
import railo.runtime.component.Member;
import railo.runtime.config.Constants;
import railo.runtime.engine.ThreadLocalPageContext;
import railo.runtime.exp.Abort;
import railo.runtime.exp.MissingIncludeException;
import railo.runtime.exp.PageException;
import railo.runtime.exp.PostContentAbort;
import railo.runtime.interpreter.JSONExpressionInterpreter;
import railo.runtime.net.http.HttpServletRequestDummy;
import railo.runtime.net.http.HttpServletResponseDummy;
import railo.runtime.net.http.ReqRspUtil;
import railo.runtime.op.Caster;
import railo.runtime.op.Decision;
import railo.runtime.op.Duplicator;
import railo.runtime.orm.ORMUtil;
import railo.runtime.type.Array;
import railo.runtime.type.ArrayImpl;
import railo.runtime.type.Collection;
import railo.runtime.type.Collection.Key;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.Struct;
import railo.runtime.type.cfc.ComponentAccess;
import railo.runtime.type.util.ArrayUtil;
import railo.runtime.type.util.KeyConstants;
import railo.runtime.type.util.UDFUtil;
public class ModernAppListener extends AppListenerSupport {
private static final Collection.Key ON_REQUEST_START = KeyImpl.intern("onRequestStart");
private static final Collection.Key ON_CFCREQUEST = KeyImpl.intern("onCFCRequest");
private static final Collection.Key ON_REQUEST = KeyImpl.intern("onRequest");
private static final Collection.Key ON_REQUEST_END = KeyImpl.intern("onRequestEnd");
private static final Collection.Key ON_ABORT = KeyImpl.intern("onAbort");
private static final Collection.Key ON_APPLICATION_START = KeyImpl.intern("onApplicationStart");
private static final Collection.Key ON_APPLICATION_END = KeyImpl.intern("onApplicationEnd");
private static final Collection.Key ON_SESSION_START = KeyImpl.intern("onSessionStart");
private static final Collection.Key ON_SESSION_END = KeyImpl.intern("onSessionEnd");
private static final Collection.Key ON_DEBUG = KeyImpl.intern("onDebug");
private static final Collection.Key ON_ERROR = KeyImpl.intern("onError");
private static final Collection.Key ON_MISSING_TEMPLATE = KeyImpl.intern("onMissingTemplate");
//private ComponentImpl app;
private Map<String,ComponentAccess> apps=new HashMap<String,ComponentAccess>();
protected int mode=MODE_CURRENT2ROOT;
@Override
public void onRequest(PageContext pc, PageSource requestedPage, RequestListener rl) throws PageException {
// on requestStart
PageSource appPS=//pc.isCFCRequest()?null:
AppListenerUtil.getApplicationPageSource(pc,requestedPage,Constants.APP_CFC,mode);
_onRequest(pc, requestedPage, appPS,rl);
}
protected void _onRequest(PageContext pc, PageSource requestedPage,PageSource appPS, RequestListener rl) throws PageException {
PageContextImpl pci = (PageContextImpl)pc;
if(appPS!=null) {
String callPath=appPS.getComponentName();
ComponentAccess app = ComponentLoader.loadComponent(pci,null,appPS, callPath, false,false);
// init
initApplicationContext(pci,app);
apps.put(pc.getApplicationContext().getName(), app);
if(!pci.initApplicationContext(this)) return;
if(rl!=null) {
requestedPage=rl.execute(pc, requestedPage);
if(requestedPage==null) return;
}
String targetPage=requestedPage.getFullRealpath();
RefBoolean goon=new RefBooleanImpl(true);
// onRequestStart
if(app.contains(pc,ON_REQUEST_START)) {
try {
Object rtn=call(app,pci, ON_REQUEST_START, new Object[]{targetPage},false);
if(!Caster.toBooleanValue(rtn,true))
return;
}
catch(PageException pe){
pe=handlePageException(pci,app,pe,requestedPage,targetPage,goon);
if(pe!=null) throw pe;
}
}
// onRequest
if(goon.toBooleanValue()) {
boolean isCFC=ResourceUtil.getExtension(targetPage,"").equalsIgnoreCase(pc.getConfig().getCFCExtension());
Object method;
if(isCFC && app.contains(pc,ON_CFCREQUEST) && (method=pc.urlFormScope().get(KeyConstants._method,null))!=null) {
Struct url = (Struct)Duplicator.duplicate(pc.urlFormScope(),true);
url.removeEL(KeyConstants._fieldnames);
url.removeEL(KeyConstants._method);
Object args=url.get(KeyConstants._argumentCollection,null);
// url returnFormat
Object oReturnFormat=url.removeEL(KeyConstants._returnFormat);
int urlReturnFormat=-1;
if(oReturnFormat!=null) urlReturnFormat=UDFUtil.toReturnFormat(Caster.toString(oReturnFormat,null),-1);
// request header accept
List<MimeType> accept = ReqRspUtil.getAccept(pc);
int headerReturnFormat = MimeType.toFormat(accept, -1,-1);
Object queryFormat=url.removeEL(KeyConstants._queryFormat);
if(args==null){
args=pc.getHttpServletRequest().getAttribute("argumentCollection");
}
if(args instanceof String){
args=new JSONExpressionInterpreter().interpret(pc, (String)args);
}
if(args!=null) {
if(Decision.isCastableToStruct(args)){
Struct sct = Caster.toStruct(args,false);
//Key[] keys = url.keys();
Iterator<Entry<Key, Object>> it = url.entryIterator();
Entry<Key, Object> e;
while(it.hasNext()){
e = it.next();
sct.setEL(e.getKey(),e.getValue());
}
args=sct;
}
else if(Decision.isCastableToArray(args)){
args = Caster.toArray(args);
}
else {
Array arr = new ArrayImpl();
arr.appendEL(args);
args=arr;
}
}
else
args=url;
Object rtn = call(app,pci, ON_CFCREQUEST, new Object[]{requestedPage.getComponentName(),method,args},true);
if(rtn!=null){
if(pc.getHttpServletRequest().getHeader("AMF-Forward")!=null) {
pc.variablesScope().setEL("AMF-Forward", rtn);
//ThreadLocalWDDXResult.set(rtn);
}
else {
try {
ComponentPage.writeToResponseStream(pc,app,method.toString(),urlReturnFormat,headerReturnFormat,queryFormat,rtn);
} catch (Exception e) {
throw Caster.toPageException(e);
}
}
}
}
//else if(!isCFC && app.contains(pc,ON_REQUEST)) {}
else {
// TODO impl die nicht so generisch ist
try{
if(!isCFC && app.contains(pc,ON_REQUEST))
call(app,pci, ON_REQUEST, new Object[]{targetPage},false);
else
pci.doInclude(requestedPage);
}
catch(PageException pe){
pe=handlePageException(pci,app,pe,requestedPage,targetPage,goon);
if(pe!=null) throw pe;
}
}
}
// onRequestEnd
if(goon.toBooleanValue() && app.contains(pc,ON_REQUEST_END)) {
try {
call(app,pci, ON_REQUEST_END, new Object[]{targetPage},false);
}
catch(PageException pe){
pe=handlePageException(pci,app,pe,requestedPage,targetPage,goon);
if(pe!=null) throw pe;
}
}
}
else {
apps.put(pc.getApplicationContext().getName(), null);
pc.doInclude(requestedPage);
}
}
private PageException handlePageException(PageContextImpl pci, ComponentAccess app, PageException pe, PageSource requestedPage, String targetPage, RefBoolean goon) throws PageException {
PageException _pe=pe;
if(pe instanceof ModernAppListenerException) {
_pe=((ModernAppListenerException) pe).getPageException();
}
if(!Abort.isSilentAbort(_pe)) {
if(_pe instanceof MissingIncludeException){
if(((MissingIncludeException) _pe).getPageSource().equals(requestedPage)){
if(app.contains(pci,ON_MISSING_TEMPLATE)) {
goon.setValue(false);
if(!Caster.toBooleanValue(call(app,pci, ON_MISSING_TEMPLATE, new Object[]{targetPage},true),true))
return pe;
}
else return pe;
}
else return pe;
}
else return pe;
}
else {
goon.setValue(false);
if(app.contains(pci,ON_ABORT)) {
call(app,pci, ON_ABORT, new Object[]{targetPage},true);
}
}
return null;
}
@Override
public boolean onApplicationStart(PageContext pc) throws PageException {
ComponentAccess app = apps.get(pc.getApplicationContext().getName());
if(app!=null && app.contains(pc,ON_APPLICATION_START)) {
Object rtn = call(app,pc, ON_APPLICATION_START, ArrayUtil.OBJECT_EMPTY,true);
return Caster.toBooleanValue(rtn,true);
}
return true;
}
@Override
public void onApplicationEnd(CFMLFactory factory, String applicationName) throws PageException {
ComponentAccess app = apps.get(applicationName);
if(app==null || !app.containsKey(ON_APPLICATION_END)) return;
PageContextImpl pc=(PageContextImpl) ThreadLocalPageContext.get();
boolean createPc=pc==null;
try {
if(createPc)pc = createPageContext(factory,app,applicationName,null,ON_APPLICATION_END);
call(app,pc, ON_APPLICATION_END, new Object[]{pc.applicationScope()},true);
}
finally {
if(createPc && pc!=null){
factory.releasePageContext(pc);
}
}
}
@Override
public void onSessionStart(PageContext pc) throws PageException {
ComponentAccess app = apps.get(pc.getApplicationContext().getName());
if(hasOnSessionStart(pc,app)) {
call(app,pc, ON_SESSION_START, ArrayUtil.OBJECT_EMPTY,true);
}
}
@Override
public void onSessionEnd(CFMLFactory factory, String applicationName, String cfid) throws PageException {
ComponentAccess app = apps.get(applicationName);
if(app==null || !app.containsKey(ON_SESSION_END)) return;
PageContextImpl pc=null;
try {
pc = createPageContext(factory,app,applicationName,cfid,ON_SESSION_END);
call(app,pc, ON_SESSION_END, new Object[]{pc.sessionScope(false),pc.applicationScope()},true);
}
finally {
if(pc!=null){
factory.releasePageContext(pc);
}
}
}
private PageContextImpl createPageContext(CFMLFactory factory, ComponentAccess app, String applicationName, String cfid,Collection.Key methodName) throws PageException {
Resource root = factory.getConfig().getRootDirectory();
String path = app.getPageSource().getFullRealpath();
// Request
HttpServletRequestDummy req = new HttpServletRequestDummy(root,"localhost",path,"",null,null,null,null,null);
if(!StringUtil.isEmpty(cfid))req.setCookies(new Cookie[]{new Cookie("cfid",cfid),new Cookie("cftoken","0")});
// Response
OutputStream os=DevNullOutputStream.DEV_NULL_OUTPUT_STREAM;
try {
Resource out = factory.getConfig().getConfigDir().getRealResource("output/"+methodName.getString()+".out");
out.getParentResource().mkdirs();
os = out.getOutputStream(false);
}
catch (IOException e) {
e.printStackTrace();
// TODO was passiert hier
}
HttpServletResponseDummy rsp = new HttpServletResponseDummy(os);
// PageContext
PageContextImpl pc = (PageContextImpl) factory.getRailoPageContext(factory.getServlet(), req, rsp, null, false, -1, false);
// ApplicationContext
ClassicApplicationContext ap = new ClassicApplicationContext(factory.getConfig(),applicationName,false,app==null?null:ResourceUtil.getResource(pc,app.getPageSource(),null));
initApplicationContext(pc, app);
ap.setName(applicationName);
ap.setSetSessionManagement(true);
//if(!ap.hasName())ap.setName("Controler")
// Base
pc.setBase(app.getPageSource());
return pc;
}
@Override
public void onDebug(PageContext pc) throws PageException {
if(((PageContextImpl)pc).isGatewayContext() || !pc.getConfig().debug()) return;
ComponentAccess app = apps.get(pc.getApplicationContext().getName());
if(app!=null && app.contains(pc,ON_DEBUG)) {
call(app,pc, ON_DEBUG, new Object[]{pc.getDebugger().getDebuggingData(pc)},true);
return;
}
try {
pc.getDebugger().writeOut(pc);
}
catch (IOException e) {
throw Caster.toPageException(e);
}
}
@Override
public void onError(PageContext pc, PageException pe) {
ComponentAccess app = apps.get(pc.getApplicationContext().getName());
if(app!=null && app.containsKey(ON_ERROR) && !Abort.isSilentAbort(pe)) {
try {
String eventName="";
if(pe instanceof ModernAppListenerException) eventName= ((ModernAppListenerException)pe).getEventName();
if(eventName==null)eventName="";
call(app,pc, ON_ERROR, new Object[]{pe.getCatchBlock(pc),eventName},true);
return;
}
catch(PageException _pe) {
pe=_pe;
}
}
pc.handlePageException(pe);
}
private Object call(Component app, PageContext pc, Collection.Key eventName, Object[] args, boolean catchAbort) throws PageException {
try {
return app.call(pc, eventName, args);
}
catch (PageException pe) {
if(Abort.isSilentAbort(pe)) {
if(catchAbort)
return ( pe instanceof PostContentAbort) ? Boolean.TRUE : Boolean.FALSE;
throw pe;
}
throw new ModernAppListenerException(pe,eventName.getString());
}
}
private void initApplicationContext(PageContextImpl pc, ComponentAccess app) throws PageException {
// use existing app context
RefBoolean throwsErrorWhileInit=new RefBooleanImpl(false);
ModernApplicationContext appContext = new ModernApplicationContext(pc,app,throwsErrorWhileInit);
pc.setApplicationContext(appContext);
if(appContext.isORMEnabled()) {
boolean hasError=throwsErrorWhileInit.toBooleanValue();
if(hasError)pc.addPageSource(app.getPageSource(), true);
try{
ORMUtil.resetEngine(pc,false);
}
finally {
if(hasError)pc.removeLastPageSource(true);
}
}
}
private static Object get(ComponentAccess app, Key name,String defaultValue) {
Member mem = app.getMember(Component.ACCESS_PRIVATE, name, true, false);
if(mem==null) return defaultValue;
return mem.getValue();
}
@Override
public void setMode(int mode) {
this.mode=mode;
}
@Override
public int getMode() {
return mode;
}
@Override
public String getType() {
return "modern";
}
@Override
public boolean hasOnSessionStart(PageContext pc) {
return hasOnSessionStart(pc, apps.get(pc.getApplicationContext().getName()));
}
private boolean hasOnSessionStart(PageContext pc,ComponentAccess app) {
return app!=null && app.contains(pc,ON_SESSION_START);
}
}