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   */
42  public class FacesBeanFactory
43  {
44    /**
45     * Create a FacesBean for a component class.
46     */
47    // TODO change from ownerClass to componentFamily?
48    static public FacesBean createFacesBean(
49      Class<?> ownerClass,
50      String   rendererType)
51    {
52      if (ownerClass == null)
53        return null;
54  
55      String className = ownerClass.getName();
56      FacesBean bean = createFacesBean(className, rendererType);
57  
58      if (bean == null && rendererType != null)
59      {
60        bean = createFacesBean(className, null);
61        _cacheFacesBeanClass(bean, className, rendererType);
62      }
63      
64      if (bean == null)
65      {
66        bean = createFacesBean(ownerClass.getSuperclass(), rendererType);
67        _cacheFacesBeanClass(bean, className, rendererType);
68      }
69  
70      return bean;
71    }
72  
73    static public FacesBean createFacesBean(
74      String beanType,
75      String rendererType)
76    {
77      String typeKey = _buildTypeKey(beanType, rendererType);
78  
79      Class<?> type = _TYPES_CLASS.get(typeKey);
80        
81      if(type == null)
82      {
83        String className = (String) _TYPES_MAP.get(typeKey);
84        if (className == null)
85          return null;
86        
87        // At this point we did not have a cached FacesBean class for the
88        // typeKey, but we did have a cached className for the typeKey.
89        //  Get the FacesBean class from the className and cache.
90        // This will improve performance based on tests.
91        try
92        {
93          type = _getClassLoader().loadClass(className);
94          _TYPES_CLASS.put(typeKey, type);
95        }
96        catch (ClassNotFoundException cnfe)
97        {
98          _LOG.severe("CANNOT_FIND_FACESBEAN", className);
99          _LOG.severe(cnfe);
100       }
101     }
102   
103     try
104     {
105       return (FacesBean) type.newInstance();
106     }
107     catch (IllegalAccessException iae)
108     {
109       _LOG.severe("CANNOT_CREATE_FACESBEAN_INSTANCE", type.getName());
110       _LOG.severe(iae);
111     }
112     catch (InstantiationException ie)
113     {
114       _LOG.severe("CANNOT_CREATE_FACESBEAN_INSTANCE", type.getName());
115       _LOG.severe(ie);
116     }
117 
118     return null;
119   }
120 
121   static private void _initializeBeanTypes()
122   {
123     _TYPES_MAP = new HashMap<Object, Object>();
124 
125     List<URL> list = new ArrayList<URL>();
126     try
127     {
128       Enumeration<URL> en = _getClassLoader().getResources(
129                                 "META-INF/faces-bean.properties");
130       while (en.hasMoreElements())
131       {
132         list.add(en.nextElement());
133       }
134 
135       Collections.reverse(list);
136     }
137     catch (IOException ioe)
138     {
139       _LOG.severe(ioe);
140       return;
141     }
142 
143     if (list.isEmpty())
144     {
145       if (_LOG.isInfo())
146         _LOG.info("NO_FACES_BEAN_PROPERTIES_FILES_LOCATED");
147     }
148 
149     for(URL url : list)
150     {
151       _initializeBeanTypes(url);
152     }
153   }
154 
155   static private void _initializeBeanTypes(URL url)
156   {
157     try
158     {
159       Properties properties = new Properties();
160       InputStream is = url.openStream();
161       try
162       {
163         properties.load(is);
164         if (_LOG.isFine())
165           _LOG.fine("Loading bean factory info from " + url);
166         
167         _TYPES_MAP.putAll(properties);
168       }
169       finally
170       {
171         is.close();
172       }
173     }
174     catch (IOException ioe)
175     {
176       _LOG.severe("CANNOT_LOAD_URL", url);
177       _LOG.severe(ioe);
178     }
179   }
180 
181 
182   static private ClassLoader _getClassLoader()
183   {
184     ClassLoader loader = Thread.currentThread().getContextClassLoader();
185     if (loader == null)
186       loader = FacesBeanFactory.class.getClassLoader();
187     return loader;
188   }
189 
190   /* given non-null beanType & rendererType, concatenate together with 
191    * a '|' in between
192    */
193   static private String _buildTypeKey(
194     String beanType, 
195     String rendererType)
196   {
197     if (rendererType != null)
198     {
199       StringBuilder typeKeyBuilder = _getSharedStringBuilder();
200       
201       typeKeyBuilder.append(beanType).append('|').append(rendererType);
202       
203       return typeKeyBuilder.toString();
204     }
205     else
206       return beanType;
207     
208   }
209   
210   static private void _cacheFacesBeanClass(
211     FacesBean bean,
212     String beanType, 
213     String rendererType)
214   {
215     // cache the typeKey and the bean's class, for performance
216     if(bean != null)
217     {
218       String typeKey = _buildTypeKey(beanType, rendererType);
219       _TYPES_CLASS.put(typeKey, bean.getClass());
220     }
221   }
222   
223   /**
224    * <p>
225    * This gets a single threadlocal shared stringbuilder instance, each time you call
226    * _getSharedStringBuilder it sets the length of the stringBuilder instance to 0.
227    * </p><p>
228    * This allows you to use the same StringBuilder instance over and over.
229    * You must call toString on the instance before calling _getSharedStringBuilder again.
230    * </p>
231    * Example that works
232    * <pre><code>
233    * StringBuilder sb1 = _getSharedStringBuilder();
234    * sb1.append(a).append(b);
235    * String c = sb1.toString();
236    *
237    * StringBuilder sb2 = _getSharedStringBuilder();
238    * sb2.append(b).append(a);
239    * String d = sb2.toString();
240    * </code></pre>
241    * <br><br>
242    * Example that doesn't work, you must call toString on sb1 before
243    * calling __getSharedStringBuilder again.
244    * <pre><code>
245    * StringBuilder sb1 = _getSharedStringBuilder();
246    * StringBuilder sb2 = _getSharedStringBuilder();
247    *
248    * sb1.append(a).append(b);
249    * String c = sb1.toString();
250    *
251    * sb2.append(b).append(a);
252    * String d = sb2.toString();
253    * </code></pre>
254    *
255    */
256   static private StringBuilder _getSharedStringBuilder()
257   {
258     StringBuilder sb = _STRING_BUILDER.get();
259 
260     if (sb == null)
261     {
262       sb = new StringBuilder();
263       _STRING_BUILDER.set(sb);
264     }
265 
266     // clear out the stringBuilder by setting the length to 0
267     sb.setLength(0);
268 
269     return sb;
270   }
271 
272   static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(FacesBeanFactory.class);
273   static private Map<Object, Object> _TYPES_MAP;
274   static private Map<String, Class<?>> _TYPES_CLASS = new ConcurrentHashMap<String, Class<?>>();
275   static private final ThreadLocal<StringBuilder> _STRING_BUILDER =
276                                                          ThreadLocalUtils.newRequestThreadLocal();
277 
278   static
279   {
280     _initializeBeanTypes();
281   }
282 }