/******************************************************************************* * Copyright 2012 Geoscience Australia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package au.gov.ga.earthsci.core.retrieve; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.PreDestroy; import javax.inject.Inject; import javax.inject.Singleton; import org.eclipse.e4.core.di.annotations.Creatable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import au.gov.ga.earthsci.common.collection.ArrayListHashMap; import au.gov.ga.earthsci.common.collection.HashSetAndArray; import au.gov.ga.earthsci.common.collection.HashSetAndArrayHashMap; import au.gov.ga.earthsci.common.collection.ListMap; import au.gov.ga.earthsci.common.collection.SetAndArray; import au.gov.ga.earthsci.common.collection.SetAndArrayMap; /** * Basic implementation of {@link IRetrievalService}. * * @author Michael de Hoog (michael.dehoog@ga.gov.au) */ @Singleton @Creatable public class RetrievalService implements IRetrievalService { private final static Logger logger = LoggerFactory.getLogger(RetrievalService.class); @Inject private IRetrieverFactory retrieverFactory; private final Map<URL, Retrieval> urlToRetrieval = new HashMap<URL, Retrieval>(); private final SetAndArrayMap<Object, IRetrieval> callerToRetrievals = new HashSetAndArrayHashMap<Object, IRetrieval>(); private final SetAndArray<IRetrieval> EMPTY_RETRIEVAL_COLLECTION = new HashSetAndArray<IRetrieval>(); private final List<IRetrievalServiceListener> listeners = new ArrayList<IRetrievalServiceListener>(); private final ListMap<Object, IRetrievalServiceListener> callerListeners = new ArrayListHashMap<Object, IRetrievalServiceListener>(); @PreDestroy public void cancelAll() { synchronized (urlToRetrieval) { Collection<Retrieval> retrievals = urlToRetrieval.values(); for (Retrieval retrieval : retrievals) { retrieval.cancel(); } } } @Override public IRetrieval retrieve(Object caller, URL url) { return retrieve(caller, url, new RetrievalProperties()); } @Override public IRetrieval retrieve(Object caller, URL url, IRetrievalProperties retrievalProperties) { return retrieve(caller, url, retrievalProperties, false); } @Override public IRetrieval retrieve(Object caller, URL url, IRetrievalProperties retrievalProperties, boolean ignoreDuplicates) { if (url == null) { throw new NullPointerException("Retrieval URL is null"); //$NON-NLS-1$ } synchronized (urlToRetrieval) { Retrieval retrieval = ignoreDuplicates ? null : urlToRetrieval.get(url); if (retrieval == null) { //create a retriever to retrieve the url IRetriever retriever = retrieverFactory.getRetriever(url); if (retriever == null) { logger.error("Unsupported retrieval URL: " + url); //$NON-NLS-1$ return null; } //create a retrieval object retrieval = new Retrieval(caller, url, retrievalProperties, retriever); if (!ignoreDuplicates) { urlToRetrieval.put(url, retrieval); } fireRetrievalAdded(retrieval); //add a listener to remove the retrieval after it's complete retrieval.addListener(new RetrievalAdapter() { @Override public void complete(IRetrieval retrieval) { retrieval.removeListener(this); removeRetrieval(retrieval); } }); } else { retrieval.addCaller(caller); } callerToRetrievals.putSingle(caller, retrieval); fireRetrievalAdded(caller, retrieval); return retrieval; } } private void removeRetrieval(IRetrieval retrieval) { synchronized (urlToRetrieval) { urlToRetrieval.remove(retrieval.getURL()); fireRetrievalRemoved(retrieval); Object[] callers = retrieval.getCallers(); for (Object caller : callers) { callerToRetrievals.removeSingle(caller, retrieval); fireRetrievalRemoved(caller, retrieval); } } } @Override public IRetrieval getRetrieval(URL url) { synchronized (urlToRetrieval) { return urlToRetrieval.get(url); } } @Override public IRetrieval[] getRetrievals(Object caller) { synchronized (urlToRetrieval) { SetAndArray<IRetrieval> retrievals = callerToRetrievals.get(caller); if (retrievals == null) { retrievals = EMPTY_RETRIEVAL_COLLECTION; } return retrievals.getArray(IRetrieval.class); } } @Override public void addListener(IRetrievalServiceListener listener) { Collection<Retrieval> currentRetrievals; synchronized (listeners) { synchronized (urlToRetrieval) { listeners.add(listener); currentRetrievals = new ArrayList<Retrieval>(urlToRetrieval.values()); } } //notify the newly added listener of all current retrievals: for (Retrieval retrieval : currentRetrievals) { listener.retrievalAdded(retrieval); } } @Override public void addListener(IRetrievalServiceListener listener, Object caller) { synchronized (callerListeners) { synchronized (urlToRetrieval) { callerListeners.putSingle(caller, listener); //notify the newly added listener of all current retrievals for this caller: for (Retrieval retrieval : urlToRetrieval.values()) { for (Object retrievalCaller : retrieval.getCallers()) { if (retrievalCaller.equals(caller)) { listener.retrievalAdded(retrieval); break; } } } } } } @Override public void removeListener(IRetrievalServiceListener listener) { synchronized (listeners) { listeners.remove(listener); } } @Override public void removeListener(IRetrievalServiceListener listener, Object caller) { synchronized (callerListeners) { callerListeners.removeSingle(caller, listener); } } private void fireRetrievalAdded(IRetrieval retrieval) { synchronized (listeners) { for (int i = listeners.size() - 1; i >= 0; i--) { listeners.get(i).retrievalAdded(retrieval); } } } private void fireRetrievalAdded(Object caller, IRetrieval retrieval) { synchronized (callerListeners) { List<IRetrievalServiceListener> listeners = callerListeners.get(caller); if (listeners != null) { for (int i = listeners.size() - 1; i >= 0; i--) { listeners.get(i).retrievalAdded(retrieval); } } } } private void fireRetrievalRemoved(IRetrieval retrieval) { synchronized (listeners) { for (int i = listeners.size() - 1; i >= 0; i--) { listeners.get(i).retrievalRemoved(retrieval); } } } private void fireRetrievalRemoved(Object caller, IRetrieval retrieval) { synchronized (callerListeners) { List<IRetrievalServiceListener> listeners = callerListeners.get(caller); if (listeners != null) { for (int i = listeners.size() - 1; i >= 0; i--) { listeners.get(i).retrievalRemoved(retrieval); } } } } }