/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* 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 org.onebusaway.federations.impl;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.onebusaway.federations.FederatedService;
import org.onebusaway.federations.FederatedServiceCollection;
import org.onebusaway.federations.FederatedServiceRegistry;
import org.onebusaway.federations.FederatedServiceRegistryEntry;
import org.onebusaway.geospatial.model.CoordinateBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.caucho.hessian.client.HessianConnectionException;
import com.caucho.hessian.client.HessianProxyFactory;
import com.caucho.hessian.client.HessianRuntimeException;
/**
* A dynamic {@link FederatedServiceCollection} implementation. Service
* instances are periodically queried from a {@link FederatedServiceRegistry}
* instance given a target service interface + parameters and our service
* collection is updated as is appropriate. It's assumed that all services
* published by the {@link FederatedServiceRegistry} have been exported as a
* Hessian rpc service, as we'll be using {@link HessianProxyFactory} to create
* proxy service instances from the service urls.
*
* @author bdferris
*/
public class DynamicFederatedServiceCollectionImpl extends
AbstractFederatedServiceCollectionWrapperImpl {
private static Logger _log = LoggerFactory.getLogger(DynamicFederatedServiceCollectionImpl.class);
private HessianProxyFactory _proxyFactory = new HessianProxyFactory();
private ScheduledExecutorService _executor = Executors.newSingleThreadScheduledExecutor();
/**
* By default, we have an empty registry
*/
private volatile FederatedServiceCollection _collection = new FederatedServiceCollectionImpl();
private volatile Set<String> _activeUrls = new HashSet<String>();
private FederatedServiceRegistry _registry;
private int _updateFrequency = 60;
private Class<?> _serviceInterface;
private Map<String, String> _queryProperties = new HashMap<String, String>();
public void setRegistry(FederatedServiceRegistry registry) {
_registry = registry;
}
public void setUpdateFrequency(int updateFrequencyInSeconds) {
_updateFrequency = updateFrequencyInSeconds;
}
public void setServiceInterface(Class<?> serviceInterface) {
_serviceInterface = serviceInterface;
}
public void setQueryProperties(Map<String, String> queryProperties) {
_queryProperties = queryProperties;
}
@PostConstruct
public void start() {
_log.debug("start");
_executor.scheduleAtFixedRate(new ServiceUpdateImpl(), 0, _updateFrequency,
TimeUnit.SECONDS);
}
@PreDestroy
public void stop() {
_log.debug("stop");
_executor.shutdown();
}
@Override
protected FederatedServiceCollection getCollection() {
return _collection;
}
private class ServiceUpdateImpl implements Runnable {
@Override
public void run() {
try {
Set<String> activeUrls = new HashSet<String>();
List<FederatedServiceRegistryEntry> entries = _registry.getServices(
_serviceInterface.getName(), _queryProperties);
Map<FederatedService, Map<String, List<CoordinateBounds>>> byProvider = new HashMap<FederatedService, Map<String, List<CoordinateBounds>>>();
for (FederatedServiceRegistryEntry serviceEntry : entries) {
String url = serviceEntry.getServiceUrl();
_log.debug("querying url: {}", url);
try {
FederatedService service = (FederatedService) _proxyFactory.create(
_serviceInterface, url);
Map<String, List<CoordinateBounds>> agencyIdsWithCoverageArea = service.getAgencyIdsWithCoverageArea();
boolean allGood = true;
for (Map.Entry<String, List<CoordinateBounds>> entry : agencyIdsWithCoverageArea.entrySet()) {
String agencyId = entry.getKey();
List<CoordinateBounds> coverage = entry.getValue();
boolean validService = FederatedServiceLibrary.checkAgencyAndCoverageAgainstExisting(
byProvider, agencyId, coverage, _serviceInterface, false);
if (!validService) {
allGood = false;
_log.warn("error in agency coverage overlap: url=" + url
+ " agencyId=" + agencyId);
}
}
if (allGood) {
_log.debug("adding service...");
byProvider.put(service, agencyIdsWithCoverageArea);
activeUrls.add(url);
}
} catch (HessianRuntimeException ex) {
_log.warn("error querying service url: " + url);
} catch (HessianConnectionException ex) {
_log.warn("error connecting to service url: " + url);
} catch (MalformedURLException e) {
_log.warn("malformed service url: " + url);
}
}
Set<String> allUrls = new HashSet<String>();
allUrls.addAll(_activeUrls);
allUrls.addAll(activeUrls);
for (String url : allUrls) {
boolean a = _activeUrls.contains(url);
boolean b = activeUrls.contains(url);
if (a && !b)
_log.info("service removed: " + url);
else if (!a && b)
_log.info("service added: " + url);
}
_collection = new FederatedServiceCollectionImpl(byProvider);
_activeUrls = activeUrls;
} catch (Throwable ex) {
_log.warn("error refreshing services", ex);
}
}
}
}