/** * 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.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.onebusaway.federations.FederatedServiceRegistry; import org.onebusaway.federations.FederatedServiceRegistryConstants; import org.onebusaway.federations.FederatedServiceRegistryEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Basic {@link FederatedServiceRegistry} implementation that implements * features such as * {@link FederatedServiceRegistryConstants#KEY_REGISTRATION_EXPIRES_AFTER}. * * @author bdferris * @see FederatedServiceRegistry */ public class FederatedServiceRegistryImpl implements FederatedServiceRegistry { private static Logger _log = LoggerFactory.getLogger(FederatedServiceRegistryImpl.class); private ConcurrentMap<String, Boolean> _persistentStatusByUrl = new ConcurrentHashMap<String, Boolean>(); private ConcurrentMap<String, FederatedServiceEntryImpl> _servicesByUrl = new ConcurrentHashMap<String, FederatedServiceEntryImpl>(); private Map<String, Map<String, FederatedServiceEntryImpl>> _servicesByTypeAndUrl = new HashMap<String, Map<String, FederatedServiceEntryImpl>>(); private ScheduledExecutorService _executor = Executors.newSingleThreadScheduledExecutor(); private int _updateFrequency = 60; public void setUpdateFrequency(int updateFrequencyInSeconds) { _updateFrequency = updateFrequencyInSeconds; } public void start() { _executor.scheduleAtFixedRate(new ServiceUpdateImpl(), 0, _updateFrequency, TimeUnit.SECONDS); } public void stop() { _executor.shutdown(); } @Override public synchronized List<FederatedServiceRegistryEntry> getAllServices() { List<FederatedServiceRegistryEntry> entries = new ArrayList<FederatedServiceRegistryEntry>(); for (FederatedServiceEntryImpl entry : _servicesByUrl.values()) entries.add(entry.getAsEntry()); return entries; } @Override public synchronized List<FederatedServiceRegistryEntry> getServices( String serviceClass, Map<String, String> properties) { List<FederatedServiceRegistryEntry> results = new ArrayList<FederatedServiceRegistryEntry>(); Map<String, FederatedServiceEntryImpl> entries = _servicesByTypeAndUrl.get(serviceClass); if (entries != null) { for (FederatedServiceEntryImpl entry : entries.values()) { if (entry.isEnabled() && entry.isApplicable(properties) && !entry.isExpired()) results.add(entry.getAsEntry()); } } return results; } @Override public synchronized void addService(String url, String serviceClass, Map<String, String> properties) { FederatedServiceEntryImpl existing = _servicesByUrl.get(url); removeService(url); FederatedServiceEntryImpl entry = new FederatedServiceEntryImpl(url, serviceClass, properties); String expiresAfter = properties.get(FederatedServiceRegistryConstants.KEY_REGISTRATION_EXPIRES_AFTER); if (expiresAfter != null) { int epiresAfterMS = Integer.parseInt(expiresAfter) * 1000; entry.setExpiresAfter(System.currentTimeMillis() + epiresAfterMS); } boolean enabled = true; if (existing != null) { enabled = existing.isEnabled(); } else if (_persistentStatusByUrl.containsKey(url)) { enabled = _persistentStatusByUrl.get(url); } else { String initiallyEnabledValue = properties.get(FederatedServiceRegistryConstants.KEY_INITIALLY_ENABLED); if (initiallyEnabledValue != null) enabled = Boolean.parseBoolean(initiallyEnabledValue); } entry.setEnabled(enabled); _persistentStatusByUrl.put(url, enabled); _servicesByUrl.put(url, entry); Map<String, FederatedServiceEntryImpl> entries = _servicesByTypeAndUrl.get(serviceClass); if (entries == null) { entries = new HashMap<String, FederatedServiceEntryImpl>(); _servicesByTypeAndUrl.put(serviceClass, entries); } entries.put(url, entry); } @Override public synchronized void removeService(String url) { FederatedServiceEntryImpl entry = _servicesByUrl.remove(url); if (entry != null) { Map<String, FederatedServiceEntryImpl> byUrl = _servicesByTypeAndUrl.get(entry.getServiceClass()); if (byUrl != null) byUrl.remove(url); } } @Override public synchronized void removeAllServices() { List<String> urls = new ArrayList<String>(_servicesByUrl.keySet()); for (String url : urls) removeService(url); } @Override public synchronized void setServiceStatus(String url, boolean enabled) { FederatedServiceEntryImpl entry = _servicesByUrl.get(url); if (entry != null) entry.setEnabled(enabled); _persistentStatusByUrl.put(url, enabled); } private synchronized void pruneExpiredServices() { Set<String> expiredUrls = new HashSet<String>(); for (FederatedServiceEntryImpl entry : _servicesByUrl.values()) { if (entry.isExpired()) expiredUrls.add(entry.getServiceUrl()); } for (String expiredUrl : expiredUrls) { _log.info("expiring service url: " + expiredUrl); removeService(expiredUrl); } } private class ServiceUpdateImpl implements Runnable { @Override public void run() { pruneExpiredServices(); } } }