1 | // Copyright 2004-2007 Jean-Francois Poilpret |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | package net.sourceforge.hivetranse.transaction.hibernate3; |
16 | |
17 | import java.lang.reflect.InvocationHandler; |
18 | import java.lang.reflect.InvocationTargetException; |
19 | import java.lang.reflect.Method; |
20 | import java.lang.reflect.Proxy; |
21 | import java.net.URL; |
22 | import java.util.ArrayList; |
23 | import java.util.HashMap; |
24 | import java.util.Iterator; |
25 | import java.util.List; |
26 | import java.util.Map; |
27 | import java.util.Properties; |
28 | |
29 | import org.apache.commons.logging.Log; |
30 | import org.apache.hivemind.ApplicationRuntimeException; |
31 | import org.apache.hivemind.Resource; |
32 | import org.apache.hivemind.ServiceImplementationFactory; |
33 | import org.apache.hivemind.ServiceImplementationFactoryParameters; |
34 | import org.apache.hivemind.events.RegistryShutdownListener; |
35 | import org.apache.hivemind.util.ToStringBuilder; |
36 | import org.hibernate.HibernateException; |
37 | import org.hibernate.SessionFactory; |
38 | import org.hibernate.classic.Session; |
39 | |
40 | import net.sourceforge.hivetranse.transaction.MandatoryTransactionException; |
41 | import net.sourceforge.hivetranse.transaction.TransactionService; |
42 | import net.sourceforge.hiveutils.collections.NameValuePair; |
43 | |
44 | /** |
45 | * This service creates Proxys to Hibernate Sessions. |
46 | * Each service built by this factory must have one configuration parameter |
47 | * indicating the hibernate configuration file to be used for building the |
48 | * Hibernate <code>SessionFactory<code>. |
49 | * <p> |
50 | * <b>ServiceModel must be singleton</b> |
51 | * |
52 | * @author Jean-Francois Poilpret |
53 | */ |
54 | public class SessionProxyFactory |
55 | implements ServiceImplementationFactory, |
56 | RegistryShutdownListener |
57 | { |
58 | public SessionProxyFactory( Log logger, |
59 | SessionFactoryBuilder builder, |
60 | TransactionService txService) |
61 | { |
62 | _logger = logger; |
63 | _builder = builder; |
64 | _txService = txService; |
65 | } |
66 | |
67 | public void registryDidShutdown() |
68 | { |
69 | _logger.debug("registryDidShutdown()"); |
70 | // Close all SessionFactorys |
71 | for (Map.Entry<String, SessionFactory> entry: _factories.entrySet()) |
72 | { |
73 | closeSessionFactory(entry.getKey(), entry.getValue()); |
74 | } |
75 | _factories.clear(); |
76 | } |
77 | |
78 | public Object createCoreServiceImplementation( |
79 | ServiceImplementationFactoryParameters factoryParams) |
80 | { |
81 | Log logger = factoryParams.getLog(); |
82 | String serviceId = factoryParams.getServiceId(); |
83 | Class<?> serviceInterface = factoryParams.getServiceInterface(); |
84 | |
85 | // Make sure that serviceInterface == Session |
86 | // (this factory is dedicated exclusively to sessions) |
87 | if (!serviceInterface.isAssignableFrom(Session.class)) |
88 | { |
89 | // throw error |
90 | logger.error("SessionProxyFactory can only build Session services."); |
91 | throw new ApplicationRuntimeException("Bad interface"); |
92 | } |
93 | |
94 | // Read parameter to create SessionFactory |
95 | SessionFactoryContribution contrib = |
96 | (SessionFactoryContribution) factoryParams.getFirstParameter(); |
97 | SessionFactory factory = buildSessionFactory(logger, serviceId, contrib); |
98 | |
99 | return Proxy.newProxyInstance( |
100 | factoryParams.getInvokingModule().getClassResolver().getClassLoader(), |
101 | new Class[] {serviceInterface}, |
102 | new SessionProxy(logger, serviceId, factory)); |
103 | } |
104 | |
105 | protected SessionFactory buildSessionFactory( |
106 | Log logger, String id, SessionFactoryContribution contrib) |
107 | { |
108 | // Get parameters given in factory parameters |
109 | Resource config = contrib.getConfig(); |
110 | Properties props = contrib.getProperties(); |
111 | List<URL> mappingFiles = new ArrayList<URL>(); |
112 | List<Class> mappingClasses = new ArrayList<Class>(); |
113 | List<String> mappingPackages = new ArrayList<String>(); |
114 | // Analyse extra parameters given as a configuration |
115 | Iterator i = contrib.getSettings().iterator(); |
116 | while (i.hasNext()) |
117 | { |
118 | Object next = i.next(); |
119 | if (next instanceof HibernateMappingContribution) |
120 | { |
121 | HibernateMappingContribution mapping = |
122 | (HibernateMappingContribution) next; |
123 | if (mapping.getMappingFile() != null) |
124 | { |
125 | URL url = mapping.getMappingFile().getResourceURL(); |
126 | if (url == null) |
127 | { |
128 | logger.warn("Bad mapping file: " + |
129 | mapping.getMappingFile().getName()); |
130 | } |
131 | else |
132 | { |
133 | mappingFiles.add(url); |
134 | } |
135 | } |
136 | if (mapping.getMappingClass() != null) |
137 | { |
138 | mappingClasses.add(mapping.getMappingClass()); |
139 | } |
140 | if (mapping.getMappingPackage() != null) |
141 | { |
142 | mappingPackages.add(mapping.getMappingPackage()); |
143 | } |
144 | } |
145 | else if (next instanceof NameValuePair) |
146 | { |
147 | // Override any previous value with the same name |
148 | NameValuePair pair = (NameValuePair) next; |
149 | props.setProperty(pair.getName(), pair.getValue()); |
150 | } |
151 | else |
152 | { |
153 | // Error to log, but still go on normally |
154 | logger.warn("bad configuration schema passed to SessionProxyFactory."); |
155 | } |
156 | } |
157 | SessionFactory factory = |
158 | _builder.buildSessionFactory( config, |
159 | props, |
160 | mappingPackages, |
161 | mappingClasses, |
162 | mappingFiles, |
163 | contrib.getInterceptor(), |
164 | contrib.getNamingStrategy()); |
165 | _factories.put(id, factory); |
166 | return factory; |
167 | } |
168 | |
169 | protected void closeSessionFactory(String id, SessionFactory factory) |
170 | { |
171 | _logger.debug("closeSessionFactory(" + id + ")"); |
172 | try |
173 | { |
174 | factory.close(); |
175 | } |
176 | catch (HibernateException e) |
177 | { |
178 | _logger.error("closing SessionFactory id " + id, e); |
179 | } |
180 | } |
181 | |
182 | private class SessionProxy implements InvocationHandler |
183 | { |
184 | public SessionProxy(Log logger, String serviceId, SessionFactory factory) |
185 | { |
186 | _logger = logger; |
187 | _serviceId = serviceId; |
188 | _factory = factory; |
189 | } |
190 | |
191 | //CSOFF: IllegalThrowsCheck |
192 | public Object invoke(Object proxy, Method method, Object[] args) |
193 | throws Throwable |
194 | { |
195 | if ("toString".equals(method.getName())) |
196 | { |
197 | ToStringBuilder builder = new ToStringBuilder(this); |
198 | builder.append("serviceId", _serviceId); |
199 | return builder.toString(); |
200 | } |
201 | |
202 | SessionsRepository repository = |
203 | (SessionsRepository) _txService.getCurrentTransaction(); |
204 | if (repository == null) |
205 | { |
206 | _logger.warn("No active transaction"); |
207 | throw new MandatoryTransactionException("No active transaction"); |
208 | } |
209 | Session session = repository.getSession(_serviceId, _factory); |
210 | try |
211 | { |
212 | return method.invoke(session, args); |
213 | } |
214 | catch (IllegalAccessException e) |
215 | { |
216 | // Can never happen (normally!) |
217 | _logger.fatal("Unexpected", e); |
218 | throw e; |
219 | } |
220 | catch (InvocationTargetException e) |
221 | { |
222 | throw e.getTargetException(); |
223 | } |
224 | } |
225 | //CSON: IllegalThrowsCheck |
226 | |
227 | private final Log _logger; |
228 | private final String _serviceId; |
229 | private final SessionFactory _factory; |
230 | } |
231 | |
232 | private final Log _logger; |
233 | private SessionFactoryBuilder _builder; |
234 | private final TransactionService _txService; |
235 | private final Map<String, SessionFactory> _factories = |
236 | new HashMap<String, SessionFactory>(); |
237 | } |