001    package railo.runtime.tag;
002    
003    import java.util.Iterator;
004    
005    import railo.commons.io.res.Resource;
006    import railo.commons.io.res.util.ResourceUtil;
007    import railo.commons.lang.SerializableObject;
008    import railo.commons.lang.StringUtil;
009    import railo.runtime.exp.ApplicationException;
010    import railo.runtime.exp.PageException;
011    import railo.runtime.exp.SecurityException;
012    import railo.runtime.ext.tag.BodyTagImpl;
013    import railo.runtime.op.Caster;
014    import railo.runtime.security.SecurityManager;
015    
016    /**
017    * Enables CFML developers to execute a process on a server computer.
018    *
019    *
020    *
021    **/
022    public final class Execute extends BodyTagImpl {
023    
024            /** Command-line arguments passed to the application. */
025            private String arguments=null;
026    
027            /** Indicates how long, in seconds, the CFML executing thread waits for the spawned process. 
028            **              A timeout of 0 is equivalent to the non-blocking mode of executing. A very high timeout value is 
029            **              equivalent to a blocking mode of execution. The default is 0; therefore, the CFML thread spawns 
030            **              a process and returns without waiting for the process to terminate.If no output file is specified, 
031            **              and the timeout value is 0, the program output is discarded. */
032            private long timeout;
033    
034            /** The full pathname of the application to execute.
035            **              Note: On Windows, you must specify the extension as part of the application's name. For example, 
036            **              myapp.exe, */
037            private String name=null;
038    
039            /** The file to which to direct the output of the program. If not specified, the output is 
040            **              displayed on the page from which it was called. */
041            private Resource outputfile;
042    
043        private String variable;
044    
045            private String body;
046    
047            private boolean terminateOnTimeout=false;
048    
049            @Override
050            public void release()   {
051                    super.release();
052                    arguments=null;
053                    timeout=0L;
054                    name=null;
055                    outputfile=null;
056                    variable=null;
057                    body=null;
058                    terminateOnTimeout=false;
059            }
060    
061            /** set the value arguments
062            *  Command-line arguments passed to the application.
063            * @param args value to set
064            **/
065            public void setArguments(Object args)   {
066                    
067                if(args instanceof railo.runtime.type.Collection) {
068                        StringBuffer sb=new StringBuffer();
069                        railo.runtime.type.Collection coll=(railo.runtime.type.Collection)args;
070                        //railo.runtime.type.Collection.Key[] keys=coll.keys();
071                        Iterator<Object> it = coll.valueIterator();
072                        while(it.hasNext()) {
073                            sb.append(' ');
074                            sb.append(it.next());
075                        }
076                        arguments=args.toString();
077                    }
078                else if(args instanceof String) {
079                    arguments=" "+args.toString();
080                }
081                else this.arguments="";
082            }
083    
084            /** set the value timeout
085            *  Indicates how long, in seconds, the CFML executing thread waits for the spawned process. 
086            *               A timeout of 0 is equivalent to the non-blocking mode of executing. A very high timeout value is 
087            *               equivalent to a blocking mode of execution. The default is 0; therefore, the CFML thread spawns 
088            *               a process and returns without waiting for the process to terminate.If no output file is specified, 
089            *               and the timeout value is 0, the program output is discarded.
090            * @param timeout value to set
091             * @throws ApplicationException 
092            **/
093            public void setTimeout(double timeout) throws ApplicationException      {
094                    if(timeout<0) 
095                            throw new ApplicationException("value must be a positive number now ["+Caster.toString(timeout)+"]");
096                    this.timeout=(long)(timeout*1000L);
097            }
098    
099            public void setTerminateontimeout(boolean terminateontimeout)   {
100                    this.terminateOnTimeout=terminateontimeout;
101            }
102            
103            /** set the value name
104            *  The full pathname of the application to execute.
105            *               Note: On Windows, you must specify the extension as part of the application's name. For example, 
106            *               myapp.exe,
107            * @param name value to set
108            **/
109            public void setName(String name)        {
110                    this.name=name;
111            }
112            
113            /**
114             * define name of variable where output is written to
115             * @param variable
116             * @throws PageException
117             */
118            public void setVariable(String variable) throws PageException   {
119                    this.variable=variable;
120                    pageContext.setVariable(variable,"");
121            }       
122    
123            /** set the value outputfile
124            *  The file to which to direct the output of the program. If not specified, the output is 
125            *               displayed on the page from which it was called.
126            * @param outputfile value to set
127             * @throws SecurityException
128            **/
129            public void setOutputfile(String outputfile)    {
130                try {
131                this.outputfile=ResourceUtil.toResourceExistingParent(pageContext,outputfile);
132                pageContext.getConfig().getSecurityManager().checkFileLocation(this.outputfile);
133                    
134            } 
135                catch (PageException e) {
136                this.outputfile=pageContext.getConfig().getTempDirectory().getRealResource(outputfile);
137                if(!this.outputfile.getParentResource().exists())
138                    this.outputfile=null;
139                else if(!this.outputfile.isFile())
140                    this.outputfile=null;
141                else if(!this.outputfile.exists()) {
142                    ResourceUtil.createFileEL(this.outputfile, false);
143                    //try {
144                        //this.outputfile.createNewFile();
145                    /*} catch (IOException e1) {
146                        this.outputfile=null;
147                    }*/
148                }
149            }
150            }
151    
152    
153            @Override
154            public int doStartTag() throws PageException    {
155                    return EVAL_BODY_BUFFERED;
156            }
157            
158            private void _execute() throws Exception        {
159                Object monitor=new SerializableObject();
160                
161                String command="";
162                if(name==null) {
163                    if(StringUtil.isEmpty(body)) {
164                            required("execute", "name", name);
165                            required("execute", "arguments", arguments);
166                    }
167                    else command=body;
168                }
169                else {
170                    if(arguments==null)command=name;
171                    else command=name+arguments;
172                }
173                
174                
175                
176                _Execute execute=new _Execute(pageContext,monitor,command,outputfile,variable,body);
177                
178                //if(timeout<=0)execute._run();
179                //else {
180            execute.start();
181            if(timeout>0){
182                    try {
183                        synchronized(monitor) {
184                            monitor.wait(timeout);
185                        }
186                    } 
187                        finally {
188                            execute.abort(terminateOnTimeout);
189                    }
190                        if(execute.hasException()) {
191                            throw execute.getException();
192                        }
193                        if(!execute.hasFinished())
194                            throw new ApplicationException("timeout ["+(timeout)+" ms] expired while executing ["+command+"]");
195                        //}
196            }
197                
198            }
199    
200            @Override
201            public int doEndTag() throws PageException      {
202                    if(pageContext.getConfig().getSecurityManager().getAccess(SecurityManager.TYPE_TAG_EXECUTE)==SecurityManager.VALUE_NO) 
203                            throw new SecurityException("can't access tag [execute]","access is prohibited by security manager");
204                try {
205                _execute();
206            } 
207                catch (PageException pe) {
208               throw pe;
209            }
210            catch (Exception e) {e.printStackTrace();
211               throw new ApplicationException("Error invoking external process",e.getMessage());
212            }
213                return EVAL_PAGE;
214            }
215    
216            @Override
217            public void doInitBody()        {
218                    
219            }
220    
221            @Override
222            public int doAfterBody()        {
223                    body=bodyContent.getString();
224                    if(!StringUtil.isEmpty(body))body=body.trim();
225                    return SKIP_BODY;
226            }
227    }