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