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    }