001 package railo.runtime; 002 003 import java.io.IOException; 004 import java.io.InputStream; 005 import java.io.OutputStream; 006 007 import javax.servlet.ServletException; 008 import javax.servlet.http.HttpServletRequest; 009 import javax.servlet.http.HttpServletResponse; 010 011 import railo.commons.io.IOUtil; 012 import railo.commons.io.res.Resource; 013 import railo.commons.io.res.util.ResourceUtil; 014 import railo.commons.lang.CFTypes; 015 import railo.commons.lang.StringUtil; 016 import railo.runtime.config.ConfigImpl; 017 import railo.runtime.config.ConfigWebImpl; 018 import railo.runtime.converter.ConverterException; 019 import railo.runtime.converter.JSONConverter; 020 import railo.runtime.converter.ScriptConverter; 021 import railo.runtime.converter.WDDXConverter; 022 import railo.runtime.dump.DumpUtil; 023 import railo.runtime.exp.ApplicationException; 024 import railo.runtime.exp.ExpressionException; 025 import railo.runtime.exp.PageException; 026 import railo.runtime.gateway.GatewayEngineImpl; 027 import railo.runtime.interpreter.JSONExpressionInterpreter; 028 import railo.runtime.net.rpc.server.ComponentController; 029 import railo.runtime.net.rpc.server.RPCServer; 030 import railo.runtime.op.Caster; 031 import railo.runtime.op.Decision; 032 import railo.runtime.type.Array; 033 import railo.runtime.type.Collection.Key; 034 import railo.runtime.type.FunctionArgument; 035 import railo.runtime.type.KeyImpl; 036 import railo.runtime.type.List; 037 import railo.runtime.type.Scope; 038 import railo.runtime.type.Struct; 039 import railo.runtime.type.UDF; 040 import railo.runtime.type.UDFImpl; 041 import railo.runtime.type.util.ComponentUtil; 042 import railo.runtime.type.util.StructUtil; 043 044 /** 045 * A Page that can produce Components 046 */ 047 public abstract class ComponentPage extends PagePlus { 048 049 private static final long serialVersionUID = -3483642653131058030L; 050 051 public static final railo.runtime.type.Collection.Key METHOD = KeyImpl.intern("method"); 052 public static final railo.runtime.type.Collection.Key QUERY_FORMAT = KeyImpl.intern("queryFormat"); 053 //public static final railo.runtime.type.Collection.Key REMOTE_PERSISTENT = KeyImpl.intern("remotePersistent"); 054 public static final railo.runtime.type.Collection.Key REMOTE_PERSISTENT_ID = KeyImpl.intern("Id16hohohh"); 055 056 //public static final short REMOTE_PERSISTENT_REQUEST = 1; 057 //public static final short REMOTE_PERSISTENT_SESSION = 2; FUTURE 058 //public static final short REMOTE_PERSISTENT_APPLICATION = 4; FUTURE 059 //public static final short REMOTE_PERSISTENT_SERVER = 8; 060 061 062 private long lastCheck=-1; 063 064 065 public abstract ComponentImpl newInstance(PageContext pc,String callPath,boolean isRealPath) 066 throws railo.runtime.exp.PageException; 067 068 /** 069 * @see railo.runtime.Page#call(railo.runtime.PageContext) 070 */ 071 public void call(PageContext pc) throws PageException { 072 073 // remote persistent (only type server is supported) 074 String strRemotePersisId = Caster.toString(pc.urlFormScope().get(REMOTE_PERSISTENT_ID,null),null); 075 if(!StringUtil.isEmpty(strRemotePersisId,true)) { 076 strRemotePersisId=strRemotePersisId.trim(); 077 } 078 else strRemotePersisId=null; 079 080 HttpServletRequest req = pc.getHttpServletRequest(); 081 // client 082 String client = Caster.toString(req.getAttribute("client"),null); 083 // call type (invocation, store-only) 084 String callType = Caster.toString(req.getAttribute("call-type"),null); 085 boolean fromGateway="railo-gateway-1-0".equals(client); 086 ComponentPro component; 087 try { 088 pc.setSilent(); 089 // load the cfc 090 try { 091 if(fromGateway && strRemotePersisId!=null) { 092 ConfigWebImpl config=(ConfigWebImpl) pc.getConfig(); 093 GatewayEngineImpl engine = config.getGatewayEngine(); 094 component=(ComponentPro) engine.getPersistentRemoteCFC(strRemotePersisId); 095 096 if(component==null) { 097 component=newInstance(pc,getPageSource().getComponentName(),false); 098 if(!fromGateway)component=ComponentWrap.toComponentWrap(Component.ACCESS_REMOTE,component); 099 100 engine.setPersistentRemoteCFC(strRemotePersisId,component); 101 } 102 103 } 104 else { 105 component=newInstance(pc,getPageSource().getComponentName(),false); 106 if(!fromGateway)component=ComponentWrap.toComponentWrap(Component.ACCESS_REMOTE,component); 107 } 108 } 109 finally { 110 pc.unsetSilent(); 111 } 112 113 // Only get the Component, no invocation 114 if("store-only".equals(callType)) { 115 req.setAttribute("component", component); 116 return; 117 } 118 119 120 121 // METHOD INVOCATION 122 String qs=pc.getHttpServletRequest().getQueryString(); 123 if(pc.getBasePageSource()==this.getPageSource()) 124 pc.getDebugger().setOutput(false); 125 boolean isPost=pc. getHttpServletRequest().getMethod().equalsIgnoreCase("POST"); 126 Object method; 127 128 boolean suppressContent = ((ConfigImpl)pc.getConfig()).isSuppressContent(); 129 if(suppressContent)pc.clear(); 130 131 // POST 132 if(isPost) { 133 // Soap 134 if(isSoap(pc)) { 135 callWebservice(pc,component); 136 //close(pc); 137 return; 138 } 139 // WDDX 140 else if((method=pc.urlFormScope().get("method",null))!=null) { 141 callWDDX(pc,component,Caster.toString(method),suppressContent); 142 //close(pc); 143 return; 144 } 145 146 } 147 148 // GET 149 else { 150 // WSDL 151 if(qs!=null && qs.trim().equalsIgnoreCase("wsdl")) { 152 callWSDL(pc,component); 153 //close(pc); 154 return; 155 } 156 // WDDX 157 else if((method=pc.urlFormScope().get("method",null))!=null) { 158 callWDDX(pc,component,Caster.toString(method),suppressContent); 159 //close(pc); 160 return; 161 } 162 } 163 164 165 // Include MUST 166 Array path = pc.getTemplatePath(); 167 //if(path.size()>1 ) { 168 if(path.size()>1 && !(path.size()==3 && List.last(path.getE(2).toString(),"/\\",true).equalsIgnoreCase("application.cfc")) ) {// MUSTMUST bad impl -> check with and without application.cfc 169 170 ComponentWrap c = ComponentWrap.toComponentWrap(Component.ACCESS_PRIVATE,ComponentUtil.toComponentAccess(component)); 171 Key[] keys = c.keys(); 172 Object el; 173 Scope var = pc.variablesScope(); 174 for(int i=0;i<keys.length;i++) { 175 el=c.get(keys[i],null); 176 if(el instanceof UDF) 177 var.set(keys[i], el); 178 179 } 180 181 return; 182 } 183 184 185 // DUMP 186 //TODO component.setAccess(pc,Component.ACCESS_PUBLIC); 187 String cdf = pc.getConfig().getComponentDumpTemplate(); 188 189 if(cdf!=null && cdf.trim().length()>0) { 190 pc.variablesScope().set("component",component); 191 pc.doInclude(cdf); 192 } 193 else pc.write(pc.getConfig().getDefaultDumpWriter().toString(pc,component.toDumpData(pc,9999,DumpUtil.toDumpProperties() ),true)); 194 195 } 196 catch(Throwable t) { 197 throw Caster.toPageException(t);//Exception Handler.castAnd Stack(t, this, pc); 198 } 199 } 200 201 /*private void close(PageContext pc) { 202 //pc.close(); 203 }*/ 204 205 public static boolean isSoap(PageContext pc) { 206 HttpServletRequest req = pc.getHttpServletRequest(); 207 InputStream is=null; 208 try { 209 is=req.getInputStream(); 210 211 String input = IOUtil.toString(is,"iso-8859-1"); 212 return 213 StringUtil.indexOfIgnoreCase(input, "soap:Envelope")!=-1 || 214 StringUtil.indexOfIgnoreCase(input, "soapenv:Envelope")!=-1 || 215 StringUtil.indexOfIgnoreCase(input, "SOAP-ENV:Envelope")!=-1; 216 } 217 catch (IOException e) { 218 return false; 219 } 220 finally { 221 IOUtil.closeEL(is); 222 } 223 } 224 225 226 private void callWDDX(PageContext pc, Component component, String methodName, boolean suppressContent) throws IOException, ConverterException, PageException { 227 Struct url = StructUtil.duplicate(pc.urlFormScope(),true); 228 229 // define args 230 url.removeEL(KeyImpl.FIELD_NAMES); 231 url.removeEL(METHOD); 232 Object args=url.get(KeyImpl.ARGUMENT_COLLECTION,null); 233 Object returnFormat=url.get(KeyImpl.RETURN_FORMAT,null); 234 Object queryFormat=url.get(QUERY_FORMAT,null); 235 236 if(args==null){ 237 args=pc.getHttpServletRequest().getAttribute("argumentCollection"); 238 } 239 240 //content-type 241 Object o = component.get(KeyImpl.init(methodName),null); 242 Props props = getProps(pc, o, returnFormat); 243 HttpServletResponse rsp = pc.getHttpServletResponse(); 244 if(!props.output) { 245 switch(props.format){ 246 case UDF.RETURN_FORMAT_WDDX: 247 rsp.setContentType("text/xml; charset=UTF-8"); 248 rsp.setHeader("Return-Format", "wddx"); 249 break; 250 case UDF.RETURN_FORMAT_JSON: 251 rsp.setContentType("application/json"); 252 rsp.setHeader("Return-Format", "json"); 253 break; 254 case UDF.RETURN_FORMAT_PLAIN: 255 rsp.setContentType("text/plain; charset=UTF-8"); 256 rsp.setHeader("Return-Format", "plain"); 257 break; 258 case UDF.RETURN_FORMAT_SERIALIZE: 259 rsp.setContentType("text/plain; charset=UTF-8"); 260 rsp.setHeader("Return-Format", "serialize"); 261 break; 262 } 263 } 264 265 Object rtn=null; 266 try{ 267 if(suppressContent)pc.setSilent(); 268 269 270 if(args==null){ 271 url=translate(component,methodName,url); 272 rtn = component.callWithNamedValues(pc, methodName, url); 273 } 274 else if(args instanceof String){ 275 try { 276 args=new JSONExpressionInterpreter().interpret(pc, (String)args); 277 278 } catch (PageException e) {} 279 } 280 281 // call 282 if(args!=null) { 283 if(Decision.isCastableToStruct(args)){ 284 rtn = component.callWithNamedValues(pc, methodName, Caster.toStruct(args,false)); 285 } 286 else if(Decision.isCastableToArray(args)){ 287 rtn = component.call(pc, methodName, Caster.toNativeArray(args)); 288 } 289 else { 290 Object[] ac=new Object[1]; 291 ac[0]=args; 292 rtn = component.call(pc, methodName, ac); 293 } 294 } 295 } 296 finally { 297 if(suppressContent)pc.unsetSilent(); 298 } 299 // convert result 300 if(rtn!=null){ 301 if(pc.getHttpServletRequest().getHeader("AMF-Forward")!=null) { 302 pc.variablesScope().setEL("AMF-Forward", rtn); 303 //ThreadLocalWDDXResult.set(rtn); 304 } 305 else { 306 pc.forceWrite(convertResult(pc, props, queryFormat, rtn)); 307 } 308 } 309 //pc.setSilent(); 310 311 } 312 313 private static Props getProps(PageContext pc, Object o,Object returnFormat) throws PageException { 314 Props props = new Props(); 315 316 props.strType="any"; 317 props.secureJson=pc.getApplicationContext().getSecureJson(); 318 if(o instanceof UDF) { 319 UDF udf = ((UDF)o); 320 props.format=udf.getReturnFormat(); 321 props.type=udf.getReturnType(); 322 props.strType=udf.getReturnTypeAsString(); 323 props.output=udf.getOutput(); 324 if(udf.getSecureJson()!=null)props.secureJson=udf.getSecureJson().booleanValue(); 325 } 326 if(!StringUtil.isEmpty(returnFormat)){ 327 props.format=UDFImpl.toReturnFormat(Caster.toString(returnFormat)); 328 } 329 330 // return type XML ignore WDDX 331 if(props.type==CFTypes.TYPE_XML) { 332 if(UDF.RETURN_FORMAT_WDDX==props.format) 333 props.format=UDF.RETURN_FORMAT_PLAIN; 334 } 335 336 337 338 return props; 339 } 340 341 public static String convertResult(PageContext pc,Component component, String methodName,Object returnFormat,Object queryFormat,Object rtn) throws ConverterException, PageException { 342 Object o = component.get(methodName,null); 343 Props p = getProps(pc, o, returnFormat); 344 return convertResult(pc, p, queryFormat, rtn); 345 } 346 347 private static String convertResult(PageContext pc,Props props,Object queryFormat,Object rtn) throws ConverterException, PageException { 348 349 /*Object o = component.get(methodName,null); 350 int format=UDF.RETURN_FORMAT_WDDX; 351 int type=CFTypes.TYPE_ANY; 352 String strType="any"; 353 boolean secureJson=pc.getApplicationContext().getSecureJson(); 354 if(o instanceof UDF) { 355 UDF udf = ((UDF)o); 356 format=udf.getReturnFormat(); 357 type=udf.getReturnType(); 358 strType=udf.getReturnTypeAsString(); 359 if(udf.getSecureJson()!=null)secureJson=udf.getSecureJson().booleanValue(); 360 } 361 if(!StringUtil.isEmpty(returnFormat)){ 362 format=UDFImpl.toReturnFormat(Caster.toString(returnFormat)); 363 }*/ 364 365 366 // return type XML ignore WDDX 367 if(props.type==CFTypes.TYPE_XML) { 368 //if(UDF.RETURN_FORMAT_WDDX==format) format=UDF.RETURN_FORMAT_PLAIN; 369 rtn=Caster.toString(Caster.toXML(rtn)); 370 } 371 // function does no real cast, only check it 372 else rtn=Caster.castTo(pc, (short)props.type, props.strType, rtn); 373 374 // WDDX 375 if(UDF.RETURN_FORMAT_WDDX==props.format) { 376 WDDXConverter converter = new WDDXConverter(pc.getTimeZone(),false,false); 377 converter.setTimeZone(pc.getTimeZone()); 378 return converter.serialize(rtn); 379 } 380 // JSON 381 else if(UDF.RETURN_FORMAT_JSON==props.format) { 382 boolean byColumn = false; 383 if(queryFormat instanceof String){ 384 String strQF=((String) queryFormat).trim(); 385 if(strQF.equalsIgnoreCase("row")); 386 else if(strQF.equalsIgnoreCase("column"))byColumn=true; 387 else throw new ApplicationException("invalid queryformat definition ["+strQF+"], valid formats are [row,column]"); 388 } 389 JSONConverter converter = new JSONConverter(false); 390 String prefix=""; 391 if(props.secureJson) { 392 prefix=pc.getApplicationContext().getSecureJsonPrefix(); 393 if(prefix==null)prefix=""; 394 } 395 return prefix+converter.serialize(pc,rtn,byColumn); 396 } 397 // Serialize 398 else if(UDF.RETURN_FORMAT_SERIALIZE==props.format) { 399 ScriptConverter converter = new ScriptConverter(false); 400 return converter.serialize(rtn); 401 } 402 // Plain 403 else if(UDF.RETURN_FORMAT_PLAIN==props.format) { 404 return Caster.toString(rtn); 405 } 406 return null; 407 } 408 409 public static Struct translate(Component c, String strMethod, Struct params) { 410 Key[] keys = params.keys(); 411 FunctionArgument[] args=null; 412 int index=-1; 413 Object value; 414 for(int i=0;i<keys.length;i++){ 415 index=Caster.toIntValue(keys[i].getString(),0); 416 if(index>0) { 417 if(args==null)args=_getArgs(c,strMethod); 418 if(args!=null && index<=args.length) { 419 value=params.removeEL(keys[i]); 420 if(value!=null)params.setEL(args[index-1].getName(), value); 421 } 422 } 423 424 } 425 return params; 426 } 427 428 private static FunctionArgument[] _getArgs(Component c, String strMethod) { 429 Object o=c.get(strMethod,null); 430 if(o instanceof UDF) return ((UDF) o).getFunctionArguments(); 431 return null; 432 } 433 434 private void callWSDL(PageContext pc, ComponentPro component) throws ServletException, IOException, ExpressionException { 435 // take wsdl file defined by user 436 String wsdl = component.getWSDLFile(); 437 if(!StringUtil.isEmpty(wsdl)) { 438 439 OutputStream os=null;// FUTURE add to interface 440 Resource input = ResourceUtil.toResourceExisting(pc, wsdl); 441 try { 442 os=((PageContextImpl)pc).getResponseStream(); 443 pc.getResponse().setContentType("text/xml; charset=utf-8"); 444 IOUtil.copy(input, os, false); 445 446 } 447 finally { 448 IOUtil.flushEL(os); 449 IOUtil.closeEL(os); 450 ((PageContextImpl)pc).getRootOut().setClosed(true); 451 } 452 } 453 // create a wsdl file 454 else { 455 RPCServer.getInstance(pc.getId(),pc.getServletContext()) 456 .doGet(pc.getHttpServletRequest(), pc. getHttpServletResponse(), component); 457 } 458 } 459 460 private void callWebservice(PageContext pc, Component component) throws IOException, ServletException { 461 ComponentController.set(pc, component); 462 try { 463 RPCServer.getInstance(pc.getId(),pc.getServletContext()) 464 .doPost(pc.getHttpServletRequest(), pc. getHttpServletResponse(), component); 465 } 466 finally { 467 ComponentController.release(); 468 } 469 } 470 471 472 public abstract void initComponent(PageContext pc,ComponentImpl c) throws PageException; 473 474 public void ckecked() { 475 lastCheck=System.currentTimeMillis(); 476 } 477 478 public long lastCheck() { 479 return lastCheck; 480 } 481 482 } 483 class Props { 484 485 public String strType="any"; 486 public boolean secureJson; 487 public int type=CFTypes.TYPE_ANY; 488 public int format=UDF.RETURN_FORMAT_WDDX; 489 public boolean output=true; 490 491 }