001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime; 020 021import java.io.ByteArrayInputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.io.OutputStream; 025import java.nio.charset.Charset; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Map.Entry; 029 030import javax.servlet.ServletException; 031import javax.servlet.http.HttpServletRequest; 032import javax.servlet.http.HttpServletResponse; 033 034import lucee.commons.io.CharsetUtil; 035import lucee.commons.io.IOUtil; 036import lucee.commons.io.res.Resource; 037import lucee.commons.io.res.util.ResourceUtil; 038import lucee.commons.lang.CFTypes; 039import lucee.commons.lang.ExceptionUtil; 040import lucee.commons.lang.StringUtil; 041import lucee.commons.lang.mimetype.MimeType; 042import lucee.runtime.config.ConfigWebImpl; 043import lucee.runtime.converter.BinaryConverter; 044import lucee.runtime.converter.ConverterException; 045import lucee.runtime.converter.JSONConverter; 046import lucee.runtime.converter.JavaConverter; 047import lucee.runtime.converter.ScriptConverter; 048import lucee.runtime.converter.WDDXConverter; 049import lucee.runtime.converter.XMLConverter; 050import lucee.runtime.converter.bin.ImageConverter; 051import lucee.runtime.dump.DumpUtil; 052import lucee.runtime.dump.DumpWriter; 053import lucee.runtime.exp.ApplicationException; 054import lucee.runtime.exp.ExpressionException; 055import lucee.runtime.exp.PageException; 056import lucee.runtime.exp.PageExceptionImpl; 057import lucee.runtime.gateway.GatewayEngineImpl; 058import lucee.runtime.interpreter.CFMLExpressionInterpreter; 059import lucee.runtime.interpreter.JSONExpressionInterpreter; 060import lucee.runtime.net.http.ReqRspUtil; 061import lucee.runtime.net.rpc.server.ComponentController; 062import lucee.runtime.net.rpc.server.RPCServer; 063import lucee.runtime.op.Caster; 064import lucee.runtime.op.Constants; 065import lucee.runtime.op.Decision; 066import lucee.runtime.rest.RestUtil; 067import lucee.runtime.rest.Result; 068import lucee.runtime.rest.path.Path; 069import lucee.runtime.type.Array; 070import lucee.runtime.type.ArrayImpl; 071import lucee.runtime.type.Collection; 072import lucee.runtime.type.Collection.Key; 073import lucee.runtime.type.FunctionArgument; 074import lucee.runtime.type.KeyImpl; 075import lucee.runtime.type.Struct; 076import lucee.runtime.type.StructImpl; 077import lucee.runtime.type.UDF; 078import lucee.runtime.type.UDFPlus; 079import lucee.runtime.type.scope.Scope; 080import lucee.runtime.type.util.ArrayUtil; 081import lucee.runtime.type.util.CollectionUtil; 082import lucee.runtime.type.util.KeyConstants; 083import lucee.runtime.type.util.ListUtil; 084import lucee.runtime.type.util.StructUtil; 085import lucee.runtime.type.util.UDFUtil; 086 087/** 088 * A Page that can produce Components 089 */ 090public abstract class ComponentPage extends PagePlus { 091 092 public static final Collection.Key ACCEPT_ARG_COLL_FORMATS = KeyImpl.getInstance("acceptedArgumentCollectionFormats"); 093 094 095 private static final long serialVersionUID = -3483642653131058030L; 096 097 public static final lucee.runtime.type.Collection.Key REMOTE_PERSISTENT_ID = KeyImpl.intern("Id16hohohh"); 098 099 private long lastCheck=-1; 100 101 102 public abstract ComponentImpl newInstance(PageContext pc,String callPath,boolean isRelPath) 103 throws lucee.runtime.exp.PageException; 104 105 @Override 106 public void call(PageContext pc) throws PageException { 107 // remote persistent (only type server is supported) 108 String strRemotePersisId = Caster.toString(getURLorForm(pc,REMOTE_PERSISTENT_ID,null),null);//Caster.toString(pc.urlFormScope().get(REMOTE_PERSISTENT_ID,null),null); 109 110 if(!StringUtil.isEmpty(strRemotePersisId,true)) { 111 strRemotePersisId=strRemotePersisId.trim(); 112 } 113 else strRemotePersisId=null; 114 115 HttpServletRequest req = pc.getHttpServletRequest(); 116 // client 117 String client = Caster.toString(req.getAttribute("client"),null); 118 // call type (invocation, store-only) 119 String callType = Caster.toString(req.getAttribute("call-type"),null); 120 boolean fromGateway="lucee-gateway-1-0".equals(client); 121 boolean fromRest="lucee-rest-1-0".equals(client); 122 Component component; 123 try { 124 pc.setSilent(); 125 // load the cfc 126 try { 127 if(fromGateway && strRemotePersisId!=null) { 128 ConfigWebImpl config=(ConfigWebImpl) pc.getConfig(); 129 GatewayEngineImpl engine = config.getGatewayEngine(); 130 component=engine.getPersistentRemoteCFC(strRemotePersisId); 131 132 if(component==null) { 133 component=newInstance(pc,getPageSource().getComponentName(),false); 134 if(!fromGateway)component=ComponentSpecificAccess.toComponentSpecificAccess(Component.ACCESS_REMOTE,component); 135 136 engine.setPersistentRemoteCFC(strRemotePersisId,component); 137 } 138 139 } 140 else { 141 component=newInstance(pc,getPageSource().getComponentName(),false); 142 if(!fromGateway)component=ComponentSpecificAccess.toComponentSpecificAccess(Component.ACCESS_REMOTE,component); 143 } 144 } 145 finally { 146 pc.unsetSilent(); 147 } 148 149 // Only get the Component, no invocation 150 if("store-only".equals(callType)) { 151 req.setAttribute("component", component); 152 return; 153 } 154 155 156 157 // METHOD INVOCATION 158 String qs=ReqRspUtil.getQueryString(pc.getHttpServletRequest()); 159 if(pc.getBasePageSource()==this.getPageSource() && pc.getConfig().debug()) 160 pc.getDebugger().setOutput(false); 161 boolean isPost=pc.getHttpServletRequest().getMethod().equalsIgnoreCase("POST"); 162 163 boolean suppressContent = ((PageContextImpl)pc).getSuppressContent(); 164 if(suppressContent)pc.clear(); 165 Object method; 166 167 if(fromRest){ 168 169 callRest(pc,component,Caster.toString(req.getAttribute("rest-path"),""),(Result)req.getAttribute("rest-result"),suppressContent); 170 return; 171 } 172 173 174 175 // POST 176 if(isPost) { 177 // Soap 178 if(isSoap(pc)) { 179 callWebservice(pc,component); 180 //close(pc); 181 return; 182 } 183 // WDDX 184 else if((method=getURLorForm(pc, KeyConstants._method, null))!=null) { 185 callWDDX(pc,component,KeyImpl.toKey(method),suppressContent); 186 //close(pc); 187 return; 188 } 189 190 } 191 192 // GET 193 else { 194 // WSDL 195 if(qs!=null && (qs.trim().equalsIgnoreCase("wsdl") || qs.trim().startsWith("wsdl&"))) { 196 callWSDL(pc,component); 197 //close(pc); 198 return; 199 } 200 // WDDX 201 else if((method=getURLorForm(pc, KeyConstants._method, null))!=null) { 202 callWDDX(pc,component,KeyImpl.toKey(method),suppressContent); 203 //close(pc); 204 return; 205 } 206 207 if(qs!=null) { 208 int rf = UDFUtil.toReturnFormat(qs.trim(),-1); 209 if(rf!=-1) callCFCMetaData(pc,component,rf); 210 //close(pc); 211 return; 212 } 213 } 214 215 216 // Include MUST 217 Array path = pc.getTemplatePath(); 218 //if(path.size()>1 ) { 219 if(path.size()>1 && !(path.size()==3 && ListUtil.last(path.getE(2).toString(),"/\\",true).equalsIgnoreCase(lucee.runtime.config.Constants.APP_CFC)) ) {// MUSTMUST bad impl -> check with and without application.cfc 220 221 ComponentSpecificAccess c = ComponentSpecificAccess.toComponentSpecificAccess(Component.ACCESS_PRIVATE,component); 222 Key[] keys = c.keys(); 223 Object el; 224 Scope var = pc.variablesScope(); 225 for(int i=0;i<keys.length;i++) { 226 el=c.get(keys[i],null); 227 if(el instanceof UDF) 228 var.set(keys[i], el); 229 230 } 231 232 return; 233 } 234 235 236 // DUMP 237 //TODO component.setAccess(pc,Component.ACCESS_PUBLIC); 238 String cdf = pc.getConfig().getComponentDumpTemplate(); 239 240 if(cdf!=null && cdf.trim().length()>0) { 241 pc.variablesScope().set(KeyConstants._component,component); 242 pc.doInclude(cdf); 243 } 244 else pc.write(pc.getConfig().getDefaultDumpWriter(DumpWriter.DEFAULT_RICH).toString(pc,component.toDumpData(pc,9999,DumpUtil.toDumpProperties() ),true)); 245 246 } 247 catch(Throwable t) { 248 throw Caster.toPageException(t);//Exception Handler.castAnd Stack(t, this, pc); 249 } 250 } 251 252 private Object getURLorForm(PageContext pc, Key key, Object defaultValue) { 253 Object res = pc.formScope().get(key,null); 254 if(res!=null) return res; 255 return pc.urlScope().get(key,defaultValue); 256 } 257 258 private void callRest(PageContext pc, Component component, String path, Result result, boolean suppressContent) throws IOException, ConverterException { 259 String method = pc.getHttpServletRequest().getMethod(); 260 String[] subPath = result.getPath(); 261 Struct cMeta; 262 try { 263 cMeta = component.getMetaData(pc); 264 } catch (PageException pe) { 265 throw ExceptionUtil.toIOException(pe); 266 } 267 268 269 // Consumes 270 MimeType[] cConsumes=null; 271 String strMimeType = Caster.toString(cMeta.get(KeyConstants._consumes,null),null); 272 if(!StringUtil.isEmpty(strMimeType,true)){ 273 cConsumes = MimeType.getInstances(strMimeType,','); 274 } 275 276 // Produces 277 MimeType[] cProduces=null; 278 strMimeType = Caster.toString(cMeta.get(KeyConstants._produces,null),null); 279 if(!StringUtil.isEmpty(strMimeType,true)){ 280 cProduces = MimeType.getInstances(strMimeType,','); 281 } 282 283 284 285 Iterator<Entry<Key, Object>> it = component.entryIterator(); 286 Entry<Key, Object> e; 287 Object value; 288 UDF udf; 289 Struct meta; 290 int status=404; 291 MimeType bestP,bestC; 292 while(it.hasNext()){ 293 e = it.next(); 294 value=e.getValue(); 295 if(value instanceof UDF){ 296 udf=(UDF)value; 297 try { 298 meta = udf.getMetaData(pc); 299 300 // check if http method match 301 String httpMethod = Caster.toString(meta.get(KeyConstants._httpmethod,null),null); 302 if(StringUtil.isEmpty(httpMethod) || !httpMethod.equalsIgnoreCase(method)) continue; 303 304 305 // get consumes mimetype 306 MimeType[] consumes; 307 strMimeType = Caster.toString(meta.get(KeyConstants._consumes,null),null); 308 if(!StringUtil.isEmpty(strMimeType,true)){ 309 consumes = MimeType.getInstances(strMimeType,','); 310 } 311 else 312 consumes=cConsumes; 313 314 315 // get produces mimetype 316 MimeType[] produces; 317 strMimeType = Caster.toString(meta.get(KeyConstants._produces,null),null); 318 if(!StringUtil.isEmpty(strMimeType,true)){ 319 produces = MimeType.getInstances(strMimeType,','); 320 } 321 else 322 produces=cProduces; 323 324 325 326 327 String restPath = Caster.toString(meta.get(KeyConstants._restPath,null),null); 328 329 // no rest path 330 if(StringUtil.isEmpty(restPath)){ 331 if(ArrayUtil.isEmpty(subPath)) { 332 bestC = best(consumes,result.getContentType()); 333 bestP = best(produces,result.getAccept()); 334 if(bestC==null) status=405; 335 else if(bestP==null) status=406; 336 else { 337 status=200; 338 _callRest(pc, component, udf, path, result.getVariables(),result,bestP,produces, suppressContent,e.getKey()); 339 break; 340 } 341 } 342 } 343 else { 344 Struct var = result.getVariables(); 345 int index=RestUtil.matchPath(var, Path.init(restPath)/*TODO cache this*/, result.getPath()); 346 if(index>=0 && index+1==result.getPath().length) { 347 bestC = best(consumes,result.getContentType()); 348 bestP = best(produces,result.getAccept()); 349 350 if(bestC==null) status=405; 351 else if(bestP==null) status=406; 352 else { 353 status=200; 354 _callRest(pc, component, udf, path, var,result,bestP,produces, suppressContent,e.getKey()); 355 break; 356 } 357 } 358 } 359 } 360 catch (PageException pe) {} 361 } 362 } 363 if(status==404) 364 RestUtil.setStatus(pc,404,"no rest service for ["+path+"] found"); 365 else if(status==405) 366 RestUtil.setStatus(pc,405,"Unsupported Media Type"); 367 else if(status==406) 368 RestUtil.setStatus(pc,406,"Not Acceptable"); 369 370 371 } 372 373 private MimeType best(MimeType[] produces, MimeType... accept) { 374 if(ArrayUtil.isEmpty(produces)){ 375 if(accept.length>0) return accept[0]; 376 return MimeType.ALL; 377 } 378 379 MimeType best=null,tmp; 380 381 for(int a=0;a<accept.length;a++){ 382 tmp=accept[a].bestMatch(produces); 383 if(tmp!=null && !accept[a].hasWildCards() && tmp.hasWildCards()){ 384 tmp=accept[a]; 385 } 386 if(tmp!=null && 387 (best==null || 388 best.getQuality()<tmp.getQuality() || 389 (best.getQuality()==tmp.getQuality() && best.hasWildCards() && !tmp.hasWildCards()))) 390 best=tmp; 391 } 392 393 394 395 return best; 396 } 397 398 private void _callRest(PageContext pc, Component component, UDF udf,String path, Struct variables, Result result, MimeType best,MimeType[] produces, boolean suppressContent, Key methodName) throws PageException, IOException, ConverterException { 399 FunctionArgument[] fa=udf.getFunctionArguments(); 400 Struct args=new StructImpl(),meta; 401 402 Key name; 403 String restArgName,restArgSource,value; 404 for(int i=0;i<fa.length;i++){ 405 name = fa[i].getName(); 406 meta=fa[i].getMetaData(); 407 restArgSource=meta==null?"":Caster.toString(meta.get(KeyConstants._restArgSource,""),""); 408 409 if("path".equalsIgnoreCase(restArgSource)) 410 setValue(fa[i],args,name, variables.get(name,null)); 411 if("query".equalsIgnoreCase(restArgSource) || "url".equalsIgnoreCase(restArgSource)) 412 setValue(fa[i],args,name, pc.urlScope().get(name,null)); 413 if("form".equalsIgnoreCase(restArgSource)) 414 setValue(fa[i],args,name, pc.formScope().get(name,null)); 415 if("cookie".equalsIgnoreCase(restArgSource)) 416 setValue(fa[i],args,name, pc.cookieScope().get(name,null)); 417 if("header".equalsIgnoreCase(restArgSource) || "head".equalsIgnoreCase(restArgSource)) { 418 restArgName=meta==null?"":Caster.toString(meta.get(KeyConstants._restArgName,""),""); 419 if(StringUtil.isEmpty(restArgName))restArgName=name.getString(); 420 value=ReqRspUtil.getHeaderIgnoreCase(pc, restArgName, null); 421 setValue(fa[i],args,name,value); 422 } 423 if("matrix".equalsIgnoreCase(restArgSource)) 424 setValue(fa[i],args,name, result.getMatrix().get(name,null)); 425 426 if("body".equalsIgnoreCase(restArgSource) || StringUtil.isEmpty(restArgSource,true)){ 427 boolean isSimple=CFTypes.isSimpleType(fa[i].getType()); 428 Object body = ReqRspUtil.getRequestBody(pc,true,null); 429 if(isSimple && !Decision.isSimpleValue(body)) 430 body= ReqRspUtil.getRequestBody(pc,false,null); 431 setValue(fa[i],args,name, body); 432 } 433 434 } 435 Object rtn=null; 436 try{ 437 if(suppressContent)pc.setSilent(); 438 rtn = component.callWithNamedValues(pc, methodName, args); 439 } 440 catch (PageException e) { 441 RestUtil.setStatus(pc, 500, ExceptionUtil.getMessage(e)); 442 } 443 finally { 444 if(suppressContent)pc.unsetSilent(); 445 } 446 447 // custom response 448 Struct sct = result.getCustomResponse(); 449 boolean hasContent=false; 450 if(sct!=null){ 451 HttpServletResponse rsp = pc.getHttpServletResponse(); 452 // status 453 int status = Caster.toIntValue(sct.get(KeyConstants._status,Constants.DOUBLE_ZERO),0); 454 if(status>0)rsp.setStatus(status); 455 456 // content 457 Object o=sct.get(KeyConstants._content,null); 458 if(o!=null) { 459 String content=Caster.toString(o,null); 460 if(content!=null) { 461 try { 462 pc.forceWrite(content); 463 hasContent=true; 464 } 465 catch (IOException e) {} 466 } 467 } 468 469 // headers 470 Struct headers=Caster.toStruct(sct.get(KeyConstants._headers,null),null); 471 if(headers!=null){ 472 //Key[] keys = headers.keys(); 473 Iterator<Entry<Key, Object>> it = headers.entryIterator(); 474 Entry<Key, Object> e; 475 String n,v; 476 Object tmp; 477 while(it.hasNext()){ 478 e = it.next(); 479 n=e.getKey().getString(); 480 tmp=e.getValue(); 481 v=Caster.toString(tmp,null); 482 if(tmp!=null && v==null) v=tmp.toString(); 483 rsp.setHeader(n, v); 484 } 485 } 486 } 487 // convert result 488 if(rtn!=null && !hasContent){ 489 Props props = new Props(); 490 props.format=result.getFormat(); 491 Charset cs = getCharset(pc); 492 if(result.hasFormatExtension()){ 493 //setFormat(pc.getHttpServletResponse(), props.format,cs); 494 _writeOut(pc, props, null, rtn,cs,true); 495 } 496 else { 497 if(best!=null && !MimeType.ALL.same(best)) { 498 int f = MimeType.toFormat(best, -1); 499 if(f!=-1) { 500 props.format=f; 501 //setFormat(pc.getHttpServletResponse(), f,cs); 502 _writeOut(pc, props, null, rtn,cs,true); 503 } 504 else { 505 writeOut(pc,props,rtn,best); 506 } 507 } 508 else { 509 _writeOut(pc, props, null, rtn,cs,true); 510 } 511 } 512 513 514 } 515 516 } 517 518 private void setValue(FunctionArgument fa, Struct args, Key name, Object value) { 519 if(value==null){ 520 Struct meta = fa.getMetaData(); 521 if(meta!=null)value=meta.get(KeyConstants._default,null); 522 } 523 args.setEL(name, value); 524 } 525 526 private void writeOut(PageContext pc, Props props, Object obj, MimeType mt) throws PageException, IOException, ConverterException { 527 // TODO miemtype mapping with converter defintion from external file 528 // Images 529 if(mt.same(MimeType.IMAGE_GIF)) writeOut(pc,obj,mt,new ImageConverter("gif")); 530 else if(mt.same(MimeType.IMAGE_JPG)) writeOut(pc,obj,mt,new ImageConverter("jpeg")); 531 else if(mt.same(MimeType.IMAGE_PNG)) writeOut(pc,obj,mt,new ImageConverter("png")); 532 else if(mt.same(MimeType.IMAGE_TIFF)) writeOut(pc,obj,mt,new ImageConverter("tiff")); 533 else if(mt.same(MimeType.IMAGE_BMP)) writeOut(pc,obj,mt,new ImageConverter("bmp")); 534 else if(mt.same(MimeType.IMAGE_WBMP)) writeOut(pc,obj,mt,new ImageConverter("wbmp")); 535 else if(mt.same(MimeType.IMAGE_FBX)) writeOut(pc,obj,mt,new ImageConverter("fbx")); 536 else if(mt.same(MimeType.IMAGE_FBX)) writeOut(pc,obj,mt,new ImageConverter("fbx")); 537 else if(mt.same(MimeType.IMAGE_PNM)) writeOut(pc,obj,mt,new ImageConverter("pnm")); 538 else if(mt.same(MimeType.IMAGE_PGM)) writeOut(pc,obj,mt,new ImageConverter("pgm")); 539 else if(mt.same(MimeType.IMAGE_PBM)) writeOut(pc,obj,mt,new ImageConverter("pbm")); 540 else if(mt.same(MimeType.IMAGE_ICO)) writeOut(pc,obj,mt,new ImageConverter("ico")); 541 else if(mt.same(MimeType.IMAGE_PSD)) writeOut(pc,obj,mt,new ImageConverter("psd")); 542 else if(mt.same(MimeType.IMAGE_ASTERIX)) writeOut(pc,obj,MimeType.IMAGE_PNG,new ImageConverter("png")); 543 544 // Application 545 else if(mt.same(MimeType.APPLICATION_JAVA)) writeOut(pc,obj,mt,new JavaConverter()); 546 //if("application".equalsIgnoreCase(mt.getType())) 547 548 549 else _writeOut(pc, props, null, obj,null,true); 550 } 551 552 private static void writeOut(PageContext pc, Object obj, MimeType mt,BinaryConverter converter) throws ConverterException, IOException { 553 pc.getResponse().setContentType(mt.toString()); 554 555 OutputStream os=null; 556 try{ 557 converter.writeOut(pc, obj, os=pc.getResponseStream()); 558 } 559 finally{ 560 IOUtil.closeEL(os); 561 } 562 } 563 564 public static boolean isSoap(PageContext pc) { 565 HttpServletRequest req = pc.getHttpServletRequest(); 566 InputStream is=null; 567 try { 568 is=req.getInputStream(); 569 570 String input = IOUtil.toString(is,CharsetUtil.ISO88591); 571 return 572 StringUtil.indexOfIgnoreCase(input, ":Envelope>")!=-1; 573 574 // soap:Envelope 575 } 576 catch (IOException e) { 577 return false; 578 } 579 finally { 580 IOUtil.closeEL(is); 581 } 582 } 583 584 585 private void callWDDX(PageContext pc, Component component, Collection.Key methodName, boolean suppressContent) throws PageException { 586 try{ 587 //Struct url = StructUtil.duplicate(pc.urlFormScope(),true); 588 Struct url=StructUtil.merge(new Struct[]{pc.formScope(),pc.urlScope()}); 589 // define args 590 url.removeEL(KeyConstants._fieldnames); 591 url.removeEL(KeyConstants._method); 592 Object args=url.get(KeyConstants._argumentCollection,null); 593 String strArgCollFormat=Caster.toString(url.get("argumentCollectionFormat",null),null); 594 595 596 // url.returnFormat 597 int urlReturnFormat=-1; 598 Object oReturnFormatFromURL=url.get(KeyConstants._returnFormat,null); 599 if(oReturnFormatFromURL!=null)urlReturnFormat=UDFUtil.toReturnFormat(Caster.toString(oReturnFormatFromURL,null),-1); 600 601 // request header "accept" 602 List<MimeType> accept = ReqRspUtil.getAccept(pc); 603 int headerReturnFormat = MimeType.toFormat(accept,UDF.RETURN_FORMAT_XML, -1); 604 605 Object queryFormat=url.get(KeyConstants._queryFormat,null); 606 607 608 609 if(args==null){ 610 args=pc.getHttpServletRequest().getAttribute("argumentCollection"); 611 } 612 if(StringUtil.isEmpty(strArgCollFormat)) { 613 strArgCollFormat=Caster.toString(pc.getHttpServletRequest().getAttribute("argumentCollectionFormat"),null); 614 } 615 616 //content-type 617 Charset cs = getCharset(pc); 618 Object o = component.get(pc,methodName,null); 619 620 // onMissingMethod 621 if(o==null) o=component.get(pc,KeyConstants._onmissingmethod,null); 622 623 624 Props props = getProps(pc, o, urlReturnFormat,headerReturnFormat); 625 //if(!props.output) 626 setFormat(pc.getHttpServletResponse(),props.format,cs); 627 628 629 Object rtn=null; 630 try{ 631 if(suppressContent)pc.setSilent(); 632 633 634 if(args==null){ 635 url=translate(component,methodName.getString(),url); 636 rtn = component.callWithNamedValues(pc, methodName, url); 637 } 638 else if(args instanceof String){ 639 String str=(String)args; 640 int format = UDFUtil.toReturnFormat(strArgCollFormat,-1); 641 642 // CFML 643 if(UDF.RETURN_FORMAT_SERIALIZE==format) { 644 // do not catch exception when format is defined 645 args=new CFMLExpressionInterpreter().interpret(pc, str); 646 } 647 // JSON 648 if(UDF.RETURN_FORMAT_JSON==format) { 649 // do not catch exception when format is defined 650 args=new JSONExpressionInterpreter(false).interpret(pc, str); 651 } 652 // default 653 else { 654 // catch exception when format is not defined, then in this case the string can also be a simple argument 655 try { 656 args=new JSONExpressionInterpreter(false).interpret(pc, str); 657 } 658 catch (PageException pe) { 659 try { 660 args=new CFMLExpressionInterpreter().interpret(pc, str); 661 } 662 catch (PageException _pe) {} 663 } 664 } 665 } 666 667 // call 668 if(args!=null) { 669 if(Decision.isCastableToStruct(args)){ 670 rtn = component.callWithNamedValues(pc, methodName, Caster.toStruct(args,false)); 671 } 672 else if(Decision.isCastableToArray(args)){ 673 rtn = component.call(pc, methodName, Caster.toNativeArray(args)); 674 } 675 else { 676 Object[] ac=new Object[1]; 677 ac[0]=args; 678 rtn = component.call(pc, methodName, ac); 679 } 680 } 681 } 682 finally { 683 if(suppressContent)pc.unsetSilent(); 684 } 685 // convert result 686 if(rtn!=null){ 687 if(pc.getHttpServletRequest().getHeader("AMF-Forward")!=null) { 688 pc.variablesScope().setEL("AMF-Forward", rtn); 689 } 690 else { 691 _writeOut(pc, props, queryFormat, rtn,cs,false); 692 } 693 } 694 } 695 catch(Throwable t){ 696 PageException pe = Caster.toPageException(t); 697 if(pe instanceof PageExceptionImpl) ((PageExceptionImpl)pe).setExposeMessage(true); 698 throw pe; 699 } 700 } 701 702 private static void setFormat(HttpServletResponse rsp, int format, Charset charset) { 703 String strCS; 704 if(charset==null) strCS=""; 705 else strCS="; charset="+charset.displayName(); 706 707 switch(format){ 708 case UDF.RETURN_FORMAT_WDDX: 709 rsp.setContentType("text/xml"+strCS); 710 rsp.setHeader("Return-Format", "wddx"); 711 break; 712 case UDF.RETURN_FORMAT_JSON: 713 rsp.setContentType("application/json"+strCS); 714 rsp.setHeader("Return-Format", "json"); 715 break; 716 case UDF.RETURN_FORMAT_PLAIN: 717 rsp.setContentType("text/plain"+strCS); 718 rsp.setHeader("Return-Format", "plain"); 719 break; 720 case UDF.RETURN_FORMAT_XML: 721 rsp.setContentType("text/xml"+strCS); 722 rsp.setHeader("Return-Format", "xml"); 723 break; 724 case UDF.RETURN_FORMAT_SERIALIZE: 725 rsp.setContentType("application/cfml"+strCS); 726 rsp.setHeader("Return-Format", "cfml"); 727 break; 728 case UDFPlus.RETURN_FORMAT_JAVA: 729 rsp.setContentType("application/java"); // no charset this is a binary format 730 rsp.setHeader("Return-Format", "java"); 731 break; 732 } 733 } 734 735 private static Props getProps(PageContext pc, Object o,int urlReturnFormat,int headerReturnFormat) { 736 Props props = new Props(); 737 738 props.strType="any"; 739 props.secureJson=pc.getApplicationContext().getSecureJson(); 740 int udfReturnFormat=-1; 741 if(o instanceof UDFPlus) { 742 UDFPlus udf = ((UDFPlus)o); 743 udfReturnFormat=udf.getReturnFormat(-1); 744 props.type=udf.getReturnType(); 745 props.strType=udf.getReturnTypeAsString(); 746 props.output=udf.getOutput(); 747 if(udf.getSecureJson()!=null)props.secureJson=udf.getSecureJson().booleanValue(); 748 } 749 750 // format 751 if(isValid(urlReturnFormat)) props.format=urlReturnFormat; 752 else if(isValid(udfReturnFormat)) props.format=udfReturnFormat; 753 else if(isValid(headerReturnFormat)) props.format=headerReturnFormat; 754 else props.format=UDF.RETURN_FORMAT_WDDX; 755 756 // return type XML ignore WDDX 757 if(props.type==CFTypes.TYPE_XML) { 758 if(UDF.RETURN_FORMAT_WDDX==props.format) 759 props.format=UDF.RETURN_FORMAT_PLAIN; 760 } 761 762 763 764 return props; 765 } 766 767 private static boolean isValid(int returnFormat) { 768 return returnFormat!=-1 && returnFormat!=UDF.RETURN_FORMAT_XML; 769 } 770 771 public static void writeToResponseStream(PageContext pc,Component component, String methodName,int urlReturnFormat,int headerReturnFormat,Object queryFormat,Object rtn) throws ConverterException, PageException, IOException { 772 Object o = component.get(KeyImpl.init(methodName),null); 773 Props p = getProps(pc, o, urlReturnFormat,headerReturnFormat); 774 _writeOut(pc, p, queryFormat, rtn,null,true); 775 } 776 777 private static void _writeOut(PageContext pc,Props props,Object queryFormat,Object rtn,Charset cs, boolean setFormat) throws ConverterException, PageException, IOException { 778 // return type XML ignore WDDX 779 if(props.type==CFTypes.TYPE_XML) { 780 //if(UDF.RETURN_FORMAT_WDDX==format) format=UDF.RETURN_FORMAT_PLAIN; 781 rtn=Caster.toString(Caster.toXML(rtn)); 782 } 783 // function does no real cast, only check it 784 else rtn=Caster.castTo(pc, (short)props.type, props.strType, rtn); 785 if(setFormat)setFormat(pc.getHttpServletResponse(), props.format, cs); 786 787 // WDDX 788 if(UDF.RETURN_FORMAT_WDDX==props.format) { 789 WDDXConverter converter = new WDDXConverter(pc.getTimeZone(),false,false); 790 converter.setTimeZone(pc.getTimeZone()); 791 pc.forceWrite(converter.serialize(rtn)); 792 } 793 // JSON 794 else if(UDF.RETURN_FORMAT_JSON==props.format) { 795 boolean byColumn = false; 796 if(queryFormat instanceof String){ 797 String strQF=((String) queryFormat).trim(); 798 if(strQF.equalsIgnoreCase("row")); 799 else if(strQF.equalsIgnoreCase("column"))byColumn=true; 800 else throw new ApplicationException("invalid queryformat definition ["+strQF+"], valid formats are [row,column]"); 801 } 802 JSONConverter converter = new JSONConverter(false,cs); 803 String prefix=""; 804 if(props.secureJson) { 805 prefix=pc.getApplicationContext().getSecureJsonPrefix(); 806 if(prefix==null)prefix=""; 807 } 808 pc.forceWrite(prefix+converter.serialize(pc,rtn,byColumn)); 809 } 810 // CFML 811 else if(UDF.RETURN_FORMAT_SERIALIZE==props.format) { 812 ScriptConverter converter = new ScriptConverter(false); 813 pc.forceWrite(converter.serialize(rtn)); 814 } 815 // XML 816 else if(UDF.RETURN_FORMAT_XML==props.format) { 817 XMLConverter converter = new XMLConverter(pc.getTimeZone(),false); 818 converter.setTimeZone(pc.getTimeZone()); 819 pc.forceWrite(converter.serialize(rtn)); 820 } 821 // Plain 822 else if(UDF.RETURN_FORMAT_PLAIN==props.format) { 823 pc.forceWrite(Caster.toString(rtn)); 824 } 825 826 // JAVA 827 else if(UDFPlus.RETURN_FORMAT_JAVA==props.format) { 828 writeOut(pc,rtn,MimeType.APPLICATION_JAVA,new JavaConverter()); 829 } 830 else throw new IOException("invalid return format defintion:"+props.format); 831 } 832 833 public static Struct translate(Component c, String strMethodName, Struct params) { 834 Collection.Key methodName=KeyImpl.init(strMethodName); 835 Key[] keys = CollectionUtil.keys(params); 836 FunctionArgument[] args=null; 837 int index=-1; 838 Object value; 839 for(int i=0;i<keys.length;i++){ 840 index=Caster.toIntValue(keys[i].getString(),0); 841 if(index>0) { 842 if(args==null)args=_getArgs(c,methodName); 843 if(args!=null && index<=args.length) { 844 value=params.removeEL(keys[i]); 845 if(value!=null)params.setEL(args[index-1].getName(), value); 846 } 847 } 848 849 } 850 return params; 851 } 852 853 private static FunctionArgument[] _getArgs(Component c, Collection.Key methodName) { 854 Object o=c.get(methodName,null); 855 if(o instanceof UDF) return ((UDF) o).getFunctionArguments(); 856 return null; 857 } 858 859 private void callCFCMetaData(PageContext pc, Component cfc, int format) throws IOException, PageException, ConverterException { 860 ComponentSpecificAccess cw = new ComponentSpecificAccess(Component.ACCESS_REMOTE,cfc); 861 ComponentScope scope = cw.getComponentScope(); 862 Struct udfs=new StructImpl(),sctUDF,sctArg; 863 Array arrArg; 864 Iterator<Object> it = scope.valueIterator(); 865 Object v; 866 UDF udf; 867 FunctionArgument[] args; 868 while(it.hasNext()){ 869 v=it.next(); 870 // UDF 871 if(v instanceof UDF) { 872 udf=(UDF) v; 873 sctUDF=new StructImpl(); 874 arrArg=new ArrayImpl(); 875 udfs.setEL(udf.getFunctionName(), sctUDF); 876 args = udf.getFunctionArguments(); 877 for(int i=0;i<args.length;i++){ 878 sctArg=new StructImpl(); 879 arrArg.appendEL(sctArg); 880 sctArg.setEL(KeyConstants._name, args[i].getName().getString()); 881 sctArg.setEL(KeyConstants._type, args[i].getTypeAsString()); 882 sctArg.setEL(KeyConstants._required, args[i].isRequired()); 883 if(!StringUtil.isEmpty(args[i].getHint()))sctArg.setEL(KeyConstants._hint, args[i].getHint()); 884 } 885 sctUDF.set(KeyConstants._arguments, arrArg); 886 sctUDF.set(KeyConstants._returntype, udf.getReturnTypeAsString()); 887 888 } 889 } 890 Struct rtn=new StructImpl(); 891 rtn.set(KeyConstants._functions, udfs); 892 rtn.set(ACCEPT_ARG_COLL_FORMATS, "cfml,json"); 893 894 895 InputStream is; 896 Charset cs=null; 897 // WDDX 898 if(UDF.RETURN_FORMAT_WDDX==format) { 899 WDDXConverter converter = new WDDXConverter(pc.getTimeZone(),false,false); 900 converter.setTimeZone(pc.getTimeZone()); 901 String str = converter.serialize(rtn); 902 cs = getCharset(pc); 903 is = new ByteArrayInputStream(str.getBytes(cs)); 904 } 905 906 // JSON 907 else if(UDF.RETURN_FORMAT_JSON==format) { 908 boolean byColumn = false; 909 cs = getCharset(pc); 910 JSONConverter converter = new JSONConverter(false,cs); 911 String str = converter.serialize(pc,rtn,byColumn); 912 is = new ByteArrayInputStream(str.getBytes(cs)); 913 914 } 915 // CFML 916 else if(UDF.RETURN_FORMAT_SERIALIZE==format) { 917 ScriptConverter converter = new ScriptConverter(false); 918 String str=converter.serialize(rtn); 919 cs = getCharset(pc); 920 is = new ByteArrayInputStream(str.getBytes(cs)); 921 } 922 // XML 923 else if(UDF.RETURN_FORMAT_XML==format) { 924 XMLConverter converter = new XMLConverter(pc.getTimeZone(),false); 925 converter.setTimeZone(pc.getTimeZone()); 926 String str=converter.serialize(rtn); 927 cs = getCharset(pc); 928 is = new ByteArrayInputStream(str.getBytes(cs)); 929 } 930 // Plain 931 else if(UDF.RETURN_FORMAT_PLAIN==format) { 932 String str= Caster.toString(rtn); 933 cs = getCharset(pc); 934 is = new ByteArrayInputStream(str.getBytes(cs)); 935 } 936 // Java 937 else if(UDFPlus.RETURN_FORMAT_JAVA==format) { 938 byte[] bytes = JavaConverter.serializeAsBinary(rtn); 939 is = new ByteArrayInputStream(bytes); 940 941 } 942 else throw new IOException("invalid format defintion:"+format); 943 944 945 946 947 OutputStream os=null; 948 try { 949 os=pc.getResponseStream(); 950 setFormat(pc.getHttpServletResponse(), format, cs); 951 IOUtil.copy(is, os, false,false); 952 953 } 954 finally { 955 IOUtil.flushEL(os); 956 IOUtil.closeEL(os); 957 ((PageContextImpl)pc).getRootOut().setClosed(true); 958 } 959 } 960 961 private Charset getCharset(PageContext pc) { 962 HttpServletResponse rsp = pc.getHttpServletResponse(); 963 Charset cs = ReqRspUtil.getCharacterEncoding(pc,rsp); 964 if(cs==null)cs=((PageContextImpl)pc).getWebCharset(); 965 return cs; 966 } 967 968 private void callWSDL(PageContext pc, Component component) throws ServletException, IOException, ExpressionException { 969 // take wsdl file defined by user 970 String wsdl = component.getWSDLFile(); 971 if(!StringUtil.isEmpty(wsdl)) { 972 973 OutputStream os=null; 974 Resource input = ResourceUtil.toResourceExisting(pc, wsdl); 975 try { 976 os=pc.getResponseStream(); 977 pc.getResponse().setContentType("text/xml; charset=utf-8"); 978 IOUtil.copy(input, os, false); 979 980 } 981 finally { 982 IOUtil.flushEL(os); 983 IOUtil.closeEL(os); 984 ((PageContextImpl)pc).getRootOut().setClosed(true); 985 } 986 } 987 // create a wsdl file 988 else { 989 RPCServer.getInstance(pc.getId(),pc.getServletContext()) 990 .doGet(pc.getHttpServletRequest(), pc. getHttpServletResponse(), component); 991 } 992 } 993 994 private void callWebservice(PageContext pc, Component component) throws IOException, ServletException { 995 ComponentController.set(pc, component); 996 try { 997 RPCServer.getInstance(pc.getId(),pc.getServletContext()) 998 .doPost(pc.getHttpServletRequest(), pc. getHttpServletResponse(), component); 999 } 1000 finally { 1001 ComponentController.release(); 1002 } 1003 } 1004 1005 1006 public abstract void initComponent(PageContext pc,ComponentImpl c) throws PageException; 1007 1008 public void ckecked() { 1009 lastCheck=System.currentTimeMillis(); 1010 } 1011 1012 public long lastCheck() { 1013 return lastCheck; 1014 } 1015 1016} 1017 class Props { 1018 1019 public String strType="any"; 1020 public boolean secureJson; 1021 public int type=CFTypes.TYPE_ANY; 1022 public int format=UDF.RETURN_FORMAT_WDDX; 1023 public boolean output=true; 1024 1025 }