/******************************************************************************* * Copyright (c) 2011 epyx SA. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package ch.windmobile.server.datasourcemodel; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.windmobile.server.datasourcemodel.DataSourceException.Error; import ch.windmobile.server.datasourcemodel.xml.Chart; import ch.windmobile.server.datasourcemodel.xml.StationData; import ch.windmobile.server.datasourcemodel.xml.StationInfo; import ch.windmobile.server.datasourcemodel.xml.StationUpdateTime; public class AggregatedDataSource implements WindMobileDataSource { protected final Logger log = LoggerFactory.getLogger(getClass()); private final int timeout; private final ExecutorService executor; private Map<String, WindMobileDataSource> dataSources; public AggregatedDataSource(int corePoolSize, int maximumPoolSize, int timeout) { this.timeout = timeout; executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, timeout, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.CallerRunsPolicy()); } public Map<String, WindMobileDataSource> getDataSources() { return dataSources; } public void setDataSources(Map<String, WindMobileDataSource> dataSources) { this.dataSources = dataSources; } private DataSourceException exceptionHandler(Exception e) { if (e instanceof DataSourceException) { return (DataSourceException) e; } else { return new DataSourceException(Error.SERVER_ERROR, e); } } @Override public StationUpdateTime getLastUpdate(String stationId) throws DataSourceException { try { AggregatedId aggregatedId = new AggregatedId(stationId); WindMobileDataSource dataSource = getDataSources().get(aggregatedId.getDataSourceKey()); return dataSource.getLastUpdate(aggregatedId.getStationId()); } catch (Exception e) { throw exceptionHandler(e); } } @Override public List<StationInfo> getStationInfoList(final boolean allStation) throws DataSourceException { try { List<AggregatedCallable<List<StationInfo>>> callables = new ArrayList<AggregatedCallable<List<StationInfo>>>(); Set<String> keys = getDataSources().keySet(); for (Iterator<String> iterator = keys.iterator(); iterator.hasNext();) { String key = iterator.next(); final WindMobileDataSource dataSource = getDataSources().get(key); AggregatedCallable<List<StationInfo>> callable = new AggregatedCallable<List<StationInfo>>(key) { @Override public List<StationInfo> call() throws Exception { return dataSource.getStationInfoList(allStation); } }; callables.add(callable); } List<Future<List<StationInfo>>> futures = executor.invokeAll(callables, timeout, TimeUnit.SECONDS); List<StationInfo> aggregatedStationInfos = new ArrayList<StationInfo>(futures.size()); for (int i = 0; i < futures.size(); i++) { Future<List<StationInfo>> future = futures.get(i); try { List<StationInfo> stationInfos = future.get(); // Replace the id by the aggregated id for (StationInfo stationInfo : stationInfos) { stationInfo.setId(AggregatedId.toString(callables.get(i).getDataSourceKey(), stationInfo.getId())); } aggregatedStationInfos.addAll(stationInfos); } catch (ExecutionException e) { log.warn("Could not get StationInfo list:", e); } catch (InterruptedException e) { log.warn("Could not get StationInfo list:", e); } catch (CancellationException e) { log.warn("Could not get StationInfo list:", e); } } Collections.sort(aggregatedStationInfos, new Comparator<StationInfo>() { @Override public int compare(StationInfo stationInfo1, StationInfo stationInfo2) { try { return stationInfo1.getShortName().compareTo(stationInfo2.getShortName()); } catch (Exception e) { return 0; } } }); return aggregatedStationInfos; } catch (Exception e) { throw exceptionHandler(e); } } @Override public StationInfo getStationInfo(String stationId) throws DataSourceException { try { AggregatedId aggregatedId = new AggregatedId(stationId); WindMobileDataSource dataSource = getDataSources().get(aggregatedId.getDataSourceKey()); StationInfo stationInfo = dataSource.getStationInfo(aggregatedId.getStationId()); stationInfo.setId(AggregatedId.toString(aggregatedId.getDataSourceKey(), stationInfo.getId())); return stationInfo; } catch (Exception e) { throw exceptionHandler(e); } } @Override public StationData getStationData(String stationId) throws DataSourceException { try { AggregatedId aggregatedId = new AggregatedId(stationId); WindMobileDataSource dataSource = getDataSources().get(aggregatedId.getDataSourceKey()); return dataSource.getStationData(aggregatedId.getStationId()); } catch (Exception e) { throw exceptionHandler(e); } } @Override public Chart getWindChart(String stationId, int duration) throws DataSourceException { try { AggregatedId aggregatedId = new AggregatedId(stationId); WindMobileDataSource dataSource = getDataSources().get(aggregatedId.getDataSourceKey()); return dataSource.getWindChart(aggregatedId.getStationId(), duration); } catch (Exception e) { throw exceptionHandler(e); } } }