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.data;
022    
023    import java.util.Collections;
024    import java.util.Comparator;
025    import java.util.Iterator;
026    import java.util.Set;
027    import java.util.TreeSet;
028    
029    import org.granite.gravity.Gravity;
030    import org.granite.logging.Logger;
031    import org.granite.tide.data.DataEnabled.PublishMode;
032    
033    
034    /**
035     * @author William DRAI
036     */
037    public class DataContext {
038        
039            private static final Logger log = Logger.getLogger(DataContext.class);
040            
041        private static ThreadLocal<DataContext> dataContext = new ThreadLocal<DataContext>();
042        
043        private static DataContext NULL_DATA_CONTEXT = new NullDataContext(); 
044        
045        private DataDispatcher dataDispatcher = null;
046        private PublishMode publishMode = null;
047        private Object[][] updates = null;
048        private DataUpdatePostprocessor dataUpdatePostprocessor = null; 
049        
050        
051        public static void init() {
052            if (dataContext.get() == null)
053                    dataContext.set(NULL_DATA_CONTEXT);
054        }
055        
056        public static void init(String topic, Class<? extends DataTopicParams> dataTopicParamsClass, PublishMode publishMode) {
057            DataContext dc = new DataContext(null, topic, dataTopicParamsClass, publishMode);
058            dataContext.set(dc);
059        }
060        
061        public static void init(Gravity gravity, String topic, Class<? extends DataTopicParams> dataTopicParamsClass, PublishMode publishMode) {
062            DataContext dc = new DataContext(gravity, topic, dataTopicParamsClass, publishMode);
063            dataContext.set(dc);
064        }
065        
066        public static void init(DataDispatcher dataDispatcher, PublishMode publishMode) {
067                    DataContext dc = new DataContext(dataDispatcher, publishMode);
068                    dataContext.set(dc);
069        }
070        
071        private DataContext(Gravity gravity, String topic, Class<? extends DataTopicParams> dataTopicParamsClass, PublishMode publishMode) {
072                    log.debug("Init Gravity data context for topic %s and mode %s", topic, publishMode);
073                    this.dataDispatcher = new DefaultDataDispatcher(gravity, topic, dataTopicParamsClass);
074                    this.publishMode = publishMode;
075        }
076    
077        private DataContext(DataDispatcher dataDispatcher, PublishMode publishMode) {
078                    log.debug("Init data context with custom dispatcher %s and mode %s", dataDispatcher, publishMode);
079                    this.dataDispatcher = dataDispatcher;
080                    this.publishMode = publishMode;
081        }
082    
083        public static DataContext get() {
084            return dataContext.get();
085        }
086        
087        public static void remove() {
088                    log.debug("Remove data context");
089            dataContext.remove();
090        }
091        
092        public static boolean isNull() {
093            return dataContext.get() == NULL_DATA_CONTEXT;
094        }
095        
096        private final Set<Object[]> dataUpdates = new TreeSet<Object[]>(new Comparator<Object[]>() {
097                    public int compare(Object[] o1, Object[] o2) {
098                        if (!((Integer)o1[2]).equals(o2[2]))
099                            return (Integer)o1[2] - (Integer)o2[2];
100                        if (!o1[1].equals(o2[1]))
101                            return o1[1].hashCode() - o2[1].hashCode();
102                        return ((EntityUpdateType)o1[0]).ordinal() - ((EntityUpdateType)o2[0]).ordinal();
103                    }
104        });
105        private boolean published = false;
106    
107        
108        public Set<Object[]> getDataUpdates() {
109            return dataUpdates;
110        }
111        
112        public Object[][] getUpdates() {
113            if (updates != null)
114                    return updates;
115            
116            if (dataUpdates == null || dataUpdates.isEmpty())
117                    return null;
118            updates = new Object[dataUpdates.size()][];
119            int i = 0;
120            Iterator<Object[]> iu = dataUpdates.iterator();
121            while (iu.hasNext()) {
122                    Object[] u = iu.next();
123                    updates[i++] = new Object[] { ((EntityUpdateType)u[0]).name(), dataUpdatePostprocessor != null ? dataUpdatePostprocessor.process(u[1]) : u[1] }; 
124            }
125                    return updates;
126        }
127        
128        public void setDataUpdatePostprocessor(DataUpdatePostprocessor dataUpdatePostprocessor) {
129            this.dataUpdatePostprocessor = dataUpdatePostprocessor;
130        }
131        
132        public static void addUpdate(EntityUpdateType type, Object entity) {
133            addUpdate(type, entity, 0);     
134        }
135        public static void addUpdate(EntityUpdateType type, Object entity, int priority) {
136            DataContext dc = get();
137            if (dc != null && dc.dataDispatcher != null) {
138                    for (Object[] update : dc.dataUpdates) {
139                            if (update[0].equals(type) && update[1].equals(entity)) {
140                                    if ((Integer)update[2] < priority)
141                                            update[2] = priority;
142                                    return;
143                            }
144                    }
145                    dc.dataUpdates.add(new Object[] { type, entity, priority });
146                    dc.updates = null;
147            }
148        }
149        
150        
151        public static void observe() {
152            DataContext dc = get();
153            if (dc != null && dc.dataDispatcher != null) {
154                    log.debug("Observe data updates");
155                    dc.dataDispatcher.observe();
156            }
157        }
158        
159        public static void publish() {
160            publish(PublishMode.MANUAL);
161        }
162        public static void publish(PublishMode publishMode) {
163            DataContext dc = get();
164            if (dc != null && dc.dataDispatcher != null && !dc.dataUpdates.isEmpty() && !dc.published 
165                    && (publishMode == PublishMode.MANUAL || (dc.publishMode.equals(publishMode)))) {
166                    log.debug("Publish %s data updates with mode %s", dc.dataUpdates.size(), dc.publishMode);
167                    dc.dataDispatcher.publish(dc.getUpdates());
168                    // Publish can be called only once but we have to keep the updates until the end of a GraniteDS request
169                    dc.published = true;    
170            }
171        }
172        
173        
174        public enum EntityUpdateType {
175            PERSIST,
176            UPDATE,
177            REMOVE
178        }
179        
180        private static class NullDataContext extends DataContext {
181            
182            public NullDataContext() {
183                    super(null, null);
184            }
185            
186            @Override
187            public Set<Object[]> getDataUpdates() {
188                    return Collections.emptySet();
189            }
190        }
191    }