001 /*
002 * Copyright 2004-2006 Stephen J. McConnell.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.runtime;
020
021 import dpml.lang.DOM3DocumentBuilder;
022 import dpml.util.StandardClassLoader;
023 import dpml.util.ElementHelper;
024 import dpml.util.DefaultLogger;
025
026 import java.io.IOException;
027 import java.lang.reflect.Constructor;
028
029 import net.dpml.lang.DecodingException;
030 import net.dpml.lang.Strategy;
031 import net.dpml.lang.StrategyHandler;
032
033 import net.dpml.annotation.Activation;
034 import net.dpml.annotation.ActivationPolicy;
035 import net.dpml.annotation.LifestylePolicy;
036 import net.dpml.annotation.CollectionPolicy;
037
038 import net.dpml.util.Resolver;
039 import net.dpml.util.Logger;
040
041 import org.w3c.dom.Element;
042
043 /**
044 * Strategy handler for the component model.
045 *
046 * @author <a href="http://www.dpml.net">Digital Product Management Library</a>
047 * @version 2.1.1
048 */
049 public class ComponentStrategyHandler implements StrategyHandler
050 {
051 /**
052 * Constant XSD namespace value.
053 */
054 public static final String NAMESPACE = "dpml:metro";
055
056 private static final DOM3DocumentBuilder BUILDER = new DOM3DocumentBuilder();
057
058 /**
059 * Creation of a new component strategy handler for a supplied class.
060 * @param c the implementation class
061 * @return the new strategy handler
062 * @exception IOException if an error occurs in strategy creation
063 */
064 public Strategy newStrategy( Class<?> c ) throws IOException
065 {
066 return newStrategy( c, null );
067 }
068
069 /**
070 * Creation of a new component strategy handler for a supplied class.
071 * @param c the implementation class
072 * @param name the strategy instance name
073 * @return the new strategy handler
074 * @exception IOException if an error occurs in strategy creation
075 */
076 public Strategy newStrategy( Class<?> c, String name ) throws IOException
077 {
078 String spec = getComponentName( c, name );
079 Profile profile = new Profile( c, spec, null );
080 ContextModel context = getContextModel( null, c, spec, profile, null, null, null, true );
081 PartsDirective parts = profile.getPartsDirective();
082 return new ComponentStrategy(
083 null, spec, 0, c, ActivationPolicy.SYSTEM, LifestylePolicy.THREAD,
084 CollectionPolicy.HARD, context, parts );
085 }
086
087 /**
088 * Creation of a new component strategy handler for a supplied class.
089 * @param c the implementation class
090 * @param name the strategy instance name
091 * @return the new strategy handler
092 * @exception IOException if an error occurs in strategy creation
093 */
094 public Component newComponent( Class<?> c, String name ) throws IOException
095 {
096 return (Component) newStrategy( c, name );
097 }
098
099 /**
100 * Construct a strategy definition using a supplied element and value resolver.
101 * @param classloader the operational classloader
102 * @param element the DOM element definining the deployment strategy
103 * @param resolver symbolic property resolver
104 * @param partition the enclosing partition
105 * @param query the query fragment to applied to the component context definition
106 * @param validate the validation policy
107 * @return the strategy definition
108 * @exception IOException if an I/O error occurs
109 */
110 public Strategy build(
111 ClassLoader classloader, Element element, Resolver resolver, String partition,
112 String query, boolean validate ) throws IOException
113 {
114 Class c = loadComponentClass( classloader, element, resolver );
115 String name = getComponentName( c, element, resolver );
116 int priority = getPriority( element, resolver );
117 String path = getComponentPath( partition, name );
118 Profile profile = new Profile( c, path, resolver );
119
120 Query q = new Query( query );
121 Element contextElement = ElementHelper.getChild( element, "context" );
122 ContextModel context =
123 getContextModel(
124 classloader, c, path, profile, contextElement, resolver, q, validate );
125 ActivationPolicy activation = getActivationPolicy( element, profile, c );
126 LifestylePolicy lifestyle = getLifestylePolicy( element, profile, c );
127 CollectionPolicy collection = getCollectionPolicy( element, profile, c );
128
129 try
130 {
131 Element partsElement = ElementHelper.getChild( element, "parts" );
132 PartsDirective parts =
133 new PartsDirective(
134 profile.getPartsDirective(),
135 classloader,
136 partsElement,
137 resolver,
138 path );
139 ComponentStrategy strategy =
140 new ComponentStrategy(
141 partition,
142 name,
143 priority,
144 c,
145 activation,
146 lifestyle,
147 collection,
148 context,
149 parts );
150 strategy.setElement( element );
151 return strategy;
152 }
153 catch( IOException ioe )
154 {
155 throw ioe;
156 }
157 catch( Exception e )
158 {
159 final String error =
160 "Unable to construct component strategy for the class ["
161 + c.getName()
162 + "] under the partition ["
163 + partition
164 + "]";
165 IOException ioe = new IOException( error );
166 ioe.initCause( e );
167 throw ioe;
168 }
169 }
170
171 /**
172 * Return the activation policy.
173 *
174 * @return the resolved activation policy
175 */
176 private static ActivationPolicy getActivationPolicy( Element element, Profile profile, Class<?> c )
177 {
178 if( null != element )
179 {
180 String value = ElementHelper.getAttribute( element, "activation" );
181 if( null != value )
182 {
183 return ActivationPolicy.valueOf( value.toUpperCase() );
184 }
185 }
186 if( null != profile )
187 {
188 return profile.getActivationPolicy();
189 }
190 if( c.isAnnotationPresent( Activation.class ) )
191 {
192 Activation annotation =
193 c.getAnnotation( Activation.class );
194 return annotation.value();
195 }
196 return ActivationPolicy.SYSTEM;
197 }
198
199 /**
200 * Return the lifestyle policy.
201 *
202 * @return the resolved lifestyle policy
203 */
204 private static LifestylePolicy getLifestylePolicy( Element element, Profile profile, Class<?> c )
205 {
206 if( null != element )
207 {
208 String value = ElementHelper.getAttribute( element, "lifestyle" );
209 if( null != value )
210 {
211 return LifestylePolicy.valueOf( value.toUpperCase() );
212 }
213 }
214 if( null != profile )
215 {
216 return profile.getLifestylePolicy();
217 }
218 if( c.isAnnotationPresent( net.dpml.annotation.Component.class ) )
219 {
220 net.dpml.annotation.Component annotation =
221 c.getAnnotation( net.dpml.annotation.Component.class );
222 return annotation.lifestyle();
223 }
224 return LifestylePolicy.THREAD;
225 }
226
227 /**
228 * Return the collection policy.
229 *
230 * @return the resolved collection policy
231 */
232 private static CollectionPolicy getCollectionPolicy( Element element, Profile profile, Class<?> c )
233 {
234 if( null != element )
235 {
236 String value = ElementHelper.getAttribute( element, "collection" );
237 if( null != value )
238 {
239 return CollectionPolicy.valueOf( value.toUpperCase() );
240 }
241 }
242 if( null != profile )
243 {
244 return profile.getCollectionPolicy();
245 }
246 if( c.isAnnotationPresent( net.dpml.annotation.Component.class ) )
247 {
248 net.dpml.annotation.Component annotation =
249 c.getAnnotation( net.dpml.annotation.Component.class );
250 return annotation.collection();
251 }
252 return CollectionPolicy.HARD;
253 }
254
255 static int getPriority( Element element, Resolver resolver )
256 {
257 String value = ElementHelper.getAttribute( element, "priority", null, resolver );
258 if( null == value )
259 {
260 return 0;
261 }
262 else
263 {
264 return Integer.parseInt( value );
265 }
266 }
267
268 static Class loadComponentClass( ClassLoader classloader, Element element, Resolver resolver ) throws DecodingException
269 {
270 String classname = buildComponentClassname( element, resolver );
271 try
272 {
273 return classloader.loadClass( classname );
274 }
275 catch( ClassNotFoundException e )
276 {
277 final String error =
278 "Class not found [" + classname + "].";
279 Logger logger = new DefaultLogger( "dpml.lang" );
280 String stack = StandardClassLoader.toString( classloader, null );
281 logger.error( error + stack );
282 throw new DecodingException( error, e, element );
283 }
284 }
285
286 private static String buildComponentClassname( Element element, Resolver resolver )
287 {
288 String classname = ElementHelper.getAttribute( element, "class", null, resolver );
289 if( null != classname )
290 {
291 return classname;
292 }
293 else
294 {
295 return ElementHelper.getAttribute( element, "type", null, resolver );
296 }
297 }
298
299 static String getComponentName( Class c, Element element, Resolver resolver )
300 {
301 String name = getComponentName( element, resolver );
302 return getComponentName( c, name );
303 }
304
305 private static String getComponentName( Element element, Resolver resolver )
306 {
307 if( null == element )
308 {
309 return null;
310 }
311 else
312 {
313 String key = ElementHelper.getAttribute( element, "key", null, resolver );
314 if( null != key )
315 {
316 return key;
317 }
318 else
319 {
320 return ElementHelper.getAttribute( element, "name", null, resolver );
321 }
322 }
323 }
324
325 private static String getComponentName( Class<?> c )
326 {
327 if( c.isAnnotationPresent( net.dpml.annotation.Component.class ) )
328 {
329 net.dpml.annotation.Component annotation =
330 c.getAnnotation( net.dpml.annotation.Component.class );
331 String name = annotation.name();
332 if( !"".equals( name ) )
333 {
334 return name;
335 }
336 }
337 return c.getName();
338 }
339
340 private static String getComponentNameFromClass( Class<?> c )
341 {
342 String classname = c.getName();
343 int n = classname.lastIndexOf( "." );
344 if( n > -1 )
345 {
346 return classname.substring( n+1 );
347 }
348 else
349 {
350 return classname;
351 }
352 }
353
354 static String getComponentPath( String partition, String name )
355 {
356 if( null == partition )
357 {
358 return name;
359 }
360 else
361 {
362 return partition + "." + name;
363 }
364 }
365
366 private static String getComponentName( Class c, String name )
367 {
368 if( null != name )
369 {
370 return name;
371 }
372 else
373 {
374 return getComponentName( c );
375 }
376 }
377
378 static Constructor getSingleConstructor( Class c )
379 {
380 Constructor[] constructors = c.getConstructors();
381 if( constructors.length < 1 )
382 {
383 final String error =
384 "The component class ["
385 + c.getName()
386 + "] does not declare a public constructor.";
387 throw new ComponentError( error );
388 }
389 else if( constructors.length > 1 )
390 {
391 final String error =
392 "The component class ["
393 + c.getName()
394 + "] declares more than one public constructor.";
395 throw new ComponentError( error );
396 }
397 else
398 {
399 return constructors[0];
400 }
401 }
402
403 private static Class getContextClass( Constructor constructor, boolean policy )
404 {
405 final Class<?>[] types = constructor.getParameterTypes();
406 for( Class c : types )
407 {
408 if( ContextInvocationHandler.isaContext( c, policy ) )
409 {
410 return c;
411 }
412 }
413 return null;
414 }
415
416 private static ContextModel getContextModel(
417 ClassLoader classloader, Class clazz, String path,
418 Profile profile, Element element, Resolver resolver, Query query,
419 boolean validate ) throws IOException
420 {
421 boolean policy = getContextHandlingPolicy( clazz );
422 Constructor constructor = getSingleConstructor( clazz );
423 Class subject = getContextClass( constructor, policy );
424 ContextDirective bundled = profile.getContextDirective();
425 ContextDirective declared = new ContextDirective( classloader, element, resolver );
426 return new ContextModel( clazz, path, subject, policy, bundled, declared, query, validate );
427 }
428
429 private static boolean getContextHandlingPolicy( Class c )
430 {
431 // TODO: update to get policy as a class annotation
432 return false; // return classic evaluation policy
433 }
434
435 }