View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.trinidad.bean;
20  
21  import java.io.InputStream;
22  import java.io.IOException;
23  import java.net.URL;
24  
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.Enumeration;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Properties;
32  import java.util.concurrent.ConcurrentHashMap;
33  
34  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
35  import org.apache.myfaces.trinidad.util.ThreadLocalUtils;
36  
37  
38  /**
39   * Base interface for FacesBean storage.
40   */
41  public class FacesBeanFactory
42  {
43    /**
44     * Create a FacesBean for a component class.
45     */
46    // TODO change from ownerClass to componentFamily?
47    static public FacesBean createFacesBean(
48      Class<?> ownerClass,
49      String   rendererType)
50    {
51      if (ownerClass == null)
52        return null;
53  
54      String className = ownerClass.getName();
55      FacesBean bean = createFacesBean(className, rendererType);
56  
57      if (bean == null && rendererType != null)
58      {
59        bean = createFacesBean(className, null);
60        _cacheFacesBeanClass(bean, className, rendererType);
61      }
62      
63      if (bean == null)
64      {
65        bean = createFacesBean(ownerClass.getSuperclass(), rendererType);
66        _cacheFacesBeanClass(bean, className, rendererType);
67      }
68  
69      return bean;
70    }
71  
72    static public FacesBean createFacesBean(
73      String beanType,
74      String rendererType)
75    {
76      String typeKey = _buildTypeKey(beanType, rendererType);
77  
78      Class<?> type = _TYPES_CLASS.get(typeKey);
79        
80      if(type == null)
81      {
82        String className = (String) _TYPES_MAP.get(typeKey);
83        if (className == null)
84          return null;
85        
86        // At this point we did not have a cached FacesBean class for the
87        // typeKey, but we did have a cached className for the typeKey.
88        //  Get the FacesBean class from the className and cache.
89        // This will improve performance based on tests.
90        try
91        {
92          type = _getClassLoader().loadClass(className);
93          _TYPES_CLASS.put(typeKey, type);
94        }
95        catch (ClassNotFoundException cnfe)
96        {
97          _LOG.severe("CANNOT_FIND_FACESBEAN", className);
98          _LOG.severe(cnfe);
99        }
100     }
101   
102     try
103     {
104       return (FacesBean) type.newInstance();
105     }
106     catch (IllegalAccessException iae)
107     {
108       _LOG.severe("CANNOT_CREATE_FACESBEAN_INSTANCE", type.getName());
109       _LOG.severe(iae);
110     }
111     catch (InstantiationException ie)
112     {
113       _LOG.severe("CANNOT_CREATE_FACESBEAN_INSTANCE", type.getName());
114       _LOG.severe(ie);
115     }
116 
117     return null;
118   }
119 
120   static private void _initializeBeanTypes()
121   {
122     _TYPES_MAP = new HashMap<Object, Object>();
123 
124     List<URL> list = new ArrayList<URL>();
125     try
126     {
127       Enumeration<URL> en = _getClassLoader().getResources(
128                                 "META-INF/faces-bean.properties");
129       while (en.hasMoreElements())
130       {
131         list.add(en.nextElement());
132       }
133 
134       Collections.reverse(list);
135     }
136     catch (IOException ioe)
137     {
138       _LOG.severe(ioe);
139       return;
140     }
141 
142     if (list.isEmpty())
143     {
144       if (_LOG.isInfo())
145         _LOG.info("NO_FACES_BEAN_PROPERTIES_FILES_LOCATED");
146     }
147 
148     for(URL url : list)
149     {
150       _initializeBeanTypes(url);
151     }
152   }
153 
154   static private void _initializeBeanTypes(URL url)
155   {
156     try
157     {
158       Properties properties = new Properties();
159       InputStream is = url.openStream();
160       try
161       {
162         properties.load(is);
163         if (_LOG.isFine())
164           _LOG.fine("Loading bean factory info from " + url);
165         
166         _TYPES_MAP.putAll(properties);
167       }
168       finally
169       {
170         is.close();
171       }
172     }
173     catch (IOException ioe)
174     {
175       _LOG.severe("CANNOT_LOAD_URL", url);
176       _LOG.severe(ioe);
177     }
178   }
179 
180 
181   static private ClassLoader _getClassLoader()
182   {
183     ClassLoader loader = Thread.currentThread().getContextClassLoader();
184     if (loader == null)
185       loader = FacesBeanFactory.class.getClassLoader();
186     return loader;
187   }
188 
189   /* given non-null beanType & rendererType, concatenate together with 
190    * a '|' in between
191    */
192   static private String _buildTypeKey(
193     String beanType, 
194     String rendererType)
195   {
196     if (rendererType != null)
197     {
198       StringBuilder typeKeyBuilder = _getSharedStringBuilder();
199       
200       typeKeyBuilder.append(beanType).append('|').append(rendererType);
201       
202       return typeKeyBuilder.toString();
203     }
204     else
205       return beanType;
206     
207   }
208   
209   static private void _cacheFacesBeanClass(
210     FacesBean bean,
211     String beanType, 
212     String rendererType)
213   {
214     // cache the typeKey and the bean's class, for performance
215     if(bean != null)
216     {
217       String typeKey = _buildTypeKey(beanType, rendererType);
218       _TYPES_CLASS.put(typeKey, bean.getClass());
219     }
220   }
221   
222   /**
223    * <p>
224    * This gets a single threadlocal shared stringbuilder instance, each time you call
225    * _getSharedStringBuilder it sets the length of the stringBuilder instance to 0.
226    * </p><p>
227    * This allows you to use the same StringBuilder instance over and over.
228    * You must call toString on the instance before calling _getSharedStringBuilder again.
229    * </p>
230    * Example that works
231    * <pre><code>
232    * StringBuilder sb1 = _getSharedStringBuilder();
233    * sb1.append(a).append(b);
234    * String c = sb1.toString();
235    *
236    * StringBuilder sb2 = _getSharedStringBuilder();
237    * sb2.append(b).append(a);
238    * String d = sb2.toString();
239    * </code></pre>
240    * <br><br>
241    * Example that doesn't work, you must call toString on sb1 before
242    * calling __getSharedStringBuilder again.
243    * <pre><code>
244    * StringBuilder sb1 = _getSharedStringBuilder();
245    * StringBuilder sb2 = _getSharedStringBuilder();
246    *
247    * sb1.append(a).append(b);
248    * String c = sb1.toString();
249    *
250    * sb2.append(b).append(a);
251    * String d = sb2.toString();
252    * </code></pre>
253    *
254    */
255   static private StringBuilder _getSharedStringBuilder()
256   {
257     StringBuilder sb = _STRING_BUILDER.get();
258 
259     if (sb == null)
260     {
261       sb = new StringBuilder();
262       _STRING_BUILDER.set(sb);
263     }
264 
265     // clear out the stringBuilder by setting the length to 0
266     sb.setLength(0);
267 
268     return sb;
269   }
270 
271   static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(FacesBeanFactory.class);
272   static private Map<Object, Object> _TYPES_MAP;
273   static private Map<String, Class<?>> _TYPES_CLASS = new ConcurrentHashMap<String, Class<?>>();
274   static private final ThreadLocal<StringBuilder> _STRING_BUILDER =
275                                                          ThreadLocalUtils.newRequestThreadLocal();
276 
277   static
278   {
279     _initializeBeanTypes();
280   }
281 }