View Javadoc

1   package com.sourceforge.jpatterns.patterns.factory;
2   
3   import com.sourceforge.jpatterns.core.JPConstants;
4   import com.sourceforge.jpatterns.core.configuration.exceptions.JPConfigException;
5   import com.sourceforge.jpatterns.patterns.PatternBase;
6   import com.sourceforge.jpatterns.schema.CastorSectionType;
7   import com.sourceforge.jpatterns.schema.Factory;
8   import com.sourceforge.jpatterns.schema.Item;
9   import com.sourceforge.jpatterns.schema.Param;
10  import com.sourceforge.jpatterns.utils.CastorUtils;
11  import com.zmicer.utils.InputArgumentUtils;
12  import com.zmicer.utils.LoggingUtils;
13  import com.zmicer.utils.ReflexionUtils;
14  import org.apache.commons.lang.StringUtils;
15  import org.apache.log4j.Logger;
16  
17  import java.util.HashMap;
18  import java.util.List;
19  import java.util.Map;
20  
21  /**
22   * The default implementation of the <code>IJPFactory</code> interface.
23   * <p/>
24   * todo [zmicer]: increase the coverage later - almost all the cases are checked still we need to finish covering all the protected methods.
25   *
26   * todo [zmicer]: adjust logAndThrow method - actually we are able to detrmine where to thrown Consumer of Framework config exception.
27   *
28   * $Author:: zmicer             $<br/>
29   * $Rev:: 67                    $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
30   */
31  public class JPFactoryImpl extends PatternBase implements IJPFactory
32  {
33      /**
34       * Logger instance.
35       */
36      final public static Logger LOG = Logger.getLogger(JPConstants.LoggingCategory.FACTORY.toString());
37  
38      /**
39       * @see com.sourceforge.jpatterns.patterns.IJPattern#checkCastorConfig(com.sourceforge.jpatterns.schema.CastorSectionType)
40       */
41      public boolean checkCastorConfig(CastorSectionType castorSectionType)
42      {
43          InputArgumentUtils.checkObjects(castorSectionType);
44          return (castorSectionType instanceof Factory);
45      }
46      
47      /**
48       * @see IJPFactory#getImplementation(java.lang.Class,java.lang.String)
49       */
50      public Object getImplementation(final Class interfaceClass, final String scope)
51      {
52          return instantiateObjectByItem(retrieveItem(interfaceClass, scope), interfaceClass);
53      }
54  
55      /**
56       * @see IJPFactory#getImplementation(java.lang.Class,java.lang.String)
57       */
58      public Object getImplementation(final String interfaceClassBaseName, final String scope)
59      {
60          return instantiateObjectByItem(retrieveItem(interfaceClassBaseName, scope), null);
61      }
62  
63      /**
64       * @see IJPFactory#getImplementationFullName(Class,String)
65       */
66      public String getImplementationFullName(final Class interfaceClass, final String scope)
67      {
68          return retrieveItem(interfaceClass, scope).getValue();
69      }
70  
71      /**
72       * @see IJPFactory#getImplementationFullName(String,String)
73       */
74      public String getImplementationFullName(final String interfaceClassBaseName, final String scope)
75      {
76          return retrieveItem(interfaceClassBaseName, scope).getValue();
77      }
78  
79      /**
80       * @see IJPFactory#getImplementation(java.lang.Class,java.lang.String,java.lang.String)
81       */
82      public Object getImplementation(final Class interfaceClass, final String implType, final String scope)
83      {
84          InputArgumentUtils.checkObjects(interfaceClass, implType);
85          return instantiateObjectByItemAndType(retrieveItem(interfaceClass, scope), implType, interfaceClass);
86      }
87  
88      /**
89       * @see IJPFactory#getImplementation(java.lang.String,java.lang.String,java.lang.String)
90       */
91      public Object getImplementation(final String interfaceClassBaseName, final String implType, final String scope)
92      {
93          InputArgumentUtils.checkObjects(interfaceClassBaseName, implType);
94          return instantiateObjectByItemAndType(retrieveItem(interfaceClassBaseName, scope), implType, null);
95      }
96  
97      /**
98       * @see IJPFactory#getImplementations(Class,String)
99       */
100     public Map<String, Object> getImplementations(final Class interfaceClass, final String scope)
101     {
102         InputArgumentUtils.checkObjects(interfaceClass);
103         Map<String, Object> result = new HashMap<String, Object>();
104         try
105         {
106             result = validateImplementationsMap(interfaceClass,
107                 ReflexionUtils.reflectObjects(getImplementationsNamesByItem(retrieveItem(interfaceClass, scope)), true));
108         }
109         catch (Exception e)
110         {
111             LoggingUtils.logException(LOG, e, "Exception occuried", JPFactoryImpl.class);
112             logAndThrow("The message of exception occuried [" + e.getMessage() + "]");
113         }
114         return result;
115     }
116 
117     /**
118      * @see IJPFactory#getImplementations(String,String)
119      */
120     public Map<String, Object> getImplementations(final String interfaceClassBaseName, final String scope)
121     {
122         InputArgumentUtils.checkObjects(interfaceClassBaseName);
123         Map<String, Object> result = new HashMap<String, Object>();
124         try
125         {
126             result = ReflexionUtils.reflectObjects(getImplementationsNamesByItem(retrieveItem(interfaceClassBaseName, scope)), true);
127         }
128         catch (Exception e)
129         {
130             LoggingUtils.logException(LOG, e, "Exception occuried", JPFactoryImpl.class);
131             logAndThrow("The message of exception occuried [" + e.getMessage() + "]");
132         }
133         return result;
134     }
135 
136     /**
137      * @see IJPFactory#getImplementationsFullNames(Class,String)
138      */
139     public Map<String, String> getImplementationsFullNames(final Class interfaceClass, final String scope)
140     {
141         return getImplementationsNamesByItem(retrieveItem(interfaceClass, scope));
142     }
143 
144     /**
145      * @see IJPFactory#getImplementationsFullNames(String,String)
146      */
147     public Map<String, String> getImplementationsFullNames(final String interfaceClassBaseName, final String scope)
148     {
149         return getImplementationsNamesByItem(retrieveItem(interfaceClassBaseName, scope));
150     }
151 
152     /**
153      * @see IJPFactory#getOperator(Class,Class,Object, String)
154      */
155     public Object getOperator(final Class productBaseClass, final Class operatorBaseClass, final Object productObj, final String scope)
156     {
157         InputArgumentUtils.checkObjects(productBaseClass, operatorBaseClass, productObj);
158         if (!ReflexionUtils.instanceOf(productObj, productBaseClass))
159         {
160             logAndThrow("The provided object: class [" + productObj.getClass().getName() + "] is not of the class of the product: class[" +
161                 productBaseClass.getName() + "]");
162         }
163 
164         final Map<String, String> implementations = getImplementationsNamesByItem(
165             retrieveItem(ReflexionUtils.getBaseName(productBaseClass) + "_" + ReflexionUtils.getBaseName(operatorBaseClass), scope));
166         if (null == implementations || implementations.isEmpty())
167         {
168             throw new JPConfigException("The map of the implementations names can not be null: product base class name [" +
169                 productBaseClass.getName() + "], operator base class name [" + operatorBaseClass + "], scope [" + scope + "]");
170         }
171         // using assumption the base name is used to define the concrete implementation (consider any jpatterns config - it is name of the param
172         // object).
173         final String productBaseName = ReflexionUtils.getBaseName(productObj.getClass());
174         if (implementations.containsKey(productBaseName) &&
175             implementations.containsKey(productObj.getClass().getName()))
176         {
177             logAndThrow("Incorrect case when the required operator configuration for product class [" + productBaseClass.getName() +
178                 "] is defined using both base and full names of the class of product specified [" + productObj.getClass().getName() + "]. It is " +
179                 "incorrect case.");
180         }
181         String className = implementations.get(productBaseName);
182         if (StringUtils.isEmpty(className))
183         {
184             LoggingUtils.debug(LOG, this.getClass(), "The implementation of the provided object was obtained using the full product class " +
185                 "definition. Product class [" + productObj.getClass().getName() + "], implementation class [" + className + "].");
186             className = implementations.get(productObj.getClass().getName());
187         }
188 
189         if (null == className)
190         {
191             logAndThrow("Can not find the operator defined for the product object with an class [" + productObj.getClass().getName() +
192                 "]. The class of the operator [" + operatorBaseClass.getName() + "], the base class of the product [" +
193                 productBaseClass.getName() + "]");
194         }
195         final Object result = ReflexionUtils.reflectObject(className, false);
196         if (null == result)
197         {
198             logAndThrow("Can not instantiate the operator with the full class name [" + className + "] for the product of the full class " +
199                 "name [" + productObj.getClass().getName() + "]");
200         }
201         else if (!ReflexionUtils.instanceOf(result, operatorBaseClass))
202         {
203             logAndThrow("The obtained operator instance: class [" + result.getClass().getName() + "] is not of the class of the operator " +
204                 "was provided: class[" + operatorBaseClass.getName() + "]");
205         }
206         return result;
207     }
208 
209     /**
210      * @see IJPFactory#getOperators(Class,Class,java.util.List, String)
211      */
212     public Map<Object, Object> getOperators(final Class productBaseClass, final Class operatorBaseClass, final List<Object> productObjs,
213         final String scope)
214     {
215         InputArgumentUtils.checkObjects(productBaseClass, operatorBaseClass, productObjs);
216         final Map<Class, Object> classesInfo = new HashMap<Class, Object>();
217         final Map<Object, Object> result = new HashMap<Object, Object>();
218         for (Object product : productObjs)
219         {
220             if (null == classesInfo.get(product.getClass()))
221             {
222                 classesInfo.put(product.getClass(), getOperator(productBaseClass, operatorBaseClass, product, scope));
223             }
224             result.put(product, classesInfo.get(product.getClass()));
225         }
226         return result;
227     }
228 
229     /**
230      * @see com.zmicer.utils.model.ICheckable#check()
231      */
232     public boolean check()
233     {
234         return super.check() && (m_castorSection instanceof Factory);
235     }
236 
237     /**
238      * Get <code>Param</code> castor object by the key.
239      *
240      * @param item the castor <code>Item</code>. Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
241      * @param key  The String key object for which we need to obtain the param.
242      *
243      * @return the Param Object
244      */
245     protected Param getParam(final Item item, final String key)
246     {
247         InputArgumentUtils.checkObjects(item, key);
248         for (final Param param : item.getParam())
249         {
250             if (null != param.getName() && param.getName().equals(key))
251             {
252                 return param;
253             }
254         }
255         return null;
256     }
257 
258     /**
259      * Instantiate the Object using the provided item. The provided class object (if it is not null) is used for validating the correct
260      * implementation is specified.
261      *
262      * @param item the Item from value attribute of which we would instantiate the object
263      * @param claz the Class instance to check that implementation correlates with the interface for which we obtain this. Could be null.
264      *
265      * @return the instantiated Object.
266      *
267      * @throws JPConfigException in the case we can not instantiate the object using the value of the provided item
268      */
269     protected Object instantiateObjectByItem(final Item item, Class claz) throws JPConfigException
270     {
271         if (null == item)
272         {
273             throw new IllegalArgumentException("Item can not be null.");
274         }
275         final String implName = item.getValue();
276         if (null == implName)
277         {
278             logAndThrow("The item should have the value attribute not empty to be processed here. String representation" +
279                 CastorUtils.toString(null, item));
280         }
281         final Object object = ReflexionUtils.reflectObject(implName, false);
282         if (null == object)
283         {
284             logAndThrow("Can not instantiate the implementation of the [" + item.getName() +
285                 "] using the scope [" + item.getScope() + "]. The name of the implementation we got from the config [" + implName + "]");
286         }
287         if (null != claz)
288         {
289             if (!ReflexionUtils.instanceOf(object, claz))
290             {
291                 logAndThrow("The obtained implementation instance: class [" + object.getClass().getName() + "] is not of the class of " +
292                     "the interface specified: class[" + claz.getName() + "]");
293             }
294         }
295         return object;
296     }
297 
298     /**
299      * Instantiate the Object using the provided item and the type we need
300      *
301      * @param item the Item from value attribute of which we would instantiate the object
302      *             Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
303      * @param type the type for which we need instantiate the object
304      *             Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
305      *
306      * @param claz this is necessary to perform the validation that the correct implementation is defined at the configuration file.
307      * @return the instantiated Object.
308      *
309      * @throws JPConfigException in the case we can not instantiate the object using the value of the provided item
310      */
311     protected Object instantiateObjectByItemAndType(final Item item, final String type, Class claz) throws JPConfigException
312     {
313         if (null == item)
314         {
315             throw new IllegalArgumentException("Item can not be null.");
316         }
317         final Param param = getParam(item, type);
318         final String message = "Can not find the required implementation defined following the path: section scope [" +
319             m_castorSection.getScope() + "] section name [" + m_castorSection.getName() + "] item scope [" + item.getScope() + "] item name [" +
320             item.getName() + "] implementation type [" + type + "]";
321         if (null == param || StringUtils.isBlank(param.getValue()))
322         {
323             LoggingUtils.error(LOG, JPFactoryImpl.class, message);
324             throw new JPConfigException(message);
325         }
326         final Object object = ReflexionUtils.reflectObject(param.getValue(), false);
327         if (null == object)
328         {
329             logAndThrow("Can not instantiate the implementation of the [" + item.getName() + "] using the scope [" +
330                 item.getScope() + "]. The name of the implementation we got from the config [" + param.getValue() + "]");
331         }
332         if (null != claz)
333         {
334             if (!ReflexionUtils.instanceOf(object, claz))
335             {
336                 logAndThrow("The obtained implementation instance: class [" + object.getClass().getName() + "] is not of the class of " +
337                     "the interface specified: class[" + claz.getName() + "]");
338             }
339         }
340 
341         return object;
342     }
343 
344     /**
345      * Instantiate the Object using the provided item
346      *
347      * @param item the Item from value attribute of which we would instantiate the object
348      *
349      * @return the Map where #key is a types of implementation, and the #value is a full name of the implementation. In the case the castor
350      *         item do not contains the mapping the empty map is returned.
351      *
352      * @throws JPConfigException in the case we can not instantiate the object using the value of the provided item
353      */
354     protected Map<String, String> getImplementationsNamesByItem(final Item item) throws JPConfigException
355     {
356         if (null == item)
357         {
358             throw new IllegalArgumentException("Item can not be null.");
359         }
360         final Map<String, String> result = new HashMap<String, String>();
361         if (item.getParam().length == 0)
362         {
363             logAndThrow("The item" + CastorUtils.toString(m_castorSection, item) + " doesn't contain the params configuration using which the " +
364                 "implementations names should be retrivied.");
365         }
366         // the names and values should be filled
367         for (Param param : item.getParam())
368         {
369             if (StringUtils.isBlank(param.getName()) || StringUtils.isBlank(param.getValue()))
370             {
371                 logAndThrow("The item" + CastorUtils.toString(m_castorSection, item) + " contains the params with blank name or " +
372                     "value. It is illegal case for the this type factory configuration.");
373             }
374             if (result.containsKey(param.getName()))
375             {
376                 logAndThrow("The item" + CastorUtils.toString(m_castorSection, item) + " contains the params with duplicated names.");
377             }
378             result.put(param.getName(), param.getValue());
379         }
380         return result;
381     }
382 
383     /**
384      * Check that all the implementations of the map - are the types of the provided class.
385      *
386      * @param clazz The class against which we would check the map, Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
387      * @param map containing the implementation we need to check
388      *
389      * @return the map with the implementation without any changes - just pass through method making the check
390      */
391     protected Map<String, Object> validateImplementationsMap(final Class clazz, final Map<String, Object> map)
392     {
393         InputArgumentUtils.checkObjects(clazz, map);
394         for (final Object object : map.values())
395         {
396             if (!ReflexionUtils.instanceOf(object, clazz))
397             {
398                 logAndThrow("The map of implememntations was introduced for the class [" + clazz.getName() + "] can not contain the implementation" +
399                     " which is not of this class [" + object.getClass().getName() + "].");    
400             }
401         }
402         return map;
403     }
404 }