001    package railo.runtime;
002    
003    import java.io.IOException;
004    import java.io.InputStream;
005    import java.io.OutputStream;
006    import java.util.Iterator;
007    import java.util.Map.Entry;
008    
009    import javax.servlet.ServletException;
010    import javax.servlet.http.HttpServletRequest;
011    import javax.servlet.http.HttpServletResponse;
012    
013    import railo.commons.io.IOUtil;
014    import railo.commons.io.res.Resource;
015    import railo.commons.io.res.util.ResourceUtil;
016    import railo.commons.lang.CFTypes;
017    import railo.commons.lang.ExceptionUtil;
018    import railo.commons.lang.StringUtil;
019    import railo.commons.lang.mimetype.MimeType;
020    import railo.runtime.config.ConfigImpl;
021    import railo.runtime.config.ConfigWebImpl;
022    import railo.runtime.converter.BinaryConverter;
023    import railo.runtime.converter.ConverterException;
024    import railo.runtime.converter.JSONConverter;
025    import railo.runtime.converter.JavaConverter;
026    import railo.runtime.converter.ScriptConverter;
027    import railo.runtime.converter.WDDXConverter;
028    import railo.runtime.converter.XMLConverter;
029    import railo.runtime.converter.bin.ImageConverter;
030    import railo.runtime.dump.DumpUtil;
031    import railo.runtime.dump.DumpWriter;
032    import railo.runtime.exp.ApplicationException;
033    import railo.runtime.exp.ExpressionException;
034    import railo.runtime.exp.PageException;
035    import railo.runtime.gateway.GatewayEngineImpl;
036    import railo.runtime.interpreter.JSONExpressionInterpreter;
037    import railo.runtime.net.http.ReqRspUtil;
038    import railo.runtime.net.rpc.server.ComponentController;
039    import railo.runtime.net.rpc.server.RPCServer;
040    import railo.runtime.op.Caster;
041    import railo.runtime.op.Constants;
042    import railo.runtime.op.Decision;
043    import railo.runtime.rest.RestUtil;
044    import railo.runtime.rest.Result;
045    import railo.runtime.rest.path.Path;
046    import railo.runtime.type.Array;
047    import railo.runtime.type.Collection;
048    import railo.runtime.type.Collection.Key;
049    import railo.runtime.type.FunctionArgument;
050    import railo.runtime.type.KeyImpl;
051    import railo.runtime.type.Struct;
052    import railo.runtime.type.StructImpl;
053    import railo.runtime.type.UDF;
054    import railo.runtime.type.UDFImpl;
055    import railo.runtime.type.scope.Scope;
056    import railo.runtime.type.util.ArrayUtil;
057    import railo.runtime.type.util.CollectionUtil;
058    import railo.runtime.type.util.ComponentUtil;
059    import railo.runtime.type.util.KeyConstants;
060    import railo.runtime.type.util.ListUtil;
061    import railo.runtime.type.util.StructUtil;
062    
063    /**
064     * A Page that can produce Components
065     */
066    public abstract class ComponentPage extends PagePlus  {
067            
068            private static final long serialVersionUID = -3483642653131058030L;
069            
070            public static final railo.runtime.type.Collection.Key METHOD = KeyConstants._method;
071            public static final railo.runtime.type.Collection.Key REMOTE_PERSISTENT_ID = KeyImpl.intern("Id16hohohh");
072    
073            private long lastCheck=-1;
074            
075            
076            public abstract ComponentImpl newInstance(PageContext pc,String callPath,boolean isRealPath)
077                    throws railo.runtime.exp.PageException; 
078            
079            @Override
080            public void call(PageContext pc) throws PageException {
081                    // remote persistent (only type server is supported)
082                    String strRemotePersisId = Caster.toString(getURLorForm(pc,REMOTE_PERSISTENT_ID,null),null);//Caster.toString(pc.urlFormScope().get(REMOTE_PERSISTENT_ID,null),null);
083                    
084                    if(!StringUtil.isEmpty(strRemotePersisId,true)) {
085                            strRemotePersisId=strRemotePersisId.trim();
086                    }
087                    else strRemotePersisId=null;
088                    
089                    HttpServletRequest req = pc.getHttpServletRequest();
090                    // client
091                    String client = Caster.toString(req.getAttribute("client"),null);
092                    // call type (invocation, store-only)
093                    String callType = Caster.toString(req.getAttribute("call-type"),null);
094                    boolean fromGateway="railo-gateway-1-0".equals(client);
095                    boolean fromRest="railo-rest-1-0".equals(client);
096                    Component component;
097            try {
098                pc.setSilent();
099                // load the cfc
100                try {
101                        if(fromGateway && strRemotePersisId!=null) {
102                            ConfigWebImpl config=(ConfigWebImpl) pc.getConfig();
103                            GatewayEngineImpl engine = config.getGatewayEngine();
104                            component=engine.getPersistentRemoteCFC(strRemotePersisId);
105                            
106                            if(component==null) {
107                                    component=newInstance(pc,getPageSource().getComponentName(),false);
108                                    if(!fromGateway)component=ComponentWrap.toComponentWrap(Component.ACCESS_REMOTE,component);
109                                    
110                                    engine.setPersistentRemoteCFC(strRemotePersisId,component);
111                            }
112                            
113                        }
114                        else {
115                            component=newInstance(pc,getPageSource().getComponentName(),false);
116                            if(!fromGateway)component=ComponentWrap.toComponentWrap(Component.ACCESS_REMOTE,component);
117                        }
118                }
119                finally {
120                    pc.unsetSilent();
121                }
122                
123                // Only get the Component, no invocation
124                if("store-only".equals(callType)) {
125                    req.setAttribute("component", component);
126                    return;
127                }
128                
129                
130                
131                // METHOD INVOCATION
132                            String qs=ReqRspUtil.getQueryString(pc.getHttpServletRequest());
133                if(pc.getBasePageSource()==this.getPageSource())
134                    pc.getDebugger().setOutput(false);
135                boolean isPost=pc.getHttpServletRequest().getMethod().equalsIgnoreCase("POST");
136                
137                boolean suppressContent = ((ConfigImpl)pc.getConfig()).isSuppressContent();
138                if(suppressContent)pc.clear();
139                Object method;
140                
141                if(fromRest){ 
142                    
143                    callRest(pc,component,Caster.toString(req.getAttribute("rest-path"),""),(Result)req.getAttribute("rest-result"),suppressContent);
144                    return;
145                }
146                
147                
148                
149                // POST
150                if(isPost) {
151                    // Soap
152                    if(isSoap(pc)) { 
153                            callWebservice(pc,component);
154                            //close(pc);
155                        return;
156                    }
157                            // WDDX
158                    else if((method=getURLorForm(pc, KeyConstants._method, null))!=null) {
159                        callWDDX(pc,component,KeyImpl.toKey(method),suppressContent);
160                            //close(pc);
161                        return;
162                    }
163                    
164                }
165                
166                // GET
167                else {
168                    // WSDL
169                    if(qs!=null && qs.trim().equalsIgnoreCase("wsdl")) {
170                        callWSDL(pc,component);
171                            //close(pc);
172                        return;
173                    } 
174                            // WDDX
175                    else if((method=getURLorForm(pc, KeyConstants._method, null))!=null) {
176                        callWDDX(pc,component,KeyImpl.toKey(method),suppressContent);
177                        //close(pc); 
178                        return;
179                    } 
180                }
181                
182                
183                // Include MUST
184                Array path = pc.getTemplatePath();
185                //if(path.size()>1 ) {
186                if(path.size()>1 && !(path.size()==3 && ListUtil.last(path.getE(2).toString(),"/\\",true).equalsIgnoreCase(railo.runtime.config.Constants.APP_CFC)) ) {// MUSTMUST bad impl -> check with and without application.cfc
187                    
188                    ComponentWrap c = ComponentWrap.toComponentWrap(Component.ACCESS_PRIVATE,ComponentUtil.toComponentAccess(component));
189                    Key[] keys = c.keys();
190                    Object el;
191                    Scope var = pc.variablesScope();
192                    for(int i=0;i<keys.length;i++) {
193                            el=c.get(keys[i],null);
194                            if(el instanceof UDF) 
195                                    var.set(keys[i], el);
196                            
197                    }
198                    
199                    return;
200                }
201               
202                
203                            // DUMP
204                            //TODO component.setAccess(pc,Component.ACCESS_PUBLIC);
205                            String cdf = pc.getConfig().getComponentDumpTemplate();
206                            
207                            if(cdf!=null && cdf.trim().length()>0) {
208                                pc.variablesScope().set(KeyConstants._component,component);
209                                pc.doInclude(cdf);
210                            }
211                            else pc.write(pc.getConfig().getDefaultDumpWriter(DumpWriter.DEFAULT_RICH).toString(pc,component.toDumpData(pc,9999,DumpUtil.toDumpProperties() ),true));
212                            
213                    }
214                    catch(Throwable t) {
215                            throw Caster.toPageException(t);//Exception Handler.castAnd Stack(t, this, pc);
216                    }
217            }
218            
219            private Object getURLorForm(PageContext pc, Key key, Object defaultValue) {
220                    Object res = pc.formScope().get(key,null);
221                    if(res!=null) return res;
222                    return pc.urlScope().get(key,defaultValue);
223            }
224    
225            private void callRest(PageContext pc, Component component, String path, Result result, boolean suppressContent) throws IOException, ConverterException {
226                    String method = pc.getHttpServletRequest().getMethod();
227                    String[] subPath = result.getPath();
228                    Struct cMeta;
229                    try {
230                            cMeta = component.getMetaData(pc);
231                    } catch (PageException pe) {
232                            throw ExceptionUtil.toIOException(pe);
233                    }
234                    
235    
236                    // Consumes
237                    MimeType[] cConsumes=null;
238                    String strMimeType = Caster.toString(cMeta.get(KeyConstants._consumes,null),null);
239                    if(!StringUtil.isEmpty(strMimeType,true)){
240                            cConsumes = MimeType.getInstances(strMimeType,',');
241                    }
242                    
243                    // Produces
244                    MimeType[] cProduces=null;
245                    strMimeType = Caster.toString(cMeta.get(KeyConstants._produces,null),null);
246                    if(!StringUtil.isEmpty(strMimeType,true)){
247                            cProduces = MimeType.getInstances(strMimeType,',');
248                    }
249                    
250                    
251                    
252                    Iterator<Entry<Key, Object>> it = component.entryIterator();
253                    Entry<Key, Object> e;
254                    Object value;
255                    UDF udf;
256                    Struct meta;
257                    int status=405;
258                    MimeType bestP,bestC;
259                    while(it.hasNext()){
260                            e = it.next();
261                            value=e.getValue();
262                            if(value instanceof UDF){
263                                    udf=(UDF)value;
264                                    try {
265                                            meta = udf.getMetaData(pc);
266                                            
267                                            // check if http method match
268                                            String httpMethod = Caster.toString(meta.get(KeyConstants._httpmethod,null),null);
269                                            if(StringUtil.isEmpty(httpMethod) || !httpMethod.equalsIgnoreCase(method)) continue;
270                                            
271    
272                                            // get consumes mimetype
273                                            MimeType[] consumes;
274                                            strMimeType = Caster.toString(meta.get(KeyConstants._consumes,null),null);
275                                            if(!StringUtil.isEmpty(strMimeType,true)){
276                                                    consumes = MimeType.getInstances(strMimeType,',');
277                                            }
278                                            else
279                                                    consumes=cConsumes;
280                                            
281                                            
282                                            // get produces mimetype
283                                            MimeType[] produces;
284                                            strMimeType = Caster.toString(meta.get(KeyConstants._produces,null),null);
285                                            if(!StringUtil.isEmpty(strMimeType,true)){
286                                                    produces = MimeType.getInstances(strMimeType,',');
287                                            }
288                                            else
289                                                    produces=cProduces;
290                                            
291                                            
292                                            
293                                            
294                                            String restPath = Caster.toString(meta.get(KeyConstants._restPath,null),null);
295                                            
296                                            // no rest path
297                                            if(StringUtil.isEmpty(restPath)){
298                                                    if(ArrayUtil.isEmpty(subPath)) {
299                                                            bestC = best(consumes,result.getContentType());
300                                                            bestP = best(produces,result.getAccept());
301                                                            if(bestC==null) status=405;
302                                                            else if(bestP==null) status=406;
303                                                            else {
304                                                                    status=200;
305                                                                    _callRest(pc, component, udf, path, result.getVariables(),result,bestP,produces, suppressContent,e.getKey());
306                                                                    break;
307                                                            }
308                                                    }
309                                            }
310                                            else {
311                                                    Struct var = result.getVariables();
312                                                    int index=RestUtil.matchPath(var, Path.init(restPath)/*TODO cache this*/, result.getPath());
313                                                    if(index>=0 && index+1==result.getPath().length) {
314                                                            bestC = best(consumes,result.getContentType());
315                                                            bestP = best(produces,result.getAccept());
316                                                            
317                                                            if(bestC==null) status=405;
318                                                            else if(bestP==null) status=406;
319                                                            else {
320                                                                    status=200;
321                                                                    _callRest(pc, component, udf, path, var,result,bestP,produces, suppressContent,e.getKey());
322                                                                    break;
323                                                            }
324                                                    }
325                                            }
326                                    } 
327                                    catch (PageException pe) {}
328                            }
329                    }
330                    if(status==404)
331                            RestUtil.setStatus(pc,404,"no rest service for ["+path+"] found");
332                    else if(status==405)
333                            RestUtil.setStatus(pc,405,"Unsupported Media Type");
334                    else if(status==406)
335                            RestUtil.setStatus(pc,406,"Not Acceptable");
336                    
337            
338            }
339    
340            private MimeType best(MimeType[] produces, MimeType... accept) {
341                    if(ArrayUtil.isEmpty(produces)){
342                            if(accept.length>0) return accept[0];
343                            return MimeType.ALL;
344                    }
345                    
346                    MimeType best=null,tmp;
347                    
348                    for(int a=0;a<accept.length;a++){
349                            tmp=accept[a].bestMatch(produces);
350                            if(tmp!=null && !accept[a].hasWildCards() && tmp.hasWildCards()){
351                                    tmp=accept[a];
352                            }
353                            if(tmp!=null && 
354                                            (best==null || 
355                                             best.getQuality()<tmp.getQuality() || 
356                                             (best.getQuality()==tmp.getQuality() && best.hasWildCards() && !tmp.hasWildCards())))
357                                    best=tmp;
358                    }
359                    
360                    
361                    
362                    return best;
363            }
364    
365            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 {
366                    FunctionArgument[] fa=udf.getFunctionArguments();
367                    Struct args=new StructImpl(),meta;
368                    
369                    Key name;
370                    String restArgName,restArgSource,value;
371                    for(int i=0;i<fa.length;i++){
372                            name = fa[i].getName();
373                            meta=fa[i].getMetaData();
374                            restArgSource=meta==null?"":Caster.toString(meta.get(KeyConstants._restArgSource,""),"");
375                            
376                            if("path".equalsIgnoreCase(restArgSource))
377                                    setValue(fa[i],args,name, variables.get(name,null));
378                            if("query".equalsIgnoreCase(restArgSource) || "url".equalsIgnoreCase(restArgSource))
379                                    setValue(fa[i],args,name, pc.urlScope().get(name,null));
380                            if("form".equalsIgnoreCase(restArgSource))
381                                    setValue(fa[i],args,name, pc.formScope().get(name,null));
382                            if("cookie".equalsIgnoreCase(restArgSource))
383                                    setValue(fa[i],args,name, pc.cookieScope().get(name,null));
384                            if("header".equalsIgnoreCase(restArgSource) || "head".equalsIgnoreCase(restArgSource)) {
385                                    restArgName=meta==null?"":Caster.toString(meta.get(KeyConstants._restArgName,""),"");
386                                    if(StringUtil.isEmpty(restArgName))restArgName=name.getString();
387                                    value=ReqRspUtil.getHeaderIgnoreCase(pc, restArgName, null);
388                                    setValue(fa[i],args,name,value);
389                            }
390                            if("matrix".equalsIgnoreCase(restArgSource))
391                                    setValue(fa[i],args,name, result.getMatrix().get(name,null));
392                            
393                            if("body".equalsIgnoreCase(restArgSource) || StringUtil.isEmpty(restArgSource,true)){
394                                    boolean isSimple=CFTypes.isSimpleType(fa[i].getType());
395                                    Object body = ReqRspUtil.getRequestBody(pc,true,null);
396                                    if(isSimple && !Decision.isSimpleValue(body))
397                                            body= ReqRspUtil.getRequestBody(pc,false,null);
398                                    setValue(fa[i],args,name, body);
399                            }
400                            
401                    }
402                    Object rtn=null;
403                    try{
404                    if(suppressContent)pc.setSilent();
405                            rtn = component.callWithNamedValues(pc, methodName, args);
406                    } 
407                    catch (PageException e) {
408                            RestUtil.setStatus(pc, 500, ExceptionUtil.getMessage(e));
409                    }
410            finally {
411                    if(suppressContent)pc.unsetSilent();
412            }
413            
414            // custom response
415                    Struct sct = result.getCustomResponse();
416                    boolean hasContent=false;
417            if(sct!=null){
418                            HttpServletResponse rsp = pc.getHttpServletResponse();
419                    // status
420                    int status = Caster.toIntValue(sct.get(KeyConstants._status,Constants.DOUBLE_ZERO),0);
421                    if(status>0)rsp.setStatus(status);
422                            
423                    // content
424                    Object o=sct.get(KeyConstants._content,null);
425                    if(o!=null) {
426                            String content=Caster.toString(o,null);
427                            if(content!=null) {
428                                    try {
429                                                    pc.forceWrite(content);
430                                                    hasContent=true;
431                                            } 
432                                    catch (IOException e) {}
433                            }
434                    }
435                    
436                    // headers
437                    Struct headers=Caster.toStruct(sct.get(KeyConstants._headers,null),null);
438                    if(headers!=null){
439                            //Key[] keys = headers.keys();
440                            Iterator<Entry<Key, Object>> it = headers.entryIterator();
441                            Entry<Key, Object> e;
442                            String n,v;
443                            Object tmp;
444                            while(it.hasNext()){
445                                    e = it.next();
446                                    n=e.getKey().getString();
447                                    tmp=e.getValue();
448                                    v=Caster.toString(tmp,null);
449                                    if(tmp!=null && v==null) v=tmp.toString();
450                                    rsp.setHeader(n, v);
451                            }       
452                    }
453                    }
454            // convert result
455                    if(rtn!=null && !hasContent){
456                            Props props = new Props();
457                    props.format=result.getFormat();
458                    
459                    if(result.hasFormatExtension()){
460                            setFormat(pc.getHttpServletResponse(), props.format);
461                            pc.forceWrite(convertResult(pc, props, null, rtn));
462                    }
463                    else {
464                            if(best!=null && !MimeType.ALL.same(best)) {
465                            int f = MimeType.toFormat(best, -1);
466                            if(f!=-1) {
467                                    props.format=f;
468                                    setFormat(pc.getHttpServletResponse(), f);
469                                    pc.forceWrite(convertResult(pc, props, null, rtn));
470                            }
471                            else {
472                                    writeOut(pc,props,rtn,best);
473                            }
474                    }
475                            else pc.forceWrite(convertResult(pc, props, null, rtn));
476                    }
477                    
478                    
479            }
480                    
481            }
482    
483            private void setValue(FunctionArgument fa, Struct args, Key name, Object value) {
484                    if(value==null){
485                            Struct meta = fa.getMetaData();
486                            if(meta!=null)value=meta.get(KeyConstants._default,null);
487                    }
488                    args.setEL(name, value);
489            }
490    
491            private void writeOut(PageContext pc, Props props, Object obj, MimeType mt) throws PageException, IOException, ConverterException {
492                    // TODO miemtype mapping with converter defintion from external file
493                    // Images
494                    if(mt.same(MimeType.IMAGE_GIF)) writeOut(pc,obj,mt,new ImageConverter("gif"));
495                    else if(mt.same(MimeType.IMAGE_JPG)) writeOut(pc,obj,mt,new ImageConverter("jpeg"));
496                    else if(mt.same(MimeType.IMAGE_PNG)) writeOut(pc,obj,mt,new ImageConverter("png"));
497                    else if(mt.same(MimeType.IMAGE_TIFF)) writeOut(pc,obj,mt,new ImageConverter("tiff"));
498                    else if(mt.same(MimeType.IMAGE_BMP)) writeOut(pc,obj,mt,new ImageConverter("bmp"));
499                    else if(mt.same(MimeType.IMAGE_WBMP)) writeOut(pc,obj,mt,new ImageConverter("wbmp"));
500                    else if(mt.same(MimeType.IMAGE_FBX)) writeOut(pc,obj,mt,new ImageConverter("fbx"));
501                    else if(mt.same(MimeType.IMAGE_FBX)) writeOut(pc,obj,mt,new ImageConverter("fbx"));
502                    else if(mt.same(MimeType.IMAGE_PNM)) writeOut(pc,obj,mt,new ImageConverter("pnm"));
503                    else if(mt.same(MimeType.IMAGE_PGM)) writeOut(pc,obj,mt,new ImageConverter("pgm"));
504                    else if(mt.same(MimeType.IMAGE_PBM)) writeOut(pc,obj,mt,new ImageConverter("pbm"));
505                    else if(mt.same(MimeType.IMAGE_ICO)) writeOut(pc,obj,mt,new ImageConverter("ico"));
506                    else if(mt.same(MimeType.IMAGE_PSD)) writeOut(pc,obj,mt,new ImageConverter("psd"));
507                    else if(mt.same(MimeType.IMAGE_ASTERIX)) writeOut(pc,obj,MimeType.IMAGE_PNG,new ImageConverter("png"));
508                    
509                    // Application
510                    else if(mt.same(MimeType.APPLICATION_JAVA)) writeOut(pc,obj,mt,new JavaConverter());
511                    //if("application".equalsIgnoreCase(mt.getType()))
512                    
513                    
514                    else pc.forceWrite(convertResult(pc, props, null, obj));
515            }
516    
517            private void writeOut(PageContext pc, Object obj, MimeType mt,BinaryConverter converter) throws ConverterException, IOException {
518                    pc.getResponse().setContentType(mt.toString());
519                    
520                    OutputStream os=null;
521                    try{
522                            converter.writeOut(pc, obj, os=pc.getResponseStream());
523                    }
524                    finally{
525                            IOUtil.closeEL(os);
526                    }
527            }
528    
529            public static  boolean isSoap(PageContext pc) {
530                    HttpServletRequest req = pc.getHttpServletRequest();
531                    InputStream is=null;
532                    try {
533                            is=req.getInputStream();
534                            
535                            String input = IOUtil.toString(is,"iso-8859-1");
536                            return 
537                            StringUtil.indexOfIgnoreCase(input, "soap:Envelope")!=-1 || 
538                            StringUtil.indexOfIgnoreCase(input, "soapenv:Envelope")!=-1 || 
539                                    StringUtil.indexOfIgnoreCase(input, "SOAP-ENV:Envelope")!=-1;
540                    } 
541                    catch (IOException e) {
542                            return false;
543                    }
544                    finally {
545                            IOUtil.closeEL(is);
546                    }
547            }
548            
549            
550        private void callWDDX(PageContext pc, Component component, Collection.Key methodName, boolean suppressContent) throws IOException, ConverterException, PageException {
551            //Struct url = StructUtil.duplicate(pc.urlFormScope(),true);
552            Struct url=StructUtil.merge(new Struct[]{pc.formScope(),pc.urlScope()});
553            // define args
554            url.removeEL(KeyConstants._fieldnames);
555            url.removeEL(METHOD);
556            Object args=url.get(KeyConstants._argumentCollection,null);
557            Object returnFormat=url.get(KeyConstants._returnFormat,null);
558            Object queryFormat=url.get(KeyConstants._queryFormat,null);
559            
560            if(args==null){
561                    args=pc.getHttpServletRequest().getAttribute("argumentCollection");
562            }
563            
564          //content-type
565            Object o = component.get(pc,methodName,null);
566            Props props = getProps(pc, o, returnFormat);
567            if(!props.output) setFormat(pc.getHttpServletResponse(),props.format);
568                    
569            
570            Object rtn=null;
571            try{
572                    if(suppressContent)pc.setSilent();
573            
574                    
575                    if(args==null){
576                            url=translate(component,methodName.getString(),url);
577                            rtn = component.callWithNamedValues(pc, methodName, url);
578                    }
579                    else if(args instanceof String){
580                            try {
581                                            args=new JSONExpressionInterpreter().interpret(pc, (String)args);
582                                            
583                                    } catch (PageException e) {}
584                    }
585                    
586                    // call
587                    if(args!=null) {
588                            if(Decision.isCastableToStruct(args)){
589                                    rtn = component.callWithNamedValues(pc, methodName, Caster.toStruct(args,false));
590                            }
591                            else if(Decision.isCastableToArray(args)){
592                                    rtn = component.call(pc, methodName, Caster.toNativeArray(args));
593                            }
594                            else {
595                                    Object[] ac=new Object[1];
596                                    ac[0]=args;
597                                    rtn = component.call(pc, methodName, ac);
598                            }
599                    }
600            }
601            finally {
602                    if(suppressContent)pc.unsetSilent();
603            }
604            // convert result
605            if(rtn!=null){
606                    if(pc.getHttpServletRequest().getHeader("AMF-Forward")!=null) {
607                            pc.variablesScope().setEL("AMF-Forward", rtn);
608                    }
609                    else {
610                            pc.forceWrite(convertResult(pc, props, queryFormat, rtn));
611                    }
612            }
613            
614        }
615        
616            private void setFormat(HttpServletResponse rsp, int format) {
617            switch(format){
618            case UDF.RETURN_FORMAT_WDDX:
619                    rsp.setContentType("text/xml; charset=UTF-8");
620                    rsp.setHeader("Return-Format", "wddx");
621            break;
622            case UDF.RETURN_FORMAT_JSON:
623                    rsp.setContentType("application/json");
624                    rsp.setHeader("Return-Format", "json");
625            break;
626            case UDF.RETURN_FORMAT_PLAIN:
627                    rsp.setContentType("text/plain; charset=UTF-8");
628                    rsp.setHeader("Return-Format", "plain");
629            break;
630            case UDF.RETURN_FORMAT_XML:
631                    rsp.setContentType("text/xml; charset=UTF-8");
632                    rsp.setHeader("Return-Format", "xml");
633            break;
634            case UDF.RETURN_FORMAT_SERIALIZE:
635                    rsp.setContentType("text/plain; charset=UTF-8");
636                    rsp.setHeader("Return-Format", "cfml");
637            break;
638            }
639            }
640    
641            private static Props getProps(PageContext pc, Object o,Object returnFormat) throws PageException {
642            Props props = new Props();
643            
644                    props.strType="any";
645                    props.secureJson=pc.getApplicationContext().getSecureJson();
646                    if(o instanceof UDF) {
647                            UDF udf = ((UDF)o);
648                            props.format=udf.getReturnFormat();
649                            props.type=udf.getReturnType();
650                            props.strType=udf.getReturnTypeAsString();
651                            props.output=udf.getOutput();
652                            if(udf.getSecureJson()!=null)props.secureJson=udf.getSecureJson().booleanValue();
653                    }
654                    if(!StringUtil.isEmpty(returnFormat)){
655                            props.format=UDFImpl.toReturnFormat(Caster.toString(returnFormat));
656                    }
657            
658                    // return type XML ignore WDDX
659                    if(props.type==CFTypes.TYPE_XML) {
660                            if(UDF.RETURN_FORMAT_WDDX==props.format)
661                                    props.format=UDF.RETURN_FORMAT_PLAIN;
662                    }
663            
664            
665            
666            return props;
667        }
668        
669        public static String convertResult(PageContext pc,Component component, String methodName,Object returnFormat,Object queryFormat,Object rtn) throws ConverterException, PageException {
670            Object o = component.get(KeyImpl.init(methodName),null);
671            Props p = getProps(pc, o, returnFormat);
672            return convertResult(pc, p, queryFormat, rtn);
673        }
674        
675        private static String convertResult(PageContext pc,Props props,Object queryFormat,Object rtn) throws ConverterException, PageException {
676            // return type XML ignore WDDX
677                    if(props.type==CFTypes.TYPE_XML) {
678                            //if(UDF.RETURN_FORMAT_WDDX==format) format=UDF.RETURN_FORMAT_PLAIN;
679                            rtn=Caster.toString(Caster.toXML(rtn));
680                    }
681                    // function does no real cast, only check it
682                    else rtn=Caster.castTo(pc, (short)props.type, props.strType, rtn);
683            
684            // WDDX
685                    if(UDF.RETURN_FORMAT_WDDX==props.format) {
686                            WDDXConverter converter = new WDDXConverter(pc.getTimeZone(),false,false);
687                converter.setTimeZone(pc.getTimeZone());
688                    return converter.serialize(rtn);
689                    }
690                    // JSON
691                    else if(UDF.RETURN_FORMAT_JSON==props.format) {
692                            boolean byColumn = false;
693                    if(queryFormat instanceof String){
694                            String strQF=((String) queryFormat).trim();
695                            if(strQF.equalsIgnoreCase("row"));
696                            else if(strQF.equalsIgnoreCase("column"))byColumn=true;
697                            else throw new ApplicationException("invalid queryformat definition ["+strQF+"], valid formats are [row,column]");
698                    }
699                    JSONConverter converter = new JSONConverter(false);
700                    String prefix="";
701                    if(props.secureJson) {
702                            prefix=pc.getApplicationContext().getSecureJsonPrefix();
703                            if(prefix==null)prefix="";
704                    }
705                return prefix+converter.serialize(pc,rtn,byColumn);
706                    }
707                    // Serialize
708                    else if(UDF.RETURN_FORMAT_SERIALIZE==props.format) {
709                            ScriptConverter converter = new ScriptConverter(false);
710                            return converter.serialize(rtn);
711                    }
712            // XML
713                    if(UDF.RETURN_FORMAT_XML==props.format) {
714                            XMLConverter converter = new XMLConverter(pc.getTimeZone(),false);
715                converter.setTimeZone(pc.getTimeZone());
716                    return converter.serialize(rtn);
717                    }
718                    // Plain
719                    else if(UDF.RETURN_FORMAT_PLAIN==props.format) {
720                    return Caster.toString(rtn);
721                    }
722                    return null;
723            }
724    
725            public static Struct translate(Component c, String strMethodName, Struct params) {
726                    Collection.Key methodName=KeyImpl.init(strMethodName);
727                    Key[] keys = CollectionUtil.keys(params);
728                    FunctionArgument[] args=null;
729                    int index=-1;
730                    Object value;
731            for(int i=0;i<keys.length;i++){
732                    index=Caster.toIntValue(keys[i].getString(),0);
733                    if(index>0)  {
734                            if(args==null)args=_getArgs(c,methodName);
735                            if(args!=null && index<=args.length) {
736                                    value=params.removeEL(keys[i]);
737                                    if(value!=null)params.setEL(args[index-1].getName(), value);
738                            }
739                    }
740                    
741            }
742            return params;
743            }
744    
745            private static FunctionArgument[] _getArgs(Component c, Collection.Key methodName) {
746                    Object o=c.get(methodName,null);
747                    if(o instanceof UDF) return ((UDF) o).getFunctionArguments();
748                    return null;
749            }
750    
751            private void callWSDL(PageContext pc, Component component) throws ServletException, IOException, ExpressionException {
752            // take wsdl file defined by user
753            String wsdl = component.getWSDLFile();
754            if(!StringUtil.isEmpty(wsdl)) {
755                    
756                    OutputStream os=null;
757                    Resource input = ResourceUtil.toResourceExisting(pc, wsdl);
758                    try {
759                            os=pc.getResponseStream();
760                                    pc.getResponse().setContentType("text/xml; charset=utf-8");
761                            IOUtil.copy(input, os, false);
762                            
763                    }
764                    finally {
765                            IOUtil.flushEL(os);
766                    IOUtil.closeEL(os);
767                    ((PageContextImpl)pc).getRootOut().setClosed(true);
768                    }
769            }
770            // create a wsdl file
771            else {
772                    RPCServer.getInstance(pc.getId(),pc.getServletContext())
773                            .doGet(pc.getHttpServletRequest(), pc. getHttpServletResponse(), component);
774            }
775        }
776        
777        private void callWebservice(PageContext pc, Component component) throws IOException, ServletException {
778            ComponentController.set(pc, component);
779            try {
780                    RPCServer.getInstance(pc.getId(),pc.getServletContext())
781                            .doPost(pc.getHttpServletRequest(), pc. getHttpServletResponse(), component);
782            }
783            finally {
784                    ComponentController.release();
785            }
786        }
787        
788    
789        public abstract void initComponent(PageContext pc,ComponentImpl c) throws PageException;
790    
791            public void ckecked() {
792                    lastCheck=System.currentTimeMillis();
793            }
794    
795            public long lastCheck() {
796                    return lastCheck;
797            }
798            
799    }
800            class Props {
801    
802                    public String strType="any";
803                    public boolean secureJson;
804                    public int type=CFTypes.TYPE_ANY;
805                    public int format=UDF.RETURN_FORMAT_WDDX;
806                    public boolean output=true;
807                    
808            }