adapt dependencies, not finished yet
[taxeditor.git] / eu.etaxonomy.taxeditor.cdmlib / src / main / java / org / hibernate / proxy / AbstractLazyInitializer.java
1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Inc.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 */
24 package org.hibernate.proxy;
25
26 import java.io.Serializable;
27
28 import javax.naming.NamingException;
29
30 import org.hibernate.HibernateException;
31 import org.hibernate.LazyInitializationException;
32 import org.hibernate.Session;
33 import org.hibernate.SessionException;
34 import org.hibernate.TransientObjectException;
35 import org.hibernate.engine.spi.EntityKey;
36 import org.hibernate.engine.spi.SessionFactoryImplementor;
37 import org.hibernate.engine.spi.SessionImplementor;
38 import org.hibernate.internal.SessionFactoryRegistry;
39 import org.hibernate.persister.entity.EntityPersister;
40 import org.jboss.logging.Logger;
41
42 import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteConfiguration;
43 import eu.etaxonomy.cdm.model.common.CdmBase;
44 import eu.etaxonomy.taxeditor.remoting.cache.ProxyUtils;
45 import eu.etaxonomy.taxeditor.service.ICachedCommonService;
46
47 /**
48 * Convenience base class for lazy initialization handlers. Centralizes the basic plumbing of doing lazy
49 * initialization freeing subclasses to acts as essentially adapters to their intended entity mode and/or
50 * proxy generation strategy.
51 *
52 * @author Gavin King
53 */
54 public abstract class AbstractLazyInitializer implements LazyInitializer {
55 private static final Logger log = Logger.getLogger( AbstractLazyInitializer.class );
56
57 private String entityName;
58 private Serializable id;
59 private Object target;
60 private boolean initialized;
61 private boolean readOnly;
62 private boolean unwrap;
63 private transient SessionImplementor session;
64 private Boolean readOnlyBeforeAttachedToSession;
65
66 private String sessionFactoryUuid;
67 private boolean specjLazyLoad = false;
68
69 /**
70 * For serialization from the non-pojo initializers (HHH-3309)
71 */
72 protected AbstractLazyInitializer() {
73 }
74
75 /**
76 * Main constructor.
77 *
78 * @param entityName The name of the entity being proxied.
79 * @param id The identifier of the entity being proxied.
80 * @param session The session owning the proxy.
81 */
82 protected AbstractLazyInitializer(String entityName, Serializable id, SessionImplementor session) {
83 this.entityName = entityName;
84 this.id = id;
85 // initialize other fields depending on session state
86 if ( session == null ) {
87 unsetSession();
88 }
89 else {
90 setSession( session );
91 }
92 }
93
94 @Override
95 public final String getEntityName() {
96 return entityName;
97 }
98
99 @Override
100 public final Serializable getIdentifier() {
101 return id;
102 }
103
104 @Override
105 public final void setIdentifier(Serializable id) {
106 this.id = id;
107 }
108
109 @Override
110 public final boolean isUninitialized() {
111 return !initialized;
112 }
113
114 @Override
115 public final SessionImplementor getSession() {
116 return session;
117 }
118
119 @Override
120 public final void setSession(SessionImplementor s) throws HibernateException {
121 if ( s != session ) {
122 // check for s == null first, since it is least expensive
123 if ( s == null ) {
124 unsetSession();
125 }
126 else if ( isConnectedToSession() ) {
127 //TODO: perhaps this should be some other RuntimeException...
128 throw new HibernateException( "illegally attempted to associate a proxy with two open Sessions" );
129 }
130 else {
131 // s != null
132 session = s;
133 if ( readOnlyBeforeAttachedToSession == null ) {
134 // use the default read-only/modifiable setting
135 final EntityPersister persister = s.getFactory().getEntityPersister( entityName );
136 setReadOnly( s.getPersistenceContext().isDefaultReadOnly() || !persister.isMutable() );
137 }
138 else {
139 // use the read-only/modifiable setting indicated during deserialization
140 setReadOnly( readOnlyBeforeAttachedToSession.booleanValue() );
141 readOnlyBeforeAttachedToSession = null;
142 }
143 }
144 }
145 }
146
147 private static EntityKey generateEntityKeyOrNull(Serializable id, SessionImplementor s, String entityName) {
148 if ( id == null || s == null || entityName == null ) {
149 return null;
150 }
151 return s.generateEntityKey( id, s.getFactory().getEntityPersister( entityName ) );
152 }
153
154 @Override
155 public final void unsetSession() {
156 prepareForPossibleSpecialSpecjInitialization();
157 session = null;
158 readOnly = false;
159 readOnlyBeforeAttachedToSession = null;
160 }
161
162 @Override
163 public final void initialize() throws HibernateException {
164 // In remoting we are sure that session is null
165 // both when using property paths and switching off conversations
166 if(session == null && remoting) {
167 remoteInitialize();
168 }
169 if ( !initialized ) {
170 if ( specjLazyLoad ) {
171 specialSpecjInitialization();
172 }
173 else if ( session == null ) {
174 throw new LazyInitializationException( "could not initialize proxy - no Session" );
175 }
176 else if ( !session.isOpen() ) {
177 throw new LazyInitializationException( "could not initialize proxy - the owning Session was closed" );
178 }
179 else if ( !session.isConnected() ) {
180 throw new LazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
181 }
182 else {
183 target = session.immediateLoad( entityName, id );
184 initialized = true;
185 checkTargetState();
186 }
187 }
188 else {
189 checkTargetState();
190 }
191 }
192
193 protected void specialSpecjInitialization() {
194 if ( session == null ) {
195 //we have a detached collection thats set to null, reattach
196 if ( sessionFactoryUuid == null ) {
197 throw new LazyInitializationException( "could not initialize proxy - no Session" );
198 }
199 try {
200 SessionFactoryImplementor sf = (SessionFactoryImplementor)
201 SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
202 SessionImplementor session = (SessionImplementor) sf.openSession();
203
204 // TODO: On the next major release, add an
205 // 'isJTA' or 'getTransactionFactory' method to Session.
206 /*boolean isJTA = session.getTransactionCoordinator()
207 .getTransactionContext().getTransactionEnvironment()
208 .getTransactionFactory()
209 .compatibleWithJtaSynchronization();
210 */
211 boolean isJTA = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
212
213 if ( !isJTA ) {
214 // Explicitly handle the transactions only if we're not in
215 // a JTA environment. A lazy loading temporary session can
216 // be created even if a current session and transaction are
217 // open (ex: session.clear() was used). We must prevent
218 // multiple transactions.
219 ( ( Session) session ).beginTransaction();
220 }
221
222 try {
223 target = session.immediateLoad( entityName, id );
224 }
225 finally {
226 // make sure the just opened temp session gets closed!
227 try {
228 if ( !isJTA ) {
229 ( ( Session) session ).getTransaction().commit();
230 }
231 ( (Session) session ).close();
232 }
233 catch (Exception e) {
234 log.warn( "Unable to close temporary session used to load lazy proxy associated to no session" );
235 }
236 }
237 initialized = true;
238 checkTargetState();
239 }
240 catch (Exception e) {
241 e.printStackTrace();
242 throw new LazyInitializationException( e.getMessage() );
243 }
244 }
245 else if ( session.isOpen() && session.isConnected() ) {
246 target = session.immediateLoad( entityName, id );
247 initialized = true;
248 checkTargetState();
249 }
250 else {
251 throw new LazyInitializationException( "could not initialize proxy - Session was closed or disced" );
252 }
253 }
254
255 protected void prepareForPossibleSpecialSpecjInitialization() {
256 if ( session != null ) {
257 specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
258
259 if ( specjLazyLoad && sessionFactoryUuid == null ) {
260 try {
261 sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
262 }
263 catch (NamingException e) {
264 //not much we can do if this fails...
265 }
266 }
267 }
268 }
269
270 private void checkTargetState() {
271 if ( !unwrap ) {
272 if ( target == null ) {
273 getSession().getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id );
274 }
275 }
276 }
277
278 /**
279 * Getter for property 'connectedToSession'.
280 *
281 * @return Value for property 'connectedToSession'.
282 */
283 protected final boolean isConnectedToSession() {
284 return getProxyOrNull() != null;
285 }
286
287 private Object getProxyOrNull() {
288 final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() );
289 if ( entityKey != null && session != null && session.isOpen() ) {
290 return session.getPersistenceContext().getProxy( entityKey );
291 }
292 return null;
293 }
294
295 @Override
296 public final Object getImplementation() {
297 initialize();
298 return target;
299 }
300
301 @Override
302 public final void setImplementation(Object target) {
303 this.target = target;
304 initialized = true;
305 }
306
307 @Override
308 public final Object getImplementation(SessionImplementor s) throws HibernateException {
309 final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), s, getEntityName() );
310 return (entityKey == null ? null : s.getPersistenceContext().getEntity( entityKey ));
311 }
312
313 /**
314 * Getter for property 'target'.
315 * <p/>
316 * Same as {@link #getImplementation()} except that this method will not force initialization.
317 *
318 * @return Value for property 'target'.
319 */
320 protected final Object getTarget() {
321 return target;
322 }
323
324 @Override
325 public final boolean isReadOnlySettingAvailable() {
326 return (session != null && !session.isClosed());
327 }
328
329 private void errorIfReadOnlySettingNotAvailable() {
330 if ( session == null ) {
331 throw new TransientObjectException(
332 "Proxy is detached (i.e, session is null). The read-only/modifiable setting is only accessible when the proxy is associated with an open session."
333 );
334 }
335 if ( session.isClosed() ) {
336 throw new SessionException(
337 "Session is closed. The read-only/modifiable setting is only accessible when the proxy is associated with an open session."
338 );
339 }
340 }
341
342 @Override
343 public final boolean isReadOnly() {
344 errorIfReadOnlySettingNotAvailable();
345 return readOnly;
346 }
347
348 @Override
349 public final void setReadOnly(boolean readOnly) {
350 errorIfReadOnlySettingNotAvailable();
351 // only update if readOnly is different from current setting
352 if ( this.readOnly != readOnly ) {
353 final EntityPersister persister = session.getFactory().getEntityPersister( entityName );
354 if ( !persister.isMutable() && !readOnly ) {
355 throw new IllegalStateException( "cannot make proxies for immutable entities modifiable" );
356 }
357 this.readOnly = readOnly;
358 if ( initialized ) {
359 EntityKey key = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() );
360 if ( key != null && session.getPersistenceContext().containsEntity( key ) ) {
361 session.getPersistenceContext().setReadOnly( target, readOnly );
362 }
363 }
364 }
365 }
366
367 /**
368 * Get the read-only/modifiable setting that should be put in affect when it is
369 * attached to a session.
370 * <p/>
371 * This method should only be called during serialization when read-only/modifiable setting
372 * is not available (i.e., isReadOnlySettingAvailable() == false)
373 *
374 * @return null, if the default setting should be used;
375 * true, for read-only;
376 * false, for modifiable
377 *
378 * @throws IllegalStateException if isReadOnlySettingAvailable() == true
379 */
380 protected final Boolean isReadOnlyBeforeAttachedToSession() {
381 if ( isReadOnlySettingAvailable() ) {
382 throw new IllegalStateException(
383 "Cannot call isReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
384 );
385 }
386 return readOnlyBeforeAttachedToSession;
387 }
388
389 /**
390 * Set the read-only/modifiable setting that should be put in affect when it is
391 * attached to a session.
392 * <p/>
393 * This method should only be called during deserialization, before associating
394 * the proxy with a session.
395 *
396 * @param readOnlyBeforeAttachedToSession, the read-only/modifiable setting to use when
397 * associated with a session; null indicates that the default should be used.
398 *
399 * @throws IllegalStateException if isReadOnlySettingAvailable() == true
400 */
401 /* package-private */
402 final void setReadOnlyBeforeAttachedToSession(Boolean readOnlyBeforeAttachedToSession) {
403 if ( isReadOnlySettingAvailable() ) {
404 throw new IllegalStateException(
405 "Cannot call setReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
406 );
407 }
408 this.readOnlyBeforeAttachedToSession = readOnlyBeforeAttachedToSession;
409 }
410
411 @Override
412 public boolean isUnwrap() {
413 return unwrap;
414 }
415
416 @Override
417 public void setUnwrap(boolean unwrap) {
418 this.unwrap = unwrap;
419 }
420
421 /** Below is section of code which makes remote service calls */
422
423 private static CdmApplicationRemoteConfiguration configuration;
424 private static boolean remoting = false;
425
426 public static void setConfiguration(CdmApplicationRemoteConfiguration conf) {
427 remoting = true;
428 configuration = conf;
429 }
430
431
432 private void remoteInitialize() {
433
434 if(!initialized) {
435 int classid = ((Integer)getIdentifier()).intValue();
436 log.info("--> Remote Lazy Initializing Object " + getEntityName() + " with id " + classid);
437 Class clazz;
438 try {
439 clazz = Class.forName(getEntityName());
440 } catch (ClassNotFoundException e) {
441 throw new HibernateException("Class for " + getEntityName() + " not found", e);
442 }
443 if(configuration == null) {
444 throw new HibernateException("CdmApplicationRemoteConfiguration not initialized (null)");
445 }
446 ICachedCommonService cachedCommonService = configuration.getCachedCommonService();
447 if(cachedCommonService == null) {
448 throw new HibernateException("commonService not initialized (null)");
449 }
450
451 CdmBase cdmBase = cachedCommonService.find(clazz,classid);
452 if(ProxyUtils.isProxy(cdmBase)) {
453 throw new HibernateException("CdmBase Object initialized but is still a proxy");
454 }
455 setImplementation(cdmBase);
456
457 }
458 }
459
460 public static boolean isInitialized(AbstractLazyInitializer obj) {
461 return obj.initialized;
462 }
463 }