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 */ 022package org.granite.tide.data; 023 024import java.util.ArrayList; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028import java.util.Map.Entry; 029 030import org.granite.clustering.DistributedData; 031import org.granite.context.GraniteContext; 032import org.granite.logging.Logger; 033 034import flex.messaging.messages.AsyncMessage; 035 036 037/** 038 * Base implementation for data update dispatchers. 039 * It should be built at beginning of each request during initialization of <code>DataContext</code>. 040 * The dispatch is a three step process : 041 * 042 * <ul> 043 * <li>Initialization in the constructor</li> 044 * <li><code>observe()</code> builds the server selector depending on the data that are processed</li> 045 * <li><code>publish()</code> handles the actual publishing</li> 046 * </ul> 047 * 048 * Actual implementations should only override <code>changeDataSelector</code> and <code>publishUpdate</code>/ 049 * 050 * @see DataDispatcher 051 * @see DataContext 052 * 053 * @author William Drai 054 */ 055public abstract class AbstractDataDispatcher implements DataDispatcher { 056 057 private static final Logger log = Logger.getLogger(AbstractDataDispatcher.class); 058 059 060 protected boolean enabled; 061 protected String topicName = null; 062 protected DataTopicParams paramsProvider = null; 063 protected String sessionId = null; 064 protected String clientId = null; 065 protected String subscriptionId = null; 066 067 068 public AbstractDataDispatcher(String topicName, Class<? extends DataTopicParams> dataTopicParamsClass) { 069 this.topicName = topicName; 070 071 try { 072 paramsProvider = dataTopicParamsClass.newInstance(); 073 } 074 catch (Exception e) { 075 log.error("Could not instantiate class " + dataTopicParamsClass, e); 076 } 077 } 078 079 080 public void observe() { 081 // Prepare the selector even if we are not yet subscribed 082 DataObserveParams params = null; 083 if (paramsProvider != null) { 084 // Collect selector parameters from component 085 params = new DataObserveParams(); 086 paramsProvider.observes(params); 087 } 088 089 // Ensure that the current Gravity consumer listens about this data topic and params 090 GraniteContext graniteContext = GraniteContext.getCurrentInstance(); 091 if (graniteContext == null) 092 return; 093 094 DistributedData gdd = graniteContext.getGraniteConfig().getDistributedDataFactory().getInstance(); 095 if (gdd == null) 096 return; // Session expired 097 098 List<DataObserveParams> selectors = DataObserveParams.fromSerializableForm(gdd.getDestinationDataSelectors(topicName)); 099 List<DataObserveParams> newSelectors = new ArrayList<DataObserveParams>(selectors); 100 101 boolean dataSelectorChanged = false; 102 String dataSelector = gdd.getDestinationSelector(topicName); 103 if (params != null) { 104 String newDataSelector = params.updateDataSelector(dataSelector, newSelectors); 105 dataSelectorChanged = !newDataSelector.equals(dataSelector); 106 if (dataSelectorChanged) { 107 log.debug("Data selector changed: %s", newDataSelector); 108 gdd.setDestinationSelector(topicName, newDataSelector); 109 dataSelector = newDataSelector; 110 } 111 } 112 113 if (!DataObserveParams.containsSame(selectors, newSelectors)) { 114 log.debug("Selectors changed: %s", newSelectors); 115 gdd.setDestinationDataSelectors(topicName, DataObserveParams.toSerializableForm(newSelectors)); 116 } 117 118 if (!enabled) 119 return; 120 121 if (dataSelectorChanged) 122 changeDataSelector(dataSelector); 123 } 124 125 protected abstract void changeDataSelector(String dataSelector); 126 127 128 public void publish(Object[][] dataUpdates) { 129 if (!enabled) 130 return; 131 132 try { 133 Map<Map<String, String>, List<Object>> updates = new HashMap<Map<String, String>, List<Object>>(); 134 if (paramsProvider != null) { 135 for (Object[] dataUpdate : dataUpdates) { 136 DataPublishParams params = new DataPublishParams(); 137 paramsProvider.publishes(params, dataUpdate[1]); 138 139 Map<String, String> headers = params.getHeaders(); 140 List<Object> list = updates.get(headers); 141 if (list == null) { 142 list = new ArrayList<Object>(); 143 updates.put(headers, list); 144 } 145 list.add(dataUpdate); 146 } 147 } 148 149 for (Entry<Map<String, String>, List<Object>> me : updates.entrySet()) { 150 Map<String, String> headers = new HashMap<String, String>(me.getKey()); 151 headers.put(AsyncMessage.SUBTOPIC_HEADER, TIDE_DATA_SUBTOPIC); 152 headers.put(GDS_SESSION_ID, sessionId); 153 headers.put(TIDE_DATA_TYPE_KEY, TIDE_DATA_TYPE_VALUE); 154 publishUpdate(headers, me.getValue().toArray()); 155 } 156 } 157 catch (Exception e) { 158 log.error(e, "Could not publish data update on topic %s", topicName); 159 } 160 } 161 162 protected abstract void publishUpdate(Map<String, String> params, Object body); 163}