View Javadoc

1   /*
2    * Copyright 2008 Simon Martinelli, Rebenweg 32, 3236 Gampelen, Switzerland
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.simject;
17  
18  import java.io.FileNotFoundException;
19  import java.io.InputStream;
20  import java.lang.annotation.Annotation;
21  import java.lang.reflect.Field;
22  import java.net.MalformedURLException;
23  import java.util.HashMap;
24  import java.util.Map;
25  import java.util.logging.Level;
26  import java.util.logging.Logger;
27  
28  import javax.persistence.EntityManager;
29  import javax.persistence.EntityManagerFactory;
30  import javax.persistence.Persistence;
31  import javax.xml.bind.JAXBContext;
32  import javax.xml.bind.Unmarshaller;
33  
34  import org.simject.exception.SimConfigException;
35  import org.simject.exception.SimResourceNotFoundException;
36  import org.simject.jaxb.Property;
37  import org.simject.jaxb.Resource;
38  import org.simject.jaxb.Resources;
39  import org.simject.remoting.client.HttpClientProxy;
40  import org.simject.util.SimConstants;
41  
42  /**
43   * SimFactory represents the IOC container. It parses the XML configuration
44   * files, does the dependency injection and provides methods for retrieving
45   * resources.
46   * 
47   * @author Simon Martinelli
48   */
49  public class SimFactory {
50  
51  	private static final Logger logger = Logger.getLogger(SimFactory.class
52  			.getName());
53  
54  	/**
55  	 * Container holding all configured resources
56  	 */
57  	final private Map<Class<?>, Object> resourceMap = new HashMap<Class<?>, Object>();
58  
59  	/**
60  	 * Constructor that takes 0-n configuration files
61  	 * 
62  	 * @param fileNames
63  	 */
64  	public SimFactory(final String... fileNames) {
65  
66  		for (String fileName : fileNames) {
67  			this.loadXmlConfig(fileName);
68  		}
69  		this.injectDependencies();
70  	}
71  
72  	/**
73  	 * Generic method to retrieve a resource identified by <type> If <type> is
74  	 * an interface, <target> must be present. If <type> is
75  	 * javax.persistence.EntityManager a special treatment will occur
76  	 * 
77  	 * @param <T>
78  	 *            type of resource
79  	 * @param clazz
80  	 *            must be the type configured in the config file
81  	 * @return an instance of the desired type
82  	 */
83  	@SuppressWarnings("unchecked")
84  	public <T> T getResource(final Class<T> clazz) {
85  		final Object obj = this.resourceMap.get(clazz);
86  		if (obj == null) {
87  			final String message = "Resource of type " + clazz.getName()
88  					+ " not found";
89  			logger.severe(message);
90  			throw new SimResourceNotFoundException(message);
91  		}
92  		return (T) obj;
93  	}
94  
95  	/**
96  	 * Uses JAXB to parse the config file
97  	 * 
98  	 * @param fileName
99  	 */
100 	private void loadXmlConfig(final String fileName) {
101 		try {
102 			logger.info("Loading configuration from <"
103 					+ SimConstants.DEFAULT_DIRECTORY + fileName + ">");
104 
105 			final JAXBContext jcontext = JAXBContext
106 					.newInstance("org.simject.jaxb");
107 			final Unmarshaller unmarshaller = jcontext.createUnmarshaller();
108 
109 			final InputStream istream = Thread.currentThread()
110 					.getContextClassLoader().getResourceAsStream(
111 							SimConstants.DEFAULT_DIRECTORY + fileName);
112 
113 			if (istream == null) {
114 				throw new FileNotFoundException(SimConstants.DEFAULT_DIRECTORY
115 						+ fileName + " not found");
116 			} else {
117 				final Resources resources = (Resources) unmarshaller
118 						.unmarshal(istream);
119 				for (Resource resource : resources.getResource()) {
120 					this.createResource(resource);
121 				}
122 			}
123 		} catch (Exception e) {
124 			logger.log(Level.SEVERE, e.getMessage(), e);
125 			throw new SimConfigException(e.getMessage(), e);
126 		}
127 	}
128 
129 	/**
130 	 * Creates an new Resource and stores it in the resource container
131 	 * 
132 	 * @param resource
133 	 *            the resource retrieved from config file
134 	 * @throws ClassNotFoundException
135 	 * @throws InstantiationException
136 	 * @throws IllegalAccessException
137 	 * @throws MalformedURLException
138 	 */
139 	private void createResource(final Resource resource)
140 			throws ClassNotFoundException, InstantiationException,
141 			IllegalAccessException, MalformedURLException {
142 
143 		final String className = resource.getType();
144 		final Class<?> clazz = Class.forName(className);
145 
146 		Object obj = null;
147 		if (resource.getType().equals(EntityManager.class.getName())) {
148 			// type is EntityManager
149 			obj = this.createEntityManager(resource);
150 		} else if (resource.getTarget() != null
151 				&& resource.getTarget().contains("http://")) {
152 			// target is URL
153 			obj = this.createHttpClientProxy(clazz, resource.getTarget());
154 		} else {
155 			// any other will create a POJO instance
156 			obj = this.createPojo(resource, clazz);
157 		}
158 		this.resourceMap.put(clazz, obj);
159 	}
160 
161 	/**
162 	 * Creates a HttpClientProxy
163 	 * 
164 	 * @param clazz
165 	 * @param target
166 	 * @return
167 	 * @throws MalformedURLException
168 	 */
169 	private Object createHttpClientProxy(final Class<?> clazz,
170 			final String target) throws MalformedURLException {
171 		logger.info("Creating <" + clazz.getName() + "> for URL <" + target
172 				+ ">");
173 
174 		return HttpClientProxy.newInstance(Thread.currentThread()
175 				.getContextClassLoader(), new Class[] { clazz }, target);
176 	}
177 
178 	/**
179 	 * Creates a simple POJO instance
180 	 * 
181 	 * @param resource
182 	 * @param clazz
183 	 * @return
184 	 * @throws InstantiationException
185 	 * @throws IllegalAccessException
186 	 * @throws ClassNotFoundException
187 	 */
188 	private Object createPojo(final Resource resource, final Class<?> clazz)
189 			throws InstantiationException, IllegalAccessException,
190 			ClassNotFoundException {
191 
192 		Object obj = null;
193 		if (resource.getTarget() == null || resource.getTarget().equals("")) {
194 			logger.info("Creating <" + resource.getType() + ">");
195 			obj = createInstance(clazz);
196 		} else {
197 			logger.info("Creating <" + resource.getType() + "> as <"
198 					+ resource.getTarget() + ">");
199 			final String realizedby = resource.getTarget();
200 			final Class<?> realizedbyClazz = Class.forName(realizedby);
201 			obj = createInstance(realizedbyClazz);
202 		}
203 		return obj;
204 	}
205 
206 	/**
207 	 * Creates a JPA EntityManager
208 	 * 
209 	 * @param resource
210 	 * @return
211 	 */
212 	private Object createEntityManager(final Resource resource) {
213 
214 		logger.info("Creating <" + resource.getType() + ">");
215 
216 		final Map<String, String> props = new HashMap<String, String>();
217 		for (Property property : resource.getProperty()) {
218 			props.put(property.getName(), property.getValue());
219 		}
220 		final EntityManagerFactory emf = Persistence
221 				.createEntityManagerFactory(resource.getName(), props);
222 		return emf.createEntityManager();
223 	}
224 
225 	/**
226 	 * Creates an instance of the provided Class. If Class is an Interface an
227 	 * Exception is thrown.
228 	 * 
229 	 * @param clazz
230 	 * @return
231 	 * @throws InstantiationException
232 	 * @throws IllegalAccessException
233 	 */
234 	private Object createInstance(final Class<?> clazz)
235 			throws InstantiationException, IllegalAccessException {
236 
237 		if (clazz.isInterface()) {
238 			throw new InstantiationException(
239 					"Can not instantiate a interface. Please check configuration");
240 		}
241 		return clazz.newInstance();
242 	}
243 
244 	/**
245 	 * Loops over the resource container and does the dependency injection
246 	 */
247 	private void injectDependencies() {
248 		try {
249 			for (Object obj : this.resourceMap.values()) {
250 				final Field[] fields = obj.getClass().getDeclaredFields();
251 				for (Field field : fields) {
252 					final Annotation[] annotations = field
253 							.getDeclaredAnnotations();
254 					for (Annotation annotation : annotations) {
255 						if (annotation.annotationType().equals(
256 								javax.annotation.Resource.class)) {
257 							final Class<?> clazz = field.getType();
258 							final Object value = this.resourceMap.get(clazz);
259 							field.setAccessible(true);
260 							field.set(obj, value);
261 							logger.info("Injecting instance of <"
262 									+ value.getClass().getName()
263 									+ "> into field <" + field.getName()
264 									+ "> in class <" + obj.getClass().getName()
265 									+ ">");
266 						}
267 					}
268 				}
269 			}
270 		} catch (Exception e) {
271 			logger.log(Level.SEVERE, e.getMessage(), e);
272 			throw new SimConfigException(
273 					"Unable to inject dependencies. Please check the configuration",
274 					e);
275 		}
276 	}
277 }