package railo.runtime.tag;
import java.util.Iterator;
import railo.commons.io.SystemUtil;
import railo.commons.lang.StringUtil;
import railo.runtime.Page;
import railo.runtime.PageContext;
import railo.runtime.PageContextImpl;
import railo.runtime.config.ConfigImpl;
import railo.runtime.exp.ApplicationException;
import railo.runtime.exp.ExpressionException;
import railo.runtime.exp.PageException;
import railo.runtime.exp.SecurityException;
import railo.runtime.ext.tag.BodyTagImpl;
import railo.runtime.ext.tag.DynamicAttributes;
import railo.runtime.op.Caster;
import railo.runtime.spooler.ExecutionPlan;
import railo.runtime.spooler.ExecutionPlanImpl;
import railo.runtime.thread.ChildSpoolerTask;
import railo.runtime.thread.ChildThread;
import railo.runtime.thread.ChildThreadImpl;
import railo.runtime.thread.ThreadUtil;
import railo.runtime.thread.ThreadsImpl;
import railo.runtime.type.Array;
import railo.runtime.type.Collection;
import railo.runtime.type.Collection.Key;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.Struct;
import railo.runtime.type.StructImpl;
import railo.runtime.type.scope.Threads;
import railo.runtime.type.util.ListUtil;
// MUST change behavor of mltiple headers now is a array, it das so?
/**
* Lets you execute HTTP POST and GET operations on files. Using cfhttp, you can execute standard
* GET operations and create a query object from a text file. POST operations lets you upload MIME file
* types to a server, or post cookie, formfield, URL, file, or CGI variables directly to a specified server.
*
*
*
*
**/
public final class ThreadTag extends BodyTagImpl implements DynamicAttributes {
private static final int ACTION_JOIN = 0;
private static final int ACTION_RUN = 1;
private static final int ACTION_SLEEP = 2;
private static final int ACTION_TERMINATE = 3;
private static final int TYPE_DEAMON = 0;
private static final int TYPE_TASK = 1;
private static final ExecutionPlan[] EXECUTION_PLAN = new ExecutionPlan[0];
private int action=ACTION_RUN;
private long duration=-1;
private String name;
private String lcName;
private int priority=Thread.NORM_PRIORITY;
private long timeout=0;
private PageContext pc;
private int type=TYPE_DEAMON;
private ExecutionPlan[] plans=EXECUTION_PLAN;
private Struct attrs;
@Override
public void release() {
super.release();
action=ACTION_RUN;
duration=-1;
name=null;
lcName=null;
priority=Thread.NORM_PRIORITY;
type=TYPE_DEAMON;
plans=EXECUTION_PLAN;
timeout=0;
attrs=null;
pc=null;
}
/**
* @param action the action to set
*/
public void setAction(String strAction) throws ApplicationException {
String lcAction = strAction.trim().toLowerCase();
if("join".equals(lcAction)) this.action=ACTION_JOIN;
else if("run".equals(lcAction)) this.action=ACTION_RUN;
else if("sleep".equals(lcAction)) this.action=ACTION_SLEEP;
else if("terminate".equals(lcAction)) this.action=ACTION_TERMINATE;
else
throw new ApplicationException("invalid value ["+strAction+"] for attribute action","values for attribute action are:join,run,sleep,terminate");
}
/**
* @param duration the duration to set
*/
public void setDuration(double duration) {
this.duration = (long) duration;
}
/**
* @param name the name to set
*/
public void setName(String name) {
if(StringUtil.isEmpty(name,true)) return;
this.name = name.trim();
this.lcName=this.name.toLowerCase();
}
/**
* @param strPriority the priority to set
*/
public void setPriority(String strPriority) throws ApplicationException {
int p = ThreadUtil.toIntPriority(strPriority);
if(p==-1) {
throw new ApplicationException("invalid value ["+strPriority+"] for attribute priority","values for attribute priority are:low,high,normal");
}
priority=p;
}
/**
* @param strType the type to set
* @throws ApplicationException
* @throws SecurityException
*/
public void setType(String strType) throws ApplicationException, SecurityException {
strType=strType.trim().toLowerCase();
if("task".equals(strType)) {
// SNSN
/*SerialNumber sn = pageContext.getConfig().getSerialNumber();
if(sn.getVersion()==SerialNumber.VERSION_COMMUNITY)
throw new SecurityException("no access to this functionality with the "+sn.getStringVersion()+" version of railo");
*/
//throw new ApplicationException("invalid value ["+strType+"] for attribute type","task is not supported at the moment");
type=TYPE_TASK;
}
else if("deamon".equals(strType)) type=TYPE_DEAMON;
else throw new ApplicationException("invalid value ["+strType+"] for attribute type","values for attribute type are:task,deamon (default)");
}
public void setRetryintervall(Object obj) throws PageException {
setRetryinterval(obj);
}
public void setRetryinterval(Object obj) throws PageException {
if(StringUtil.isEmpty(obj))return;
Array arr = Caster.toArray(obj,null);
if(arr==null){
plans=new ExecutionPlan[]{toExecutionPlan(obj,1)};
}
else {
Iterator<Object> it = arr.valueIterator();
plans=new ExecutionPlan[arr.size()];
int index=0;
while(it.hasNext()) {
plans[index++]=toExecutionPlan(it.next(),index==1?1:0);
}
}
}
private ExecutionPlan toExecutionPlan(Object obj,int plus) throws PageException {
if(obj instanceof Struct){
Struct sct=(Struct)obj;
// GERT
// tries
Object oTries=sct.get("tries",null);
if(oTries==null)throw new ExpressionException("missing key tries inside struct");
int tries=Caster.toIntValue(oTries);
if(tries<0)throw new ExpressionException("tries must contain a none negative value");
// interval
Object oInterval=sct.get("interval",null);
if(oInterval==null)oInterval=sct.get("intervall",null);
if(oInterval==null)throw new ExpressionException("missing key interval inside struct");
int interval=toSeconds(oInterval);
if(interval<0)throw new ExpressionException("interval should contain a positive value or 0");
return new ExecutionPlanImpl(tries+plus,interval);
}
return new ExecutionPlanImpl(1+plus,toSeconds(obj));
}
private int toSeconds(Object obj) throws PageException {
return (int)Caster.toTimespan(obj).getSeconds();
}
/**
* @param timeout the timeout to set
*/
public void setTimeout(double timeout) {
this.timeout = (long)timeout;
}
@Override
public void setDynamicAttribute(String uri, String name, Object value) {
if(attrs==null)attrs=new StructImpl();
Key key = KeyImpl.getInstance(StringUtil.trim(name,""));
attrs.setEL(key,value);
}
@Override
public void setDynamicAttribute(String uri, Collection.Key name, Object value) {
if(attrs==null)attrs=new StructImpl();
Key key = KeyImpl.getInstance(StringUtil.trim(name.getString(),""));
attrs.setEL(key,value);
}
@Override
public int doStartTag() throws PageException {
pc=pageContext;
switch(action) {
case ACTION_JOIN:
doJoin();
break;
case ACTION_SLEEP:
required("thread", "sleep", "duration", duration,-1);
doSleep();
break;
case ACTION_TERMINATE:
required("thread", "terminate", "name", name);
doTerminate();
break;
case ACTION_RUN:
required("thread", "run", "name", name);
return EVAL_BODY_INCLUDE;
}
return SKIP_BODY;
}
@Override
public int doEndTag() throws PageException {
this.pc=pageContext;
//if(ACTION_RUN==action) doRun();
return EVAL_PAGE;
}
public void register(Page currentPage, int threadIndex) throws PageException {
if(ACTION_RUN!=action) return;
if(((PageContextImpl)pc).getParentPageContext()!=null)
throw new ApplicationException("could not create a thread within a child thread");
try {
Threads ts = pc.getThreadScope(lcName);
if(type==TYPE_DEAMON){
if(ts!=null)
throw new ApplicationException("could not create a thread with the name ["+name+"]. name must be unique within a request");
ChildThreadImpl ct = new ChildThreadImpl((PageContextImpl) pc,currentPage,name,threadIndex,attrs,false);
pc.setThreadScope(name,new ThreadsImpl(ct));
ct.setPriority(priority);
ct.setDaemon(false);
ct.start();
}
else {
ChildThreadImpl ct = new ChildThreadImpl((PageContextImpl) pc,currentPage,name,threadIndex,attrs,true);
ct.setPriority(priority);
((ConfigImpl)pc.getConfig()).getSpoolerEngine().add(new ChildSpoolerTask(ct,plans));
}
}
catch (Throwable t) {
throw Caster.toPageException(t);
}
finally {
pc.reuse(this);// this method is not called from template when type is run, a call from template is to early,
}
}
private void doSleep() throws ExpressionException {
if(duration>=0) {
SystemUtil.sleep(duration);
}
else throw new ExpressionException("The attribute duration must be greater or equal than 0, now ["+duration+"]");
}
private void doJoin() throws ApplicationException {
PageContextImpl mpc=(PageContextImpl)getMainPageContext(pc);
String[] names;
if(lcName==null) {
names=mpc.getThreadScopeNames();
}
else names=ListUtil.listToStringArray(lcName, ',');
ChildThread ct;
Threads ts;
long start=System.currentTimeMillis(),_timeout=timeout>0?timeout:-1;
for(int i=0;i<names.length;i++) {
if(StringUtil.isEmpty(names[i],true))continue;
//PageContextImpl mpc=(PageContextImpl)getMainPageContext(pc);
ts = mpc.getThreadScope(names[i]);
if(ts==null)
throw new ApplicationException("there is no thread running with the name ["+names[i]+"], only the following threads existing ["+ListUtil.arrayToList(mpc.getThreadScopeNames(),", ")+"]");
ct=ts.getChildThread();
if(ct.isAlive()) {
try {
if(_timeout!=-1)ct.join(_timeout);
else ct.join();
}
catch (InterruptedException e) {}
}
if(_timeout!=-1){
_timeout=_timeout-(System.currentTimeMillis()-start);
if(_timeout<1) break;
}
}
}
private void doTerminate() throws ApplicationException {
PageContextImpl mpc=(PageContextImpl)getMainPageContext(pc);
Threads ts = mpc.getThreadScope(lcName);
if(ts==null)
throw new ApplicationException("there is no thread running with the name ["+name+"]");
ChildThread ct = ts.getChildThread();
if(ct.isAlive()){
ct.terminated();
ct.stop();
}
}
private PageContext getMainPageContext(PageContext pc) {
if(pc==null)pc=pageContext;
if(pc.getParentPageContext()==null) return pc;
return pc.getParentPageContext();
}
@Override
public void doInitBody() {
}
@Override
public int doAfterBody() {
return SKIP_BODY;
}
/**
* sets if has body or not
* @param hasBody
*/
public void hasBody(boolean hasBody) {
}
}