KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sapia > resource > include > IncludeState


1 package org.sapia.resource.include;
2
3 import java.util.Collections JavaDoc;
4 import java.util.HashMap JavaDoc;
5 import java.util.Map JavaDoc;
6 import java.util.Stack JavaDoc;
7
8 import org.sapia.resource.ResourceCapable;
9
10 /**
11  * This class is used to perform file-includes programmatically. It internally
12  * keeps a stack of {@link org.sapia.resource.include.IncludeContext} instances that
13  * correspond to recursive resource-inclusion operations.
14  * <p>
15  * Internally, state is maintained on a per-thread and per-application basis. This allows multiple
16  * threads to perform resource inclusion operations in parallel, and isolates different applications/frameworks so
17  * that resource inclusion does not conflict (this framework could be used by a given framework X, that could itself
18  * be used by a given framework Y, also using this framework...).
19  * <p>
20  * In order to use this class, applications must:
21  * <ol>
22  * <li>Extend the {@link org.sapia.resource.include.IncludeContext} class and overridde the {@link org.sapia.resource.include.IncludeContext#doInclude(InputStream, Object)}
23  * method.
24  * <li>Implement an {@link org.sapia.resource.include.IncludeContextFactory}, that will create instances of the above {@link org.sapia.resource.include.IncludeContext}.
25  * <li>Implement a {@link org.sapia.resource.ResourceCapable} class. An instance of that class is in charge of resolving resources (see {@link org.sapia.resource.ResourceCapable}
26  * for details).
27  * </ol>
28  * <p>
29  *
30  * Then, the first step in using this class in order to perform resource inclusion is to create an {@link org.sapia.resource.include.IncludeConfig}
31  * instance that is kept by the application and can be used for multiple resource-inclusion operations:
32  *
33  * <pre>
34  * IncludeConfig config = IncludeState.createConfig("org.acme.myapp", myIncludeContextFactory, myResourceCapableObject);
35  * </pre>
36  *
37  * <p>
38  * It should be noted that the {@link #createConfig(String, IncludeContextFactory, ResourceCapable)} method takes a so-called
39  * "application key" has an argument. This is because this class could be used by multiple applications/frameworks residing within
40  * the same VM and classloader... Provided these frameworks use this class concurrently, this could pose problem. Therefore,
41  * the registering inclusion state under a given application key, and to the current thread, ensures that no conflict occur.
42  * <p>
43  * Note that the best way to guarantee uniqueness of application keys is to use root package names.
44  *
45  * <p>
46  * Once an IncludeConfig has been created, use it with this class to perform resource-inclusion:
47  * <p>
48  *
49  * <pre>
50  * Object someObject = IncludeState.createContext("file:conf/main.xml", config).include();
51  * </pre>
52  * <p>
53  * The framework handles relative file resolution. Therefore, if the following code comes after the previous one,
54  * the resource will be resolved relatively to "conf/main.xml" (meaning that it will correspond
55  * to "conf/includes/database.xml"):
56  *
57  * <pre>
58  * Object someObject = IncludeState.createContext("includes/database.xml", config).include();
59  * </pre>
60  * <p>
61  * This framework provides a {@link Resource} primitive. Multiple implementations exist, that correspond to
62  * the following protocol schemes:
63  *
64  * <ul>
65  * <li>file (e.g.: file:conf/main.xml, file:/home/foo/conf/main.xml).
66  * <li>resource (e.g.: resource:/org/acme/myapp/main.xml).
67  * <li>http
68  * </ul>
69  * A {@link org.sapia.resource.ResourceCapable} implementation is provided: the {@link org.sapia.resource.ResourceHandlerChain}
70  * class, which implements a chain of responsibility that encapsulates {@link org.sapia.resource.ResourceHandler}s. You could use
71  * these implementations as follows:
72  *
73  * <pre>
74  * ResourceHandlerChain resources = new ResourceHandlerChain();
75  * resource.append(new FileResourceHanler());
76  * resource.append(new ClassPathResourceHanler());
77  * resource.append(new UrlPathResourceHanler());
78  *
79  * IncludeConfig config = IncludeState.createConfig("org.acme.myapp", myIncludeContextFactory, resources);
80  * </pre>
81  *
82  * @see org.sapia.resource.include.IncludeContext
83  * @see org.sapia.resource.include.IncludeContextFactory
84  * @see org.sapia.resource.include.IncludeConfig
85  *
86  * @author yduchesne
87  *
88  */

89 public class IncludeState {
90   
91   private static ThreadLocal JavaDoc _state = new ThreadLocal JavaDoc();
92   private Stack JavaDoc _stack = new Stack JavaDoc();
93   
94   static{
95     _state.set(Collections.synchronizedMap(new HashMap JavaDoc()));
96   }
97   
98   IncludeState(){
99   }
100   
101   /**
102    * @param appKey the application key
103    * @param fac an <code>IncludeContextFactory</code>.
104    * @param resources a <code>ResourceCapable</code> instance.
105    *
106    * @return an <code>IncludeConfig</code>.
107    */

108   public static synchronized IncludeConfig createConfig(
109       String JavaDoc appKey,
110       IncludeContextFactory fac,
111       ResourceCapable resources){
112     IncludeConfig conf = new IncludeConfig(appKey, fac, resources);
113     return conf;
114   }
115   
116   /**
117    * @param appKey the application key
118    * @return this instance's current <code>IncludeContext</code>, or <code>null</code> if no
119    * such instance exists (i.e.: none has been stacked).
120    */

121   public static synchronized IncludeContext currentContext(String JavaDoc appKey){
122     IncludeState instance = instance(appKey);
123     if(instance._stack.size() == 0){
124       return null;
125     }
126     else{
127       return (IncludeContext)instance._stack.peek();
128     }
129   }
130   
131   /**
132    * This method internally creates an {@link IncludeContext} that it pushes on its internall include stack
133    * before returning it.
134    *
135    * @param uri the URI for which to create a new <code>IncludeContext</code>.
136    * @param config the <code>IncludeConfig</code> to use to create the context.
137    *
138    * @return a new <code>IncludeContext</code>.
139    */

140   public static synchronized IncludeContext createContext(
141       String JavaDoc uri,
142       IncludeConfig config){
143     IncludeState instance = instance(config.getAppKey());
144     IncludeContext child = config.getFactory().createInstance();
145     if(instance != null && instance._stack.size() > 0){
146       child.setParent((IncludeContext)instance._stack.peek());
147     }
148     instance._stack.push(child);
149     child.setConfig(config);
150     child.setUri(uri);
151     return child;
152   }
153   
154   static void popContext(String JavaDoc appKey){
155     IncludeState instance = instance(appKey);
156     if(instance._stack.size() > 0){
157       instance._stack.pop();
158     }
159   }
160   
161   static IncludeState instance(String JavaDoc appKey){
162     IncludeState state = (IncludeState)((Map JavaDoc)_state.get()).get(appKey);
163     if(state == null){
164       state = new IncludeState();
165       ((Map JavaDoc)_state.get()).put(appKey, state);
166     }
167     return state;
168   }
169   
170 }
171
Popular Tags