2 * Hibernate, Relational Persistence for Idiomatic Java
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.
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.
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
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
24 package org
.hibernate
.proxy
;
26 import java
.io
.Serializable
;
28 import javax
.naming
.NamingException
;
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 import org
.springframework
.beans
.factory
.annotation
.Autowire
;
42 import org
.springframework
.beans
.factory
.annotation
.Configurable
;
43 import org
.springframework
.stereotype
.Component
;
45 import eu
.etaxonomy
.cdm
.api
.application
.ICdmApplicationConfiguration
;
46 import eu
.etaxonomy
.cdm
.api
.service
.ICommonService
;
47 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
50 * Convenience base class for lazy initialization handlers. Centralizes the basic plumbing of doing lazy
51 * initialization freeing subclasses to acts as essentially adapters to their intended entity mode and/or
52 * proxy generation strategy.
57 @Configurable(dependencyCheck
= true,autowire
= Autowire
.BY_TYPE
)
58 public abstract class AbstractLazyInitializer
implements LazyInitializer
{
59 private static final Logger log
= Logger
.getLogger( AbstractLazyInitializer
.class );
61 private String entityName
;
62 private Serializable id
;
63 private Object target
;
64 private boolean initialized
;
65 private boolean readOnly
;
66 private boolean unwrap
;
67 private transient SessionImplementor session
;
68 private Boolean readOnlyBeforeAttachedToSession
;
70 private String sessionFactoryUuid
;
71 private boolean specjLazyLoad
= false;
74 * For serialization from the non-pojo initializers (HHH-3309)
76 protected AbstractLazyInitializer() {
82 * @param entityName The name of the entity being proxied.
83 * @param id The identifier of the entity being proxied.
84 * @param session The session owning the proxy.
86 protected AbstractLazyInitializer(String entityName
, Serializable id
, SessionImplementor session
) {
87 this.entityName
= entityName
;
89 // initialize other fields depending on session state
90 if ( session
== null ) {
94 setSession( session
);
99 public final String
getEntityName() {
104 public final Serializable
getIdentifier() {
109 public final void setIdentifier(Serializable id
) {
114 public final boolean isUninitialized() {
119 public final SessionImplementor
getSession() {
124 public final void setSession(SessionImplementor s
) throws HibernateException
{
125 if ( s
!= session
) {
126 // check for s == null first, since it is least expensive
130 else if ( isConnectedToSession() ) {
131 //TODO: perhaps this should be some other RuntimeException...
132 throw new HibernateException( "illegally attempted to associate a proxy with two open Sessions" );
137 if ( readOnlyBeforeAttachedToSession
== null ) {
138 // use the default read-only/modifiable setting
139 final EntityPersister persister
= s
.getFactory().getEntityPersister( entityName
);
140 setReadOnly( s
.getPersistenceContext().isDefaultReadOnly() || !persister
.isMutable() );
143 // use the read-only/modifiable setting indicated during deserialization
144 setReadOnly( readOnlyBeforeAttachedToSession
.booleanValue() );
145 readOnlyBeforeAttachedToSession
= null;
151 private static EntityKey
generateEntityKeyOrNull(Serializable id
, SessionImplementor s
, String entityName
) {
152 if ( id
== null || s
== null || entityName
== null ) {
155 return s
.generateEntityKey( id
, s
.getFactory().getEntityPersister( entityName
) );
159 public final void unsetSession() {
160 prepareForPossibleSpecialSpecjInitialization();
163 readOnlyBeforeAttachedToSession
= null;
167 public final void initialize() throws HibernateException
{
168 // FIXME:Remoting is it true that session is null => we are in remoting mode
169 // if(session == null) {
170 // remoteInitialize();
172 if ( !initialized
) {
173 if ( specjLazyLoad
) {
174 specialSpecjInitialization();
176 else if ( session
== null ) {
177 throw new LazyInitializationException( "could not initialize proxy - no Session" );
179 else if ( !session
.isOpen() ) {
180 throw new LazyInitializationException( "could not initialize proxy - the owning Session was closed" );
182 else if ( !session
.isConnected() ) {
183 throw new LazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
186 target
= session
.immediateLoad( entityName
, id
);
196 protected void specialSpecjInitialization() {
197 if ( session
== null ) {
198 //we have a detached collection thats set to null, reattach
199 if ( sessionFactoryUuid
== null ) {
200 throw new LazyInitializationException( "could not initialize proxy - no Session" );
203 SessionFactoryImplementor sf
= (SessionFactoryImplementor
)
204 SessionFactoryRegistry
.INSTANCE
.getSessionFactory( sessionFactoryUuid
);
205 SessionImplementor session
= (SessionImplementor
) sf
.openSession();
207 // TODO: On the next major release, add an
208 // 'isJTA' or 'getTransactionFactory' method to Session.
209 boolean isJTA
= session
.getTransactionCoordinator()
210 .getTransactionContext().getTransactionEnvironment()
211 .getTransactionFactory()
212 .compatibleWithJtaSynchronization();
215 // Explicitly handle the transactions only if we're not in
216 // a JTA environment. A lazy loading temporary session can
217 // be created even if a current session and transaction are
218 // open (ex: session.clear() was used). We must prevent
219 // multiple transactions.
220 ( ( Session
) session
).beginTransaction();
224 target
= session
.immediateLoad( entityName
, id
);
227 // make sure the just opened temp session gets closed!
230 ( ( Session
) session
).getTransaction().commit();
232 ( (Session
) session
).close();
234 catch (Exception e
) {
235 log
.warn( "Unable to close temporary session used to load lazy proxy associated to no session" );
241 catch (Exception e
) {
243 throw new LazyInitializationException( e
.getMessage() );
246 else if ( session
.isOpen() && session
.isConnected() ) {
247 target
= session
.immediateLoad( entityName
, id
);
252 throw new LazyInitializationException( "could not initialize proxy - Session was closed or disced" );
256 protected void prepareForPossibleSpecialSpecjInitialization() {
257 if ( session
!= null ) {
258 specjLazyLoad
= session
.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
260 if ( specjLazyLoad
&& sessionFactoryUuid
== null ) {
262 sessionFactoryUuid
= (String
) session
.getFactory().getReference().get( "uuid" ).getContent();
264 catch (NamingException e
) {
265 //not much we can do if this fails...
271 private void checkTargetState() {
273 if ( target
== null ) {
274 getSession().getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName
, id
);
280 * Getter for property 'connectedToSession'.
282 * @return Value for property 'connectedToSession'.
284 protected final boolean isConnectedToSession() {
285 return getProxyOrNull() != null;
288 private Object
getProxyOrNull() {
289 final EntityKey entityKey
= generateEntityKeyOrNull( getIdentifier(), session
, getEntityName() );
290 if ( entityKey
!= null && session
!= null && session
.isOpen() ) {
291 return session
.getPersistenceContext().getProxy( entityKey
);
297 public final Object
getImplementation() {
303 public final void setImplementation(Object target
) {
304 this.target
= target
;
309 public final Object
getImplementation(SessionImplementor s
) throws HibernateException
{
310 final EntityKey entityKey
= generateEntityKeyOrNull( getIdentifier(), s
, getEntityName() );
311 return (entityKey
== null ?
null : s
.getPersistenceContext().getEntity( entityKey
));
315 * Getter for property 'target'.
317 * Same as {@link #getImplementation()} except that this method will not force initialization.
319 * @return Value for property 'target'.
321 protected final Object
getTarget() {
326 public final boolean isReadOnlySettingAvailable() {
327 return (session
!= null && !session
.isClosed());
330 private void errorIfReadOnlySettingNotAvailable() {
331 if ( session
== null ) {
332 throw new TransientObjectException(
333 "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."
336 if ( session
.isClosed() ) {
337 throw new SessionException(
338 "Session is closed. The read-only/modifiable setting is only accessible when the proxy is associated with an open session."
344 public final boolean isReadOnly() {
345 errorIfReadOnlySettingNotAvailable();
350 public final void setReadOnly(boolean readOnly
) {
351 errorIfReadOnlySettingNotAvailable();
352 // only update if readOnly is different from current setting
353 if ( this.readOnly
!= readOnly
) {
354 final EntityPersister persister
= session
.getFactory().getEntityPersister( entityName
);
355 if ( !persister
.isMutable() && !readOnly
) {
356 throw new IllegalStateException( "cannot make proxies for immutable entities modifiable" );
358 this.readOnly
= readOnly
;
360 EntityKey key
= generateEntityKeyOrNull( getIdentifier(), session
, getEntityName() );
361 if ( key
!= null && session
.getPersistenceContext().containsEntity( key
) ) {
362 session
.getPersistenceContext().setReadOnly( target
, readOnly
);
369 * Get the read-only/modifiable setting that should be put in affect when it is
370 * attached to a session.
372 * This method should only be called during serialization when read-only/modifiable setting
373 * is not available (i.e., isReadOnlySettingAvailable() == false)
375 * @return null, if the default setting should be used;
376 * true, for read-only;
377 * false, for modifiable
379 * @throws IllegalStateException if isReadOnlySettingAvailable() == true
381 protected final Boolean
isReadOnlyBeforeAttachedToSession() {
382 if ( isReadOnlySettingAvailable() ) {
383 throw new IllegalStateException(
384 "Cannot call isReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
387 return readOnlyBeforeAttachedToSession
;
391 * Set the read-only/modifiable setting that should be put in affect when it is
392 * attached to a session.
394 * This method should only be called during deserialization, before associating
395 * the proxy with a session.
397 * @param readOnlyBeforeAttachedToSession, the read-only/modifiable setting to use when
398 * associated with a session; null indicates that the default should be used.
400 * @throws IllegalStateException if isReadOnlySettingAvailable() == true
402 /* package-private */
403 final void setReadOnlyBeforeAttachedToSession(Boolean readOnlyBeforeAttachedToSession
) {
404 if ( isReadOnlySettingAvailable() ) {
405 throw new IllegalStateException(
406 "Cannot call setReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
409 this.readOnlyBeforeAttachedToSession
= readOnlyBeforeAttachedToSession
;
413 public boolean isUnwrap() {
418 public void setUnwrap(boolean unwrap
) {
419 this.unwrap
= unwrap
;
422 /** Below is section of code which makes remote service calls */
424 private static ICdmApplicationConfiguration configuration
;
426 public static void setConfiguration(ICdmApplicationConfiguration conf
) {
427 configuration
= conf
;
431 private void remoteInitialize() {
434 int classid
= ((Integer
)getIdentifier()).intValue();
435 System
.out
.print("--> Remote Lazy Initializing" + getEntityName() + " with id " + classid
);
438 clazz
= (Class
<?
extends CdmBase
>) Class
.forName(getEntityName());
439 } catch (ClassNotFoundException e
) {
440 throw new HibernateException("Class for " + getEntityName() + " not found", e
);
442 if(configuration
== null) {
443 throw new HibernateException("CdmApplicationRemoteConfiguration not initialized (null)");
445 ICommonService commonService
= configuration
.getCommonService();
446 if(commonService
== null) {
447 throw new HibernateException("commonService not initialized (null)");
450 CdmBase cdmBase
= CdmBase
.deproxy(commonService
.find(clazz
,classid
),clazz
);
451 setImplementation(cdmBase
);
452 System
.out
.println("....Done");