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