001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2012 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.client.tide.data.impl;
022    
023    import java.util.ArrayList;
024    import java.util.Arrays;
025    import java.util.HashMap;
026    import java.util.HashSet;
027    import java.util.List;
028    import java.util.Map;
029    
030    import org.granite.logging.Logger;
031    import org.granite.client.messaging.RemoteService;
032    import org.granite.client.messaging.ResultFaultIssuesResponseListener;
033    import org.granite.client.messaging.events.FaultEvent;
034    import org.granite.client.messaging.events.IssueEvent;
035    import org.granite.client.messaging.events.ResultEvent;
036    import org.granite.client.tide.Context;
037    import org.granite.tide.Expression;
038    import org.granite.client.tide.collections.ManagedPersistentAssociation;
039    import org.granite.client.tide.data.EntityManager;
040    import org.granite.client.tide.data.Identifiable;
041    import org.granite.client.tide.data.PersistenceManager;
042    import org.granite.client.tide.data.RemoteInitializer;
043    import org.granite.tide.invocation.InvocationCall;
044    import org.granite.tide.invocation.InvocationResult;
045    import org.granite.client.tide.server.ServerSession;
046    
047    /**
048     * @author William DRAI
049     */
050    public class RemoteInitializerImpl implements RemoteInitializer {
051            
052            private static final Logger log = Logger.getLogger(RemoteInitializerImpl.class);
053            
054            private final Context context;
055            private boolean enabled = true;
056    
057            
058            public RemoteInitializerImpl(Context context) {
059                    this.context = context;
060            }
061    
062            @Override
063            public void setEnabled(boolean enabled) {
064                    this.enabled = enabled;
065            }
066    
067            @Override
068            public boolean isEnabled() {
069                    return enabled;
070            }
071            
072            private List<Object[]> objectsInitializing = new ArrayList<Object[]>();
073        
074            /**
075             *      {@inheritdoc}
076             */
077        public boolean initializeObject(ServerSession serverSession, Object object) {
078                    if (!enabled || context.isFinished())
079                            return false;
080                    
081                    log.debug("initialize {0}", ObjectUtil.toString(object));
082                    
083                    if (!(object instanceof ManagedPersistentAssociation && ((ManagedPersistentAssociation)object).getOwner() instanceof Identifiable))
084                            return false;
085                    
086                    Object entity = ((ManagedPersistentAssociation)object).getOwner();
087                    EntityManager entityManager = PersistenceManager.getEntityManager(entity);
088                    Expression path = null;
089                    
090                    if (context.getContextId() != null && context.isContextIdFromServer())
091                            path = entityManager.getReference(entity, false, new HashSet<Object>());
092                    
093                    entityManager.addReference(entity, null, null, null);
094                    
095                    synchronized (objectsInitializing) {
096                            objectsInitializing.add(new Object[] { context, path != null ? path.getPath() : entity, ((ManagedPersistentAssociation)object).getPropertyName() });
097                    }
098                    
099                    context.callLater(new DoInitializeObjects(serverSession));
100                    return true;
101            }
102        
103        public class DoInitializeObjects implements Runnable {
104            
105            private final ServerSession serverSession;
106            
107            public DoInitializeObjects(ServerSession serverSession) {
108                    this.serverSession = serverSession;
109            }
110            
111            public void run() {
112                    Map<Object, List<String>> initMap = new HashMap<Object, List<String>>();
113                            
114                    synchronized (objectsInitializing) {
115                                    for (int i = 0; i < objectsInitializing.size(); i++) {
116                                            if (objectsInitializing.get(i)[0] != context)
117                                                    continue;
118                                            
119                                            List<String> propertyNames = initMap.get(objectsInitializing.get(i)[1]);
120                                            if (propertyNames == null) {
121                                                    propertyNames = Arrays.asList((String)objectsInitializing.get(i)[2]);
122                                                    initMap.put(objectsInitializing.get(i)[1], propertyNames);
123                                            }
124                                            else
125                                                    propertyNames.add((String)objectsInitializing.get(i)[2]);
126                                            
127                                            objectsInitializing.remove(i--);
128                                    }
129                    }
130                            
131                    RemoteService rs = serverSession.getRemoteService();
132                            for (Object entity : initMap.keySet()) {
133                                    rs.newInvocation("initializeObject", entity, initMap.get(entity).toArray(), new InvocationCall())
134                                            .addListener(new InitializerListener(serverSession, entity)).invoke();
135                                    
136                            }
137            }
138            }
139            
140        
141        public class InitializerListener extends ResultFaultIssuesResponseListener {
142            
143            private final ServerSession serverSession;
144            private final Object entity;
145            
146            public InitializerListener(ServerSession serverSession, Object entity) {
147                    this.serverSession = serverSession;
148                    this.entity = entity;
149            }
150    
151                    @Override
152                    public void onResult(final ResultEvent event) {
153                            context.callLater(new Runnable() {
154                                    public void run() {
155                                            EntityManager entityManager = PersistenceManager.getEntityManager(entity);
156                                            
157                                            boolean saveUninitializeAllowed = entityManager.isUninitializeAllowed();
158                                            try {
159                                                    entityManager.setUninitializeAllowed(false);
160                                                    
161                                                    // Assumes objects is a PersistentCollection or PersistentMap
162                                                    serverSession.handleResult(context, null, null, (InvocationResult)event.getResult(), ((InvocationResult)event.getResult()).getResult(), null);
163                                            }
164                                            finally {
165                                                    entityManager.setUninitializeAllowed(saveUninitializeAllowed);
166                                            }
167                                    }
168                            });
169                    }
170    
171                    @Override
172                    public void onFault(final FaultEvent event) {
173                            context.callLater(new Runnable() {
174                                    public void run() {
175                                            log.error("Fault initializing collection " + ObjectUtil.toString(entity) + " " + event.toString());
176                                            
177                                            serverSession.handleFault(context, null, null, event.getMessage());
178                                    }
179                            });
180                    }       
181    
182                    @Override
183                    public void onIssue(final IssueEvent event) {
184                            context.callLater(new Runnable() {
185                                    public void run() {
186                                            log.error("Fault initializing collection " + ObjectUtil.toString(entity) + " " + event.toString());
187                                            
188                                            serverSession.handleFault(context, null, null, null);
189                                    }
190                            });
191                    }       
192        }
193    }