001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004    
005      This file is part of Granite Data Services.
006    
007      Granite Data Services is free software; you can redistribute it and/or modify
008      it under the terms of the GNU Library General Public License as published by
009      the Free Software Foundation; either version 2 of the License, or (at your
010      option) any later version.
011    
012      Granite Data Services is distributed in the hope that it will be useful, but
013      WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014      FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015      for more details.
016    
017      You should have received a copy of the GNU Library General Public License
018      along with this library; if not, see <http://www.gnu.org/licenses/>.
019    */
020    
021    package org.granite.tide.seam.lazy;
022    
023    import static org.jboss.seam.annotations.Install.FRAMEWORK;
024    
025    import java.io.Serializable;
026    import java.util.HashSet;
027    import java.util.Set;
028    
029    import javax.persistence.EntityManager;
030    
031    import org.granite.tide.TidePersistenceManager;
032    import org.granite.tide.data.DataMergeContext;
033    import org.jboss.seam.Component;
034    import org.jboss.seam.ScopeType;
035    import org.jboss.seam.annotations.Install;
036    import org.jboss.seam.annotations.Logger;
037    import org.jboss.seam.annotations.Name;
038    import org.jboss.seam.annotations.Scope;
039    import org.jboss.seam.annotations.Transactional;
040    import org.jboss.seam.annotations.intercept.BypassInterceptors;
041    import org.jboss.seam.core.Expressions;
042    import org.jboss.seam.log.Log;
043    
044    /**
045     * Initializes a request for a passed in entity and a lazy property.
046     
047     * @author CIngram,VDanda
048     */
049    @Name("org.granite.tide.seam.seamInitializer")
050    @Scope(ScopeType.CONVERSATION)
051    @Install(precedence=FRAMEWORK)
052    @BypassInterceptors
053    public class SeamInitializer implements Serializable {
054    
055        private static final long serialVersionUID = 1L;
056            
057        @Logger Log log;
058        
059        private transient TidePersistenceManager pm = null;
060        
061        private Set<Object> loadedEntities = new HashSet<Object>();
062        
063        
064        /**
065         * Initiliazes the property for the passed in entity. It is attached to an associated context
066         * and then the property is accessed.
067         * @return Returns result from initializing the property.   
068         */
069            @Transactional
070        public Object lazyInitialize(Object entity, String[] propertyNames) {
071                    boolean removeAfterCall = false;
072                    
073                    restoreLoadedEntities();
074                    
075                    try {
076                        if (entity instanceof String) {
077                    String expression = "${" + entity + "}";
078                    Expressions.ValueExpression<Object> valueExpr = Expressions.instance().createValueExpression(expression, Object.class);
079                    entity = valueExpr.getValue();
080                        }
081                        
082                            if (pm == null) {
083                                    removeAfterCall = true;
084                                pm = tryToDetermineInitiailzer();
085                                    if (pm == null)
086                                        throw new RuntimeException("TideInitializer is null, Entities with Lazy relationships have to be retrieved in a conversation, or the EntityManager name must be entityManager");
087                            }       
088                            
089                            Object initializedEntity = pm.attachEntity(entity, propertyNames);
090    
091                            saveLoadedEntities();
092                    
093                            return initializedEntity;
094                    } finally {
095                            if (removeAfterCall)
096                                    Component.forName("org.granite.tide.seam.seamInitializer").destroy(this);
097                    }
098            }
099            
100            
101            /**
102             * Try to determine what type of persistence the application is using. 
103             * If the EntityManager is stored under entityManager or if the Hibernate session is 
104             * stored under session. Then the context will be found and used. This is only called if a 
105             * ITidePersistenceManager is not found, probably because the query was not run in a conversation.
106             * @return The appropriate manager for the persistence context being used, if it can be determined
107             * otherwise a null is returned. 
108             */  
109            protected TidePersistenceManager tryToDetermineInitiailzer() {
110                    EntityManager em = findEntityManager();
111                    if (em != null) 
112                            return TidePersistenceFactory.createTidePersistence(null, em);
113                    
114                    return null;
115            }
116            
117            /**
118             * Try to find the entityManager if possible. Assume that the entityManager is stored under
119             * entityManager.
120             * @return The Current Entity manager
121             */
122            protected EntityManager findEntityManager() {
123                    return (EntityManager) Component.getInstance("entityManager");
124            }
125            
126            /**
127             * @return A instance of this component for the conversation.
128             */
129        public static SeamInitializer instance() { 
130            return (SeamInitializer)Component.getInstance(SeamInitializer.class);
131        }
132            
133        public void setTidePersistenceManager(TidePersistenceManager pm) {
134            this.pm = pm;
135        }
136        
137        public TidePersistenceManager getTidePersistenceManager() {
138            return this.pm;
139        }
140        
141        public void restoreLoadedEntities() {
142            DataMergeContext.restoreLoadedEntities(loadedEntities);
143        }
144        
145        public void saveLoadedEntities() {
146            for (Object entity : DataMergeContext.getLoadedEntities())
147                    loadedEntities.add(entity);
148        }
149    }
150