View Javadoc

1   /*
2    * Copyright 2002,2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.jelly.tags.threads;
17  
18  import org.apache.commons.jelly.JellyContext;
19  import org.apache.commons.jelly.JellyTagException;
20  import org.apache.commons.jelly.TagSupport;
21  import org.apache.commons.jelly.XMLOutput;
22  import org.apache.commons.jelly.util.NestedRuntimeException;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  
27  import java.io.FileOutputStream;
28  import java.io.IOException;
29  import java.io.UnsupportedEncodingException;
30  
31  /***
32   * A tag that spawns the contained script in a separate thread.  A thread
33   * can wait on another thread or another thread group to finish before starting.
34   *
35   * @author <a href="mailto:vinayc@apache.org">Vinay Chandran</a>
36   * @author <a href="mailto:jason@jhorman.org">Jason Horman</a>
37   */
38  public class ThreadTag extends TagSupport {
39      /*** The Log to which logging calls will be made. */
40      private static final Log log = LogFactory.getLog(ThreadTag.class);
41  
42      /*** The current thread number. Used for default thread naming */
43      private static int threadNumber = 0;
44  
45      /*** Variable to place the thread into */
46      private String var = null;
47      /*** Thread Name */
48      private String name = null;
49      /*** Thread priority, defaults to Thread.NORM_PRIORITY */
50      private int priority = Thread.NORM_PRIORITY;
51      /*** Set if the thread should be a daemon or not */
52      private boolean daemon = false;
53      /*** the destination of output */
54      private XMLOutput xmlOutput;
55      /*** Should we close the underlying output */
56      private boolean closeOutput;
57      /*** Should a new context be created */
58      private boolean newContext = false;
59      /*** Keep a reference to the thread */
60      private JellyThread thread = new JellyThread();
61  
62      public ThreadTag() {
63          super();
64      }
65  
66      public ThreadTag(boolean shouldTrim) {
67          super(shouldTrim);
68      }
69  
70      // Tag interface
71      //-------------------------------------------------------------------------
72      public void doTag(final XMLOutput output) throws JellyTagException {
73          if (xmlOutput == null) {
74              // lets default to system.out
75              try {
76                  xmlOutput = XMLOutput.createXMLOutput(System.out);
77              }
78              catch (UnsupportedEncodingException e) {
79                  throw new JellyTagException(e);
80              }
81          }
82  
83          // lets create a child context
84          final JellyContext useThisContext = newContext ? context.newJellyContext() : context;
85  
86          // set the target to run
87          thread.setTarget(new Runnable() {
88              public void run() {
89                  try {
90                      getBody().run(useThisContext, xmlOutput);
91                      if (closeOutput) {
92                          xmlOutput.close();
93                      }
94                      else {
95                          xmlOutput.flush();
96                      }
97                  }
98                  catch (JellyTagException e) {
99                      // jelly wraps the exceptions thrown
100                     Throwable subException = e.getCause();
101                     if (subException != null) {
102                         if (subException instanceof TimeoutException) {
103                             throw (TimeoutException)subException;
104                         } else if (subException instanceof RequirementException) {
105                             throw (RequirementException)subException;
106                         }
107                     }
108 
109                     log.error(e);
110 
111                     // wrap the exception with a RuntimeException
112                     throw new NestedRuntimeException(e);
113                 }
114                 catch (Exception e) {
115                     log.error(e);
116 
117                     // wrap the exception with a RuntimeException
118                     if (e instanceof RuntimeException) {
119                         throw (RuntimeException) e;
120                     }
121                     else {
122                         throw new NestedRuntimeException(e);
123                     }
124                 }
125             }
126         });
127 
128         // set the threads priority
129         thread.setPriority(priority);
130 
131         // set the threads name
132         if (name != null) {
133             thread.setName(name);
134         } else {
135             thread.setName("Jelly Thread #" + (threadNumber++));
136         }
137 
138         // set whether this thread is a daemon thread
139         thread.setDaemon(daemon);
140 
141         // save the thread in a context variable
142         if (var != null) {
143             context.setVariable(var, thread);
144         }
145 
146         // check if this tag is nested inside a group tag. if so
147         // add this thread to the thread group but do not start it.
148         // all threads in a thread group should start together.
149         GroupTag gt = (GroupTag) findAncestorWithClass(GroupTag.class);
150         if (gt != null) {
151             gt.addThread(thread);
152         } else {
153             // start the thread
154             thread.start();
155         }
156     }
157 
158     /***
159      * Sets the variable name to export, optional
160      * @param var The variable name
161      */
162     public void setVar(String var) {
163         this.var = var;
164         if (name == null) {
165             name = var;
166         }
167     }
168 
169     /***
170      * Sets the name of the thread.
171      * @param name The name to set
172      */
173     public void setName(String name) {
174         this.name = name;
175     }
176 
177     /***
178      * Set the threads priority. Defaults to Thread.NORM_PRIORITY
179      */
180     public void setPriority(int priority) {
181         this.priority = priority;
182     }
183 
184     /***
185      * Sets the thread to be a daemon thread if true
186      */
187     public void setDaemon(boolean daemon) {
188         this.daemon = daemon;
189     }
190 
191     /***
192      * Sets the destination of output
193      */
194     public void setXmlOutput(XMLOutput xmlOutput) {
195         this.closeOutput = false;
196         this.xmlOutput = xmlOutput;
197     }
198 
199     /***
200      * Set the file which is generated from the output
201      * @param name The output file name
202      */
203     public void setFile(String name) throws IOException {
204         this.closeOutput = true;
205         setXmlOutput(XMLOutput.createXMLOutput(new FileOutputStream(name)));
206     }
207 
208     /***
209      * Should a new context be created for this thread?
210      */
211     public void setNewContext(boolean newContext) {
212         this.newContext = newContext;
213     }
214 
215     /***
216      * Get the thread instance
217      * @return The thread
218      */
219     public Thread getThread() {
220         return thread;
221     }
222 }