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