Java tutorial
/** * Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org> * Copyright (C) 2011 Google, Inc. * * 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.transit_data_federation.impl.service_alerts; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.transit_data_federation.services.FederatedTransitDataBundle; import org.onebusaway.transit_data_federation.services.blocks.BlockInstance; import org.onebusaway.transit_data_federation.services.blocks.BlockTripInstance; import org.onebusaway.transit_data_federation.services.service_alerts.ServiceAlerts.Affects; import org.onebusaway.transit_data_federation.services.service_alerts.ServiceAlerts.Id; import org.onebusaway.transit_data_federation.services.service_alerts.ServiceAlerts.ServiceAlert; import org.onebusaway.transit_data_federation.services.service_alerts.ServiceAlerts.ServiceAlertsCollection; import org.onebusaway.transit_data_federation.services.service_alerts.ServiceAlertsService; import org.onebusaway.transit_data_federation.services.transit_graph.BlockStopTimeEntry; import org.onebusaway.transit_data_federation.services.transit_graph.BlockTripEntry; import org.onebusaway.transit_data_federation.services.transit_graph.StopEntry; import org.onebusaway.transit_data_federation.services.transit_graph.StopTimeEntry; import org.onebusaway.transit_data_federation.services.transit_graph.TripEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.onebusaway.transit_data.model.service_alerts.SituationQueryBean; import org.onebusaway.transit_data.model.service_alerts.SituationQueryBean.RouteIdAndDirection; import org.onebusaway.transit_data_federation.services.service_alerts.ServiceAlerts.TimeRange; @Component class ServiceAlertsServiceImpl implements ServiceAlertsService { private static Logger _log = LoggerFactory.getLogger(ServiceAlertsServiceImpl.class); private ConcurrentMap<AgencyAndId, ServiceAlert> _serviceAlerts = new ConcurrentHashMap<AgencyAndId, ServiceAlert>(); /** * This map groups service alert ids by the agency id in their * {@link ServiceAlert#getId()} id. */ private ConcurrentMap<String, Set<AgencyAndId>> _serviceAlertIdsByServiceAlertAgencyId = new ConcurrentHashMap<String, Set<AgencyAndId>>(); /** * This map groups service alert ids by any agency id mentioned in * {@link Affects#getAgencyId()}. */ private ConcurrentMap<String, Set<AgencyAndId>> _serviceAlertIdsByAgencyId = new ConcurrentHashMap<String, Set<AgencyAndId>>(); private ConcurrentMap<AgencyAndId, Set<AgencyAndId>> _serviceAlertIdsByStopId = new ConcurrentHashMap<AgencyAndId, Set<AgencyAndId>>(); private ConcurrentMap<AgencyAndId, Set<AgencyAndId>> _serviceAlertIdsByRouteId = new ConcurrentHashMap<AgencyAndId, Set<AgencyAndId>>(); private ConcurrentMap<RouteAndDirectionRef, Set<AgencyAndId>> _serviceAlertIdsByRouteAndDirectionId = new ConcurrentHashMap<RouteAndDirectionRef, Set<AgencyAndId>>(); private ConcurrentMap<RouteAndStopCallRef, Set<AgencyAndId>> _serviceAlertIdsByRouteAndStop = new ConcurrentHashMap<RouteAndStopCallRef, Set<AgencyAndId>>(); private ConcurrentMap<RouteDirectionAndStopCallRef, Set<AgencyAndId>> _serviceAlertIdsByRouteDirectionAndStopCall = new ConcurrentHashMap<RouteDirectionAndStopCallRef, Set<AgencyAndId>>(); private ConcurrentMap<AgencyAndId, Set<AgencyAndId>> _serviceAlertIdsByTripId = new ConcurrentHashMap<AgencyAndId, Set<AgencyAndId>>(); private ConcurrentMap<TripAndStopCallRef, Set<AgencyAndId>> _serviceAlertIdsByTripAndStopId = new ConcurrentHashMap<TripAndStopCallRef, Set<AgencyAndId>>(); private FederatedTransitDataBundle _bundle; private File _serviceAlertsPath; @Autowired public void setBundle(FederatedTransitDataBundle bundle) { _bundle = bundle; } public void setServiceAlertsPath(File path) { _serviceAlertsPath = path; } @PostConstruct public void start() { loadServiceAlerts(); } @PreDestroy public void stop() { saveServiceAlerts(); } /**** * {@link ServiceAlertsService} Interface ****/ @Override public synchronized ServiceAlert createOrUpdateServiceAlert(ServiceAlert.Builder builder, String defaultAgencyId) { if (!builder.hasId()) { UUID uuid = UUID.randomUUID(); Id id = ServiceAlertLibrary.id(defaultAgencyId, uuid.toString()); builder.setId(id); } if (!builder.hasCreationTime()) builder.setCreationTime(System.currentTimeMillis()); builder.setModifiedTime(System.currentTimeMillis()); ServiceAlert serviceAlert = builder.build(); updateReferences(serviceAlert); saveServiceAlerts(); return serviceAlert; } @Override public synchronized void removeServiceAlert(AgencyAndId serviceAlertId) { removeServiceAlerts(Arrays.asList(serviceAlertId)); } @Override public synchronized void removeServiceAlerts(List<AgencyAndId> serviceAlertIds) { for (AgencyAndId serviceAlertId : serviceAlertIds) { ServiceAlert existingServiceAlert = _serviceAlerts.remove(serviceAlertId); if (existingServiceAlert != null) { updateReferences(existingServiceAlert, null); } } saveServiceAlerts(); } @Override public synchronized void removeAllServiceAlertsForFederatedAgencyId(String agencyId) { Set<AgencyAndId> ids = _serviceAlertIdsByServiceAlertAgencyId.get(agencyId); if (ids != null) removeServiceAlerts(new ArrayList<AgencyAndId>(ids)); } @Override public ServiceAlert getServiceAlertForId(AgencyAndId serviceAlertId) { return _serviceAlerts.get(serviceAlertId); } @Override public List<ServiceAlert> getAllServiceAlerts() { return new ArrayList<ServiceAlert>(_serviceAlerts.values()); } @Override public List<ServiceAlert> getServiceAlertsForFederatedAgencyId(String agencyId) { Set<AgencyAndId> serviceAlertIds = _serviceAlertIdsByServiceAlertAgencyId.get(agencyId); return getServiceAlertIdsAsObjects(serviceAlertIds); } @Override public List<ServiceAlert> getServiceAlertsForAgencyId(long time, String agencyId) { Set<AgencyAndId> serviceAlertIds = new HashSet<AgencyAndId>(); getServiceAlertIdsForKey(_serviceAlertIdsByAgencyId, agencyId, serviceAlertIds); return getServiceAlertIdsAsObjects(serviceAlertIds, time); } @Override public List<ServiceAlert> getServiceAlertsForStopId(long time, AgencyAndId stopId) { Set<AgencyAndId> serviceAlertIds = new HashSet<AgencyAndId>(); getServiceAlertIdsForKey(_serviceAlertIdsByAgencyId, stopId.getAgencyId(), serviceAlertIds); getServiceAlertIdsForKey(_serviceAlertIdsByStopId, stopId, serviceAlertIds); return getServiceAlertIdsAsObjects(serviceAlertIds, time); } @Override public List<ServiceAlert> getServiceAlertsForStopIds(long time, Iterable<AgencyAndId> stopIds) { Set<AgencyAndId> serviceAlertIds = new HashSet<AgencyAndId>(); for (AgencyAndId stopId : stopIds) { getServiceAlertIdsForKey(_serviceAlertIdsByAgencyId, stopId.getAgencyId(), serviceAlertIds); getServiceAlertIdsForKey(_serviceAlertIdsByStopId, stopId, serviceAlertIds); } return getServiceAlertIdsAsObjects(serviceAlertIds); } @Override public List<ServiceAlert> getServiceAlertsForStopCall(long time, BlockInstance blockInstance, BlockStopTimeEntry blockStopTime, AgencyAndId vehicleId) { BlockTripEntry blockTrip = blockStopTime.getTrip(); TripEntry trip = blockTrip.getTrip(); AgencyAndId tripId = trip.getId(); AgencyAndId lineId = trip.getRouteCollection().getId(); String directionId = trip.getDirectionId(); StopTimeEntry stopTime = blockStopTime.getStopTime(); StopEntry stop = stopTime.getStop(); AgencyAndId stopId = stop.getId(); Set<AgencyAndId> serviceAlertIds = new HashSet<AgencyAndId>(); /* * TODO: Temporarily disable */ /* * getServiceAlertIdsForKey(_serviceAlertsIdsByAgencyId, * lineId.getAgencyId(), serviceAlertIds); */ getServiceAlertIdsForKey(_serviceAlertIdsByRouteId, lineId, serviceAlertIds); RouteAndStopCallRef routeAndStopCallRef = new RouteAndStopCallRef(lineId, stopId); getServiceAlertIdsForKey(_serviceAlertIdsByRouteAndStop, routeAndStopCallRef, serviceAlertIds); /** * Remember that direction is optional */ if (directionId != null) { RouteAndDirectionRef lineAndDirectionRef = new RouteAndDirectionRef(lineId, directionId); RouteDirectionAndStopCallRef lineDirectionAndStopCallRef = new RouteDirectionAndStopCallRef(lineId, directionId, stopId); getServiceAlertIdsForKey(_serviceAlertIdsByRouteAndDirectionId, lineAndDirectionRef, serviceAlertIds); getServiceAlertIdsForKey(_serviceAlertIdsByRouteDirectionAndStopCall, lineDirectionAndStopCallRef, serviceAlertIds); } getServiceAlertIdsForKey(_serviceAlertIdsByTripId, trip.getId(), serviceAlertIds); TripAndStopCallRef tripAndStopCallRef = new TripAndStopCallRef(tripId, stopId); getServiceAlertIdsForKey(_serviceAlertIdsByTripAndStopId, tripAndStopCallRef, serviceAlertIds); return getServiceAlertIdsAsObjects(serviceAlertIds, time); } @Override public List<ServiceAlert> getServiceAlertsForVehicleJourney(long time, BlockTripInstance blockTripInstance, AgencyAndId vehicleId) { BlockTripEntry blockTrip = blockTripInstance.getBlockTrip(); TripEntry trip = blockTrip.getTrip(); AgencyAndId lineId = trip.getRouteCollection().getId(); RouteAndDirectionRef lineAndDirectionRef = new RouteAndDirectionRef(lineId, trip.getDirectionId()); Set<AgencyAndId> serviceAlertIds = new HashSet<AgencyAndId>(); getServiceAlertIdsForKey(_serviceAlertIdsByAgencyId, lineId.getAgencyId(), serviceAlertIds); getServiceAlertIdsForKey(_serviceAlertIdsByRouteId, lineId, serviceAlertIds); getServiceAlertIdsForKey(_serviceAlertIdsByRouteAndDirectionId, lineAndDirectionRef, serviceAlertIds); getServiceAlertIdsForKey(_serviceAlertIdsByTripId, trip.getId(), serviceAlertIds); return getServiceAlertIdsAsObjects(serviceAlertIds, time); } @Override public List<ServiceAlert> getServiceAlerts(SituationQueryBean query) { Set<AgencyAndId> serviceAlertIds = new HashSet<AgencyAndId>(); // Note we are treating the query's agency ID as that of what the service // alert affects, not the alert's agency ID. if (!StringUtils.isEmpty(query.getAgencyId())) { getServiceAlertIdsForKey(_serviceAlertIdsByAgencyId, query.getAgencyId(), serviceAlertIds); } if (!CollectionUtils.isEmpty(query.getStopIds())) { for (String stopId : query.getStopIds()) { AgencyAndId id = ServiceAlertLibrary .agencyAndId(ServiceAlertLibrary.id(AgencyAndId.convertFromString(stopId))); getServiceAlertIdsForKey(_serviceAlertIdsByStopId, id, serviceAlertIds); } } if (!CollectionUtils.isEmpty(query.getRoutes())) { for (RouteIdAndDirection route : query.getRoutes()) { if (route.direction != null) { RouteAndDirectionRef lineAndDirectionRef = new RouteAndDirectionRef( AgencyAndId.convertFromString(route.routeId), route.direction); getServiceAlertIdsForKey(_serviceAlertIdsByRouteAndDirectionId, lineAndDirectionRef, serviceAlertIds); } else { getServiceAlertIdsForKey(_serviceAlertIdsByRouteId, AgencyAndId.convertFromString(route.routeId), serviceAlertIds); } } } return getServiceAlertIdsAsObjects(serviceAlertIds); } /**** * Private Methods ****/ private void updateReferences(ServiceAlert serviceAlert) { AgencyAndId id = ServiceAlertLibrary.agencyAndId(serviceAlert.getId()); ServiceAlert existingServiceAlert = _serviceAlerts.put(id, serviceAlert); updateReferences(existingServiceAlert, serviceAlert); } private void updateReferences(ServiceAlert existingServiceAlert, ServiceAlert serviceAlert) { updateReferences(existingServiceAlert, serviceAlert, _serviceAlertIdsByServiceAlertAgencyId, AffectsServiceAlertAgencyKeyFactory.INSTANCE); updateReferences(existingServiceAlert, serviceAlert, _serviceAlertIdsByAgencyId, AffectsAgencyKeyFactory.INSTANCE); updateReferences(existingServiceAlert, serviceAlert, _serviceAlertIdsByStopId, AffectsStopKeyFactory.INSTANCE); updateReferences(existingServiceAlert, serviceAlert, _serviceAlertIdsByRouteId, AffectsRouteKeyFactory.INSTANCE); updateReferences(existingServiceAlert, serviceAlert, _serviceAlertIdsByRouteAndDirectionId, AffectsRouteAndDirectionKeyFactory.INSTANCE); updateReferences(existingServiceAlert, serviceAlert, _serviceAlertIdsByRouteAndStop, AffectsRouteAndStopKeyFactory.INSTANCE); updateReferences(existingServiceAlert, serviceAlert, _serviceAlertIdsByRouteDirectionAndStopCall, AffectsRouteDirectionAndStopCallKeyFactory.INSTANCE); updateReferences(existingServiceAlert, serviceAlert, _serviceAlertIdsByTripId, AffectsTripKeyFactory.INSTANCE); updateReferences(existingServiceAlert, serviceAlert, _serviceAlertIdsByTripAndStopId, AffectsTripAndStopKeyFactory.INSTANCE); } private <T> void updateReferences(ServiceAlert existingServiceAlert, ServiceAlert serviceAlert, ConcurrentMap<T, Set<AgencyAndId>> map, AffectsKeyFactory<T> affectsKeyFactory) { Set<T> existingEffects = Collections.emptySet(); if (existingServiceAlert != null) { existingEffects = affectsKeyFactory.getKeysForAffects(existingServiceAlert); } Set<T> newEffects = Collections.emptySet(); if (serviceAlert != null) { newEffects = affectsKeyFactory.getKeysForAffects(serviceAlert); } for (T existingEffect : existingEffects) { if (newEffects.contains(existingEffect)) continue; AgencyAndId id = ServiceAlertLibrary.agencyAndId(existingServiceAlert.getId()); Set<AgencyAndId> ids = map.get(existingEffect); ids.remove(id); if (ids.isEmpty()) map.remove(existingEffect); } for (T newEffect : newEffects) { if (existingEffects.contains(newEffect)) continue; AgencyAndId id = ServiceAlertLibrary.agencyAndId(serviceAlert.getId()); Set<AgencyAndId> ids = map.get(newEffect); if (ids == null) { ids = new HashSet<AgencyAndId>(); map.put(newEffect, ids); } ids.add(id); } } private <T> void getServiceAlertIdsForKey(ConcurrentMap<T, Set<AgencyAndId>> serviceAlertIdsByKey, T key, Collection<AgencyAndId> matches) { Set<AgencyAndId> ids = serviceAlertIdsByKey.get(key); if (ids != null) matches.addAll(ids); } private List<ServiceAlert> getServiceAlertIdsAsObjects(Collection<AgencyAndId> serviceAlertIds) { return getServiceAlertIdsAsObjects(serviceAlertIds, -1); } private List<ServiceAlert> getServiceAlertIdsAsObjects(Collection<AgencyAndId> serviceAlertIds, long time) { if (serviceAlertIds == null || serviceAlertIds.isEmpty()) return Collections.emptyList(); List<ServiceAlert> serviceAlerts = new ArrayList<ServiceAlert>(serviceAlertIds.size()); for (AgencyAndId serviceAlertId : serviceAlertIds) { ServiceAlert serviceAlert = _serviceAlerts.get(serviceAlertId); if (serviceAlert != null && filterByTime(serviceAlert, time)) serviceAlerts.add(serviceAlert); } return serviceAlerts; } private boolean filterByTime(ServiceAlert serviceAlert, long time) { if (time == -1 || serviceAlert.getPublicationWindowList().size() == 0) return true; for (TimeRange publicationWindow : serviceAlert.getPublicationWindowList()) { if ((!publicationWindow.hasStart() || publicationWindow.getStart() <= time) && (!publicationWindow.hasEnd() || publicationWindow.getEnd() >= time)) { return true; } } return false; } /**** * Serialization ****/ private synchronized void loadServiceAlerts() { _log.info("Loading service alerts from bundle"); File path = getServiceAlertsPath(); if (path == null || !path.exists()) return; InputStream in = null; try { in = new BufferedInputStream(new FileInputStream(path)); ServiceAlertsCollection collection = ServiceAlertsCollection.parseFrom(in); for (ServiceAlert serviceAlert : collection.getServiceAlertsList()) updateReferences(serviceAlert); } catch (Exception ex) { _log.error("error loading service alerts from path " + path, ex); } finally { if (in != null) { try { in.close(); } catch (IOException ex) { _log.error("error closing service alerts path " + path, ex); } } } } private synchronized void saveServiceAlerts() { File path = getServiceAlertsPath(); if (path == null) return; ServiceAlertsCollection.Builder builder = ServiceAlertsCollection.newBuilder(); builder.addAllServiceAlerts(_serviceAlerts.values()); ServiceAlertsCollection collection = builder.build(); OutputStream out = null; try { out = new BufferedOutputStream(new FileOutputStream(path)); collection.writeTo(out); out.close(); } catch (Exception ex) { _log.error("error saving service alerts to path " + path, ex); } finally { if (out != null) { try { out.close(); } catch (IOException ex) { _log.error("error closing service output to path " + path, ex); } } } } private File getServiceAlertsPath() { if (_serviceAlertsPath != null) return _serviceAlertsPath; return _bundle.getServiceAlertsPath(); } }