001 package railo.runtime.tag; 002 003 import java.util.Iterator; 004 005 import railo.print; 006 import railo.commons.io.SystemUtil; 007 import railo.commons.lang.StringUtil; 008 import railo.runtime.Page; 009 import railo.runtime.PageContext; 010 import railo.runtime.PageContextImpl; 011 import railo.runtime.config.ConfigImpl; 012 import railo.runtime.exp.ApplicationException; 013 import railo.runtime.exp.ExpressionException; 014 import railo.runtime.exp.PageException; 015 import railo.runtime.exp.SecurityException; 016 import railo.runtime.ext.tag.BodyTagImpl; 017 import railo.runtime.ext.tag.DynamicAttributes; 018 import railo.runtime.op.Caster; 019 import railo.runtime.spooler.ExecutionPlan; 020 import railo.runtime.spooler.ExecutionPlanImpl; 021 import railo.runtime.thread.ChildSpoolerTask; 022 import railo.runtime.thread.ChildThread; 023 import railo.runtime.thread.ChildThreadImpl; 024 import railo.runtime.thread.ThreadUtil; 025 import railo.runtime.thread.ThreadsImpl; 026 import railo.runtime.type.Array; 027 import railo.runtime.type.Collection; 028 import railo.runtime.type.Collection.Key; 029 import railo.runtime.type.KeyImpl; 030 import railo.runtime.type.Struct; 031 import railo.runtime.type.StructImpl; 032 import railo.runtime.type.scope.Threads; 033 import railo.runtime.type.util.ListUtil; 034 035 // MUST change behavor of mltiple headers now is a array, it das so? 036 037 /** 038 * Lets you execute HTTP POST and GET operations on files. Using cfhttp, you can execute standard 039 * GET operations and create a query object from a text file. POST operations lets you upload MIME file 040 * types to a server, or post cookie, formfield, URL, file, or CGI variables directly to a specified server. 041 * 042 * 043 * 044 * 045 **/ 046 public final class ThreadTag extends BodyTagImpl implements DynamicAttributes { 047 048 private static final int ACTION_JOIN = 0; 049 private static final int ACTION_RUN = 1; 050 private static final int ACTION_SLEEP = 2; 051 private static final int ACTION_TERMINATE = 3; 052 053 private static final int TYPE_DEAMON = 0; 054 private static final int TYPE_TASK = 1; 055 private static final ExecutionPlan[] EXECUTION_PLAN = new ExecutionPlan[0]; 056 057 private int action=ACTION_RUN; 058 private long duration=-1; 059 private String name; 060 private String lcName; 061 private int priority=Thread.NORM_PRIORITY; 062 private long timeout=0; 063 private PageContext pc; 064 private int type=TYPE_DEAMON; 065 private ExecutionPlan[] plans=EXECUTION_PLAN; 066 private Struct attrs; 067 068 069 @Override 070 public void release() { 071 super.release(); 072 action=ACTION_RUN; 073 duration=-1; 074 name=null; 075 lcName=null; 076 priority=Thread.NORM_PRIORITY; 077 type=TYPE_DEAMON; 078 plans=EXECUTION_PLAN; 079 timeout=0; 080 attrs=null; 081 pc=null; 082 } 083 084 /** 085 * @param action the action to set 086 */ 087 public void setAction(String strAction) throws ApplicationException { 088 String lcAction = strAction.trim().toLowerCase(); 089 090 if("join".equals(lcAction)) this.action=ACTION_JOIN; 091 else if("run".equals(lcAction)) this.action=ACTION_RUN; 092 else if("sleep".equals(lcAction)) this.action=ACTION_SLEEP; 093 else if("terminate".equals(lcAction)) this.action=ACTION_TERMINATE; 094 else 095 throw new ApplicationException("invalid value ["+strAction+"] for attribute action","values for attribute action are:join,run,sleep,terminate"); 096 } 097 098 099 /** 100 * @param duration the duration to set 101 */ 102 public void setDuration(double duration) { 103 this.duration = (long) duration; 104 } 105 106 107 /** 108 * @param name the name to set 109 */ 110 public void setName(String name) { 111 if(StringUtil.isEmpty(name,true)) return; 112 this.name = name.trim(); 113 this.lcName=this.name.toLowerCase(); 114 } 115 116 117 /** 118 * @param strPriority the priority to set 119 */ 120 public void setPriority(String strPriority) throws ApplicationException { 121 int p = ThreadUtil.toIntPriority(strPriority); 122 if(p==-1) { 123 throw new ApplicationException("invalid value ["+strPriority+"] for attribute priority","values for attribute priority are:low,high,normal"); 124 } 125 priority=p; 126 } 127 128 129 /** 130 * @param strType the type to set 131 * @throws ApplicationException 132 * @throws SecurityException 133 */ 134 public void setType(String strType) throws ApplicationException, SecurityException { 135 strType=strType.trim().toLowerCase(); 136 137 if("task".equals(strType)) { 138 // SNSN 139 /*SerialNumber sn = pageContext.getConfig().getSerialNumber(); 140 if(sn.getVersion()==SerialNumber.VERSION_COMMUNITY) 141 throw new SecurityException("no access to this functionality with the "+sn.getStringVersion()+" version of railo"); 142 */ 143 144 145 //throw new ApplicationException("invalid value ["+strType+"] for attribute type","task is not supported at the moment"); 146 type=TYPE_TASK; 147 } 148 else if("deamon".equals(strType)) type=TYPE_DEAMON; 149 else throw new ApplicationException("invalid value ["+strType+"] for attribute type","values for attribute type are:task,deamon (default)"); 150 151 } 152 153 public void setRetryintervall(Object obj) throws PageException { 154 setRetryinterval(obj); 155 } 156 157 public void setRetryinterval(Object obj) throws PageException { 158 if(StringUtil.isEmpty(obj))return; 159 Array arr = Caster.toArray(obj,null); 160 if(arr==null){ 161 plans=new ExecutionPlan[]{toExecutionPlan(obj,1)}; 162 } 163 else { 164 Iterator<Object> it = arr.valueIterator(); 165 plans=new ExecutionPlan[arr.size()]; 166 int index=0; 167 while(it.hasNext()) { 168 plans[index++]=toExecutionPlan(it.next(),index==1?1:0); 169 } 170 } 171 172 } 173 174 175 176 177 178 private ExecutionPlan toExecutionPlan(Object obj,int plus) throws PageException { 179 180 if(obj instanceof Struct){ 181 Struct sct=(Struct)obj; 182 // GERT 183 184 // tries 185 Object oTries=sct.get("tries",null); 186 if(oTries==null)throw new ExpressionException("missing key tries inside struct"); 187 int tries=Caster.toIntValue(oTries); 188 if(tries<0)throw new ExpressionException("tries must contain a none negative value"); 189 190 // interval 191 Object oInterval=sct.get("interval",null); 192 if(oInterval==null)oInterval=sct.get("intervall",null); 193 194 if(oInterval==null)throw new ExpressionException("missing key interval inside struct"); 195 int interval=toSeconds(oInterval); 196 if(interval<0)throw new ExpressionException("interval should contain a positive value or 0"); 197 198 199 return new ExecutionPlanImpl(tries+plus,interval); 200 } 201 return new ExecutionPlanImpl(1+plus,toSeconds(obj)); 202 } 203 204 private int toSeconds(Object obj) throws PageException { 205 return (int)Caster.toTimespan(obj).getSeconds(); 206 } 207 /** 208 * @param timeout the timeout to set 209 */ 210 public void setTimeout(double timeout) { 211 this.timeout = (long)timeout; 212 } 213 214 @Override 215 public void setDynamicAttribute(String uri, String name, Object value) { 216 if(attrs==null)attrs=new StructImpl(); 217 Key key = KeyImpl.getInstance(StringUtil.trim(name,"")); 218 attrs.setEL(key,value); 219 } 220 221 @Override 222 public void setDynamicAttribute(String uri, Collection.Key name, Object value) { 223 if(attrs==null)attrs=new StructImpl(); 224 Key key = KeyImpl.getInstance(StringUtil.trim(name.getString(),"")); 225 attrs.setEL(key,value); 226 } 227 228 @Override 229 public int doStartTag() throws PageException { 230 pc=pageContext; 231 switch(action) { 232 case ACTION_JOIN: 233 doJoin(); 234 break; 235 case ACTION_SLEEP: 236 required("thread", "sleep", "duration", duration,-1); 237 doSleep(); 238 break; 239 case ACTION_TERMINATE: 240 required("thread", "terminate", "name", name); 241 doTerminate(); 242 break; 243 case ACTION_RUN: 244 required("thread", "run", "name", name); 245 return EVAL_BODY_INCLUDE; 246 247 } 248 return SKIP_BODY; 249 } 250 251 @Override 252 public int doEndTag() throws PageException { 253 this.pc=pageContext; 254 //if(ACTION_RUN==action) doRun(); 255 return EVAL_PAGE; 256 } 257 258 public void register(Page currentPage, int threadIndex) throws PageException { 259 if(ACTION_RUN!=action) return; 260 261 if(((PageContextImpl)pc).getParentPageContext()!=null) 262 throw new ApplicationException("could not create a thread within a child thread"); 263 264 try { 265 Threads ts = pc.getThreadScope(lcName); 266 267 if(type==TYPE_DEAMON){ 268 if(ts!=null) 269 throw new ApplicationException("could not create a thread with the name ["+name+"]. name must be unique within a request"); 270 ChildThreadImpl ct = new ChildThreadImpl((PageContextImpl) pc,currentPage,name,threadIndex,attrs,false); 271 pc.setThreadScope(name,new ThreadsImpl(ct)); 272 ct.setPriority(priority); 273 ct.setDaemon(false); 274 ct.start(); 275 } 276 else { 277 ChildThreadImpl ct = new ChildThreadImpl((PageContextImpl) pc,currentPage,name,threadIndex,attrs,true); 278 ct.setPriority(priority); 279 ((ConfigImpl)pc.getConfig()).getSpoolerEngine().add(new ChildSpoolerTask(ct,plans)); 280 } 281 282 } 283 catch (Throwable t) { 284 throw Caster.toPageException(t); 285 } 286 finally { 287 pc.reuse(this);// this method is not called from template when type is run, a call from template is to early, 288 } 289 } 290 291 private void doSleep() throws ExpressionException { 292 if(duration>=0) { 293 SystemUtil.sleep(duration); 294 } 295 else throw new ExpressionException("The attribute duration must be greater or equal than 0, now ["+duration+"]"); 296 297 } 298 299 private void doJoin() throws ApplicationException { 300 PageContextImpl mpc=(PageContextImpl)getMainPageContext(pc); 301 302 String[] names; 303 if(lcName==null) { 304 names=mpc.getThreadScopeNames(); 305 } 306 else names=ListUtil.listToStringArray(lcName, ','); 307 308 ChildThread ct; 309 Threads ts; 310 long start=System.currentTimeMillis(),_timeout=timeout>0?timeout:-1; 311 312 for(int i=0;i<names.length;i++) { 313 if(StringUtil.isEmpty(names[i],true))continue; 314 //PageContextImpl mpc=(PageContextImpl)getMainPageContext(pc); 315 ts = mpc.getThreadScope(names[i]); 316 if(ts==null) 317 throw new ApplicationException("there is no thread running with the name ["+names[i]+"], only the following threads existing ["+ListUtil.arrayToList(mpc.getThreadScopeNames(),", ")+"]"); 318 ct=ts.getChildThread(); 319 320 if(ct.isAlive()) { 321 try { 322 if(_timeout!=-1)ct.join(_timeout); 323 else ct.join(); 324 } 325 catch (InterruptedException e) {} 326 } 327 if(_timeout!=-1){ 328 _timeout=_timeout-(System.currentTimeMillis()-start); 329 if(_timeout<1) break; 330 } 331 } 332 333 } 334 private void doTerminate() throws ApplicationException { 335 PageContextImpl mpc=(PageContextImpl)getMainPageContext(pc); 336 337 Threads ts = mpc.getThreadScope(lcName); 338 339 if(ts==null) 340 throw new ApplicationException("there is no thread running with the name ["+name+"]"); 341 ChildThread ct = ts.getChildThread(); 342 343 if(ct.isAlive()){ 344 ct.terminated(); 345 ct.stop(); 346 } 347 348 } 349 350 private PageContext getMainPageContext(PageContext pc) { 351 if(pc==null)pc=pageContext; 352 if(pc.getParentPageContext()==null) return pc; 353 return pc.getParentPageContext(); 354 } 355 356 @Override 357 public void doInitBody() { 358 359 } 360 361 @Override 362 public int doAfterBody() { 363 return SKIP_BODY; 364 } 365 366 /** 367 * sets if has body or not 368 * @param hasBody 369 */ 370 public void hasBody(boolean hasBody) { 371 372 } 373 }