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