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 }