View Javadoc

1   /**
2    * This file Copyright (c) 2003-2011 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.objectfactory;
35  
36  import info.magnolia.init.MagnoliaConfigurationProperties;
37  import org.apache.commons.lang.exception.ExceptionUtils;
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  
41  import java.util.HashMap;
42  import java.util.Map;
43  import java.util.Properties;
44  import java.util.Set;
45  
46  /**
47   * This {@link info.magnolia.objectfactory.ComponentProvider} is using the configuration provided by
48   * {@link info.magnolia.cms.core.SystemProperty}. Each property key is the interface/base-class, and the value
49   * is either the implementation-to-use class name, an implementation of {@link info.magnolia.objectfactory.ComponentFactory}
50   * which is used to instantiate the desired implementation, or the path to a node in the repository (in the form of
51   * <code>repository:/path/to/node</code> or <code>/path/to/node</code>, which defaults to the <code>config</code>
52   * repository). In the latter case, the component is constructed via {@link info.magnolia.objectfactory.ObservedComponentFactory}
53   * and reflects (through observation) the contents of the given path.
54   *
55   * @deprecated since 4.5, use IoC, i.e another implementation of ComponentProvider.
56   *
57   * @version $Id$
58   */
59  public class DefaultComponentProvider implements ComponentProvider {
60      private final static Logger log = LoggerFactory.getLogger(DefaultComponentProvider.class);
61  
62      /**
63       * Registered singleton instances.
64       */
65      private final Map<Class<?>, Object> instances = new HashMap<Class<?>, Object>();
66  
67      /**
68       * Registered prototypes used by newInstance().
69       */
70      private final Map<Class<?>, ComponentFactory<?>> factories = new HashMap<Class<?>, ComponentFactory<?>>();
71  
72      private final Properties mappings;
73  
74      public DefaultComponentProvider(final MagnoliaConfigurationProperties mappings) {
75          this(new Properties() {{
76              final Set<String> keys = mappings.getKeys();
77              for (String key : keys) {
78                  put(key, mappings.getProperty(key));
79              }
80          }});
81      }
82  
83      public DefaultComponentProvider(Properties mappings) {
84          // Ideally, the dependency should be on SystemProperty or other relevant object.
85          // Hopefully, we'll de-staticize SystemProperty soon.
86          this.mappings = mappings;
87  
88          // TODO : we have a dependency on ClassFactory, but we can't inject it here,
89          // since it might get swapped later
90      }
91  
92      @Override
93      @Deprecated
94      public synchronized <T> T getSingleton(Class<T> type) {
95          return getComponent(type);
96      }
97  
98      @Override
99      public synchronized <T> T getComponent(Class<T> type) {
100         T instance = (T) instances.get(type);
101         if (instance == null) {
102             log.debug("No instance for {} yet, creating new one.", type);
103             instance = newInstance(type);
104             instances.put(type, instance);
105             log.debug("New instance for {} created: {}", type, instance);
106         }
107 
108         return instance;
109     }
110 
111     @Override
112     public <T> T newInstance(Class<T> type, Object... parameters) {
113         if (type == null) {
114             log.error("type can't be null", new Throwable());
115             return null;
116         }
117 
118         try {
119             if (factories.containsKey(type)) {
120                 final ComponentFactory<T> factory = (ComponentFactory<T>) factories.get(type);
121                 return factory.newInstance();
122             }
123 
124             final String className = getImplementationName(type);
125             if (isInRepositoryDefinition(className)) {
126                 final ComponentConfigurationPath path = new ComponentConfigurationPath(className);
127                 final ObservedComponentFactory<T> factory = new ObservedComponentFactory<T>(path.getRepository(), path.getPath(), type, this);
128                 setInstanceFactory(type, factory);
129                 // now that the factory is registered, we call ourself again
130                 return newInstance(type);
131             }
132             final Class<?> clazz = Classes.getClassFactory().forName(className);
133             if (!Classes.isConcrete(clazz)) {
134                 throw new MgnlInstantiationException("No concrete implementation defined for " + clazz);
135             }
136             final Object instance = Classes.getClassFactory().newInstance(clazz, parameters);
137 
138             if (instance instanceof ComponentFactory) {
139                 final ComponentFactory<T> factory = (ComponentFactory<T>) instance;
140                 setInstanceFactory(type, factory);
141                 return factory.newInstance();
142             }
143             return (T) instance;
144         } catch (Exception e) {
145             if (e instanceof MgnlInstantiationException) {
146                 throw (MgnlInstantiationException) e;
147             }
148             throw new MgnlInstantiationException("Can't instantiate an implementation of this class [" + type.getName() + "]: " + ExceptionUtils.getMessage(e), e);
149         }
150     }
151 
152     @Override
153     public <T> T newInstanceWithParameterResolvers(Class<T> type, ParameterResolver... parameters) {
154         throw new UnsupportedOperationException();
155     }
156 
157     /**
158      * @deprecated since 4.5, use {@link Classes#isConcrete(Class)}
159      */
160     protected boolean isConcrete(Class<?> clazz) {
161         return Classes.isConcrete(clazz);
162     }
163 
164     // TODO - is this needed / correct ?
165     @Override
166     public <C> Class<? extends C> getImplementation(Class<C> type) throws ClassNotFoundException {
167         final String className = getImplementationName(type);
168         if (!isInRepositoryDefinition(className)) {
169             return (Class<? extends C>) Classes.getClassFactory().forName(className);
170         }
171         return type;
172     }
173 
174     protected String getImplementationName(Class<?> type) {
175         final String name = type.getName();
176         return mappings.getProperty(name, name);
177     }
178 
179     @Override
180     public ComponentProvider getParent() {
181         return null;
182     }
183 
184     /**
185      * @deprecated since 4.5, use {@link ComponentConfigurationPath#isComponentConfigurationPath(String)}
186      */
187     static boolean isInRepositoryDefinition(String className) {
188         return ComponentConfigurationPath.isComponentConfigurationPath(className);
189     }
190 
191     /**
192      * Used only in tests.
193      * @see {@link info.magnolia.test.ComponentsTestUtil}
194      */
195     public void setImplementation(Class<?> type, String impl) {
196         mappings.setProperty(type.getName(), impl);
197     }
198 
199     /**
200      * Used only in tests.
201      * @see {@link info.magnolia.test.ComponentsTestUtil}
202      */
203     public void setInstance(Class<?> type, Object instance) {
204         instances.put(type, instance);
205     }
206 
207     /**
208      * Used only in tests.
209      * @see {@link info.magnolia.test.ComponentsTestUtil}
210      */
211     public void setInstanceFactory(Class<?> type, ComponentFactory<?> factory) {
212         factories.put(type, factory);
213     }
214 
215     /**
216      * Used only in tests.
217      * <strong>Warning:</strong> this does NOT clear the *mappings*. With the current/default implementation,
218      * this means tests also have to call SystemProperty.clearr()
219      * @see {@link info.magnolia.test.ComponentsTestUtil}
220      */
221     public void clear() {
222         factories.clear();
223         instances.clear();
224     }
225 
226 }