001    package railo.runtime.gateway;
002    
003    import java.io.IOException;
004    import java.util.HashMap;
005    import java.util.Iterator;
006    import java.util.Map;
007    import java.util.Map.Entry;
008    
009    import javax.servlet.http.HttpServletRequest;
010    
011    import org.opencfml.eventgateway.Gateway;
012    import org.opencfml.eventgateway.GatewayEngine;
013    import org.opencfml.eventgateway.GatewayException;
014    
015    import railo.commons.io.DevNullOutputStream;
016    import railo.commons.io.log.Log;
017    import railo.commons.lang.ClassException;
018    import railo.commons.lang.Md5;
019    import railo.commons.lang.Pair;
020    import railo.loader.util.Util;
021    import railo.runtime.CFMLFactory;
022    import railo.runtime.Component;
023    import railo.runtime.ComponentPage;
024    import railo.runtime.PageContext;
025    import railo.runtime.PageContextImpl;
026    import railo.runtime.config.Config;
027    import railo.runtime.config.ConfigWeb;
028    import railo.runtime.config.ConfigWebImpl;
029    import railo.runtime.engine.ThreadLocalPageContext;
030    import railo.runtime.exp.ExpressionException;
031    import railo.runtime.exp.PageException;
032    import railo.runtime.op.Caster;
033    import railo.runtime.thread.ThreadUtil;
034    import railo.runtime.type.Collection;
035    import railo.runtime.type.KeyImpl;
036    import railo.runtime.type.Struct;
037    import railo.runtime.type.StructImpl;
038    
039    public class GatewayEngineImpl implements GatewayEngine {
040    
041            private static final Object OBJ = new Object();
042    
043            private static final Collection.Key AMF_FORWARD = KeyImpl.init("AMF-Forward");
044    
045            private Map<String,GatewayEntry> entries=new HashMap<String,GatewayEntry>();
046            private ConfigWeb config;
047            private Log log;
048    
049            
050            public GatewayEngineImpl(ConfigWeb config){
051                    this.config=config;
052                    this.log=((ConfigWebImpl)config).getGatewayLogger();
053                    
054            }
055            
056            public void addEntries(Config config,Map<String, GatewayEntry> entries) throws ClassException, PageException,GatewayException {
057                    Iterator<Entry<String, GatewayEntry>> it = entries.entrySet().iterator();
058                    while(it.hasNext()){
059                            addEntry(config,it.next().getValue());
060                    }
061            }
062    
063            public void addEntry(Config config,GatewayEntry ge) throws ClassException, PageException,GatewayException {
064                    String id=ge.getId().toLowerCase().trim();
065                    GatewayEntry existing=(GatewayEntry) entries.get(id);
066                    Gateway g=null;
067                    
068                    // does not exist
069                    if(existing==null) {
070                            entries.put(id,load(config,ge));
071                    }
072                    // exist but changed
073                    else if(!existing.equals(ge)){
074                            g=existing.getGateway();
075                            if(g.getState()==Gateway.RUNNING) g.doStop();
076                            entries.put(id,load(config,ge));
077                    }
078                    // not changed
079                    //else print.out("untouched:"+id);
080            }
081    
082            private GatewayEntry load(Config config,GatewayEntry ge) throws ClassException,PageException {
083                    ge.createGateway(config);
084                    return ge;
085            }
086    
087            /**
088             * @return the entries
089             */
090            public Map<String,GatewayEntry> getEntries() {
091                    return entries;
092            }
093    
094            public void remove(GatewayEntry ge) throws GatewayException {
095                    String id=ge.getId().toLowerCase().trim();
096                    GatewayEntry existing=(GatewayEntry) entries.remove(id);
097                    Gateway g=null;
098                    
099                    // does not exist
100                    if(existing!=null) {
101                            g=existing.getGateway();
102                            try{
103                                    if(g.getState()==Gateway.RUNNING) g.doStop();
104                            }
105                            catch(Throwable t){}
106                    }
107            }
108    
109            /**
110             * get the state of gateway
111             * @param gatewayId
112             * @return
113             * @throws PageException
114             */
115            public int getState(String gatewayId) throws PageException {
116                    return getGateway(gatewayId).getState();
117            }
118            
119            /**
120             * get helper object
121             * @param gatewayId
122             * @return
123             * @throws PageException
124             */
125            public Object getHelper(String gatewayId) throws PageException {
126                    return getGateway(gatewayId).getHelper();
127            }
128    
129            /**
130             * send the message to the gateway
131             * @param gatewayId
132             * @param data
133             * @return
134             * @throws PageException
135             */
136            public String sendMessage(String gatewayId, Struct data) throws PageException,GatewayException {
137                    Gateway g = getGateway(gatewayId);
138                    if(g.getState()!=Gateway.RUNNING) throw new GatewayException("Gateway ["+gatewayId+"] is not running");
139                    return g.sendMessage(data);
140            }
141            
142            /**
143             * start the gateway
144             * @param gatewayId
145             * @throws PageException
146             */
147            public void start(String gatewayId) throws PageException {
148                    executeThread(gatewayId,GatewayThread.START);
149            }
150            private void start(Gateway gateway) throws PageException {
151                    executeThread(gateway,GatewayThread.START);
152            }
153    
154            /**
155             * stop the gateway
156             * @param gatewayId
157             * @throws PageException
158             */
159            public void stop(String gatewayId) throws PageException {
160                    executeThread(gatewayId,GatewayThread.STOP);
161            }
162            private void stop(Gateway gateway) throws PageException {
163                    executeThread(gateway,GatewayThread.STOP);
164            }
165            
166            
167    
168    
169            public void reset() throws PageException {
170                    Iterator<Entry<String, GatewayEntry>> it = entries.entrySet().iterator();
171                    Entry<String, GatewayEntry> entry;
172                    GatewayEntry ge;
173                    Gateway g;
174                    while(it.hasNext()){
175                            entry = it.next();
176                            ge = entry.getValue();
177                            g=ge.getGateway();
178                            if(g.getState()==Gateway.RUNNING) {
179                                    try {
180                                            g.doStop();
181                                    } catch (GatewayException e) {
182                                            log(g, LOGLEVEL_ERROR, e.getMessage());
183                                    }
184                            }
185                            if(ge.getStartupMode()==GatewayEntry.STARTUP_MODE_AUTOMATIC)
186                                    start(g);
187                            
188                    }
189            }
190    
191            public synchronized void clear() throws GatewayException, PageException {
192                    Iterator<Entry<String, GatewayEntry>> it = entries.entrySet().iterator();
193                    Entry<String, GatewayEntry> entry;
194                    while(it.hasNext()){
195                            entry = it.next();
196                            if(entry.getValue().getGateway().getState()==Gateway.RUNNING) 
197                                    stop(entry.getValue().getGateway());
198                    }
199                    entries.clear();
200            }
201            
202            /**
203             * restart the gateway
204             * @param gatewayId
205             * @throws PageException
206             */
207            public void restart(String gatewayId) throws PageException {
208                    executeThread(gatewayId,GatewayThread.RESTART);
209            }
210            
211            private Gateway getGateway(String gatewayId) throws PageException {
212                    return getGatewayEntry(gatewayId).getGateway();
213            }
214            
215            private GatewayEntry getGatewayEntry(String gatewayId) throws PageException {
216                    String id=gatewayId.toLowerCase().trim();
217                    
218                    GatewayEntry ge=(GatewayEntry) entries.get(id);
219                    if(ge!=null) return ge;
220                    
221                    // create list
222                    Iterator<String> it = entries.keySet().iterator();
223                    StringBuilder sb=new StringBuilder();
224                    while(it.hasNext()){
225                            if(sb.length()>0) sb.append(", ");
226                            sb.append(it.next());
227                    }
228                    
229                    throw new ExpressionException("there is no gateway instance with id ["+gatewayId+"], available gateway instances are ["+sb+"]");
230            }
231            private GatewayEntry getGatewayEntry(Gateway gateway)  {
232                    String gatewayId=gateway.getId();
233                    // it must exist, because it only can come from here
234                    return (GatewayEntry) entries.get(gatewayId);
235            }
236            
237            private void executeThread(String gatewayId, int action) throws PageException {
238                    new GatewayThread(this,getGateway(gatewayId),action).start();
239            }
240    
241            private void executeThread(Gateway g, int action) throws PageException {
242                    new GatewayThread(this,g,action).start();
243            }
244            
245            
246            
247    
248            public static int toIntState(String state, int defaultValue) {
249                    state=state.trim().toLowerCase();
250                    if("running".equals(state))     return Gateway.RUNNING;
251                    if("started".equals(state)) return Gateway.RUNNING;
252                    if("run".equals(state))         return Gateway.RUNNING;
253                    
254                    if("failed".equals(state))      return Gateway.FAILED;
255                    if("starting".equals(state))return Gateway.STARTING;
256                    if("stopped".equals(state)) return Gateway.STOPPED;
257                    if("stopping".equals(state))return Gateway.STOPPING;
258                    
259                    return defaultValue;
260            }
261            
262            public static String toStringState(int state, String defaultValue) {
263                    if(Gateway.RUNNING==state)      return "running";
264                    if(Gateway.FAILED==state)       return "failed";
265                    if(Gateway.STOPPED==state)      return "stopped";
266                    if(Gateway.STOPPING==state)     return "stopping";
267                    if(Gateway.STARTING==state)     return "starting";
268                            
269                    return defaultValue;
270            }
271    
272            public boolean invokeListener(Gateway gateway, String method, Map data) {// FUTUTE add generic type to interface
273                    data=GatewayUtil.toCFML(data);
274                    
275                    GatewayEntry entry = getGatewayEntry(gateway);
276                    String cfcPath = entry.getListenerCfcPath();
277                    if(!Util.isEmpty(cfcPath,true)){
278                            try {
279                                    if(!callOneWay(cfcPath,gateway.getId(), method, Caster.toStruct(data,null,false), false))
280                                            log(gateway,LOGLEVEL_ERROR, "function ["+method+"] does not exist in cfc ["+toRequestURI(cfcPath)+"]");
281                                    else
282                                            return true;
283                            } 
284                            catch (PageException e) {
285                                    e.printStackTrace();
286                                    log(gateway,LOGLEVEL_ERROR, e.getMessage());
287                            }
288                    }
289                    else
290                            log(gateway,LOGLEVEL_ERROR, "there is no listener cfc defined");
291                    return false;
292            }
293            
294            public Object callEL(String cfcPath,String id,String functionName,Struct arguments, boolean cfcPeristent, Object defaultValue)  {
295                    try {
296                            return call(cfcPath,id,functionName, arguments,cfcPeristent, defaultValue);
297                    } catch (PageException e) {
298                            return defaultValue;
299                    }
300            }
301            
302            public boolean callOneWay(String cfcPath,String id,String functionName,Struct arguments, boolean cfcPeristent) throws PageException {
303                    return call(cfcPath,id,functionName, arguments,cfcPeristent, OBJ)!=OBJ;
304            }
305    
306            public Object getComponent(String cfcPath,String id) throws PageException  {
307                    String requestURI=toRequestURI(cfcPath);
308                    
309                    PageContext oldPC = ThreadLocalPageContext.get();
310                    PageContextImpl pc = createPageContext(requestURI,id, "init", null, false);
311                    try {
312                            ThreadLocalPageContext.register(pc);
313                            return getCFC(pc,requestURI);
314                    }
315                    finally{
316                            pc.setGatewayContext(false);
317                            CFMLFactory f = config.getFactory();
318                            f.releasePageContext(pc);
319                            ThreadLocalPageContext.register(oldPC);
320                    }
321            }
322    
323            public Object call(String cfcPath,String id,String functionName,Struct arguments, boolean cfcPeristent, Object defaultValue) throws PageException  {
324                    String requestURI=toRequestURI(cfcPath);
325                    
326                    PageContext oldPC = ThreadLocalPageContext.get();
327                    PageContextImpl pc=createPageContext(requestURI,id,functionName,arguments,cfcPeristent);
328                    
329                    try {
330                            ThreadLocalPageContext.register(pc);
331                            Component cfc=getCFC(pc,requestURI);
332                            if(cfc.containsKey(functionName)){
333                                    pc.execute(requestURI, true,false);
334                                    // Result
335                                    return pc.variablesScope().get(AMF_FORWARD,null);
336                            }
337                    }
338                    finally{
339                            pc.setGatewayContext(false);
340                            CFMLFactory f = config.getFactory();
341                            f.releasePageContext(pc);
342                            ThreadLocalPageContext.register(oldPC);
343                    }
344                    return defaultValue;
345            }
346    
347            private Component getCFC(PageContextImpl pc,String requestURI) throws PageException  {
348                    HttpServletRequest req = pc.getHttpServletRequest();
349                    try {
350                            req.setAttribute("client", "railo-gateway-1-0");
351                            req.setAttribute("call-type", "store-only");
352                            pc.execute(requestURI, true,false);
353                            return (Component) req.getAttribute("component");
354                    }
355                    finally {
356                            req.removeAttribute("call-type");
357                            req.removeAttribute("component");
358                    }
359            }
360            
361            private PageContextImpl createPageContext(String requestURI,String id,String functionName, Struct arguments, boolean cfcPeristent) throws PageException {
362                    Struct attrs=new StructImpl();
363                    String remotePersisId;
364                    try {
365                            remotePersisId=Md5.getDigestAsString(requestURI+id);
366                    } catch (IOException e) {
367                            throw Caster.toPageException(e);
368                    }
369                    PageContextImpl pc = ThreadUtil.createPageContext(
370                                    config, 
371                                    DevNullOutputStream.DEV_NULL_OUTPUT_STREAM, 
372                                    "localhost", 
373                                    requestURI, 
374                                    "method="+functionName+(cfcPeristent?"&"+ComponentPage.REMOTE_PERSISTENT_ID+"="+remotePersisId:""), 
375                                    null, 
376                                    new Pair[]{new Pair("AMF-Forward","true")}, 
377                                    null, 
378                                    attrs);
379                    
380                    pc.setRequestTimeout(999999999999999999L); 
381                    pc.setGatewayContext(true);
382                    if(arguments!=null)attrs.setEL(KeyImpl.ARGUMENT_COLLECTION, arguments);
383                    attrs.setEL("client", "railo-gateway-1-0");
384                    return pc;
385            }
386    
387            /**
388             * @see org.opencfml.eventgateway.GatewayEngine#toRequestURI(java.lang.String)
389             */
390            public String toRequestURI(String cfcPath) {
391                    return GatewayUtil.toRequestURI(cfcPath);
392            }
393    
394            public void log(Gateway gateway, int level, String message) {
395                    int l=level;
396                    switch(level){
397                    case LOGLEVEL_INFO:l=Log.LEVEL_INFO;
398                    break;
399                    case LOGLEVEL_DEBUG:l=Log.LEVEL_DEBUG;
400                    break;
401                    case LOGLEVEL_ERROR:l=Log.LEVEL_ERROR;
402                    break;
403                    case LOGLEVEL_FATAL:l=Log.LEVEL_FATAL;
404                    break;
405                    case LOGLEVEL_WARN:l=Log.LEVEL_WARN;
406                    break;
407                    }
408                    log.log(l, "Gateway:"+gateway.getId(), message);
409            }
410            
411    
412            private Map<String, Component> persistentRemoteCFC;
413            public Component getPersistentRemoteCFC(String id) {
414                    if(persistentRemoteCFC==null) persistentRemoteCFC=new HashMap<String,Component>();
415                    return persistentRemoteCFC.get(id);
416            }
417            
418            public Component setPersistentRemoteCFC(String id, Component cfc) {
419                    if(persistentRemoteCFC==null) persistentRemoteCFC=new HashMap<String,Component>();
420                    return persistentRemoteCFC.put(id,cfc);
421            }
422    }
423