diff --git a/pom.xml b/pom.xml index e5e95cab4a6..6035d593474 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ org.opentripplanner otp - 1.5.0-SNAPSHOT + 1.5.0.1-SNAPSHOT jar @@ -242,21 +242,6 @@ - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - sign-artifacts - - verify - - sign - - - - org.apache.maven.plugins maven-surefire-plugin diff --git a/src/main/java/org/opentripplanner/annotation/Component.java b/src/main/java/org/opentripplanner/annotation/Component.java new file mode 100644 index 00000000000..f7394d4806c --- /dev/null +++ b/src/main/java/org/opentripplanner/annotation/Component.java @@ -0,0 +1,17 @@ +package org.opentripplanner.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Component { + + String key(); + + ServiceType type(); +} diff --git a/src/main/java/org/opentripplanner/annotation/ComponentAnnotationConfigurator.java b/src/main/java/org/opentripplanner/annotation/ComponentAnnotationConfigurator.java new file mode 100644 index 00000000000..eb5cd997312 --- /dev/null +++ b/src/main/java/org/opentripplanner/annotation/ComponentAnnotationConfigurator.java @@ -0,0 +1,194 @@ +package org.opentripplanner.annotation; + +import static java.util.Collections.emptyList; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Sets; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class ComponentAnnotationConfigurator { + + private static Map>> componentMaps = new HashMap<>(); + private static ComponentAnnotationConfigurator instance = new ComponentAnnotationConfigurator(); + public static final String COMPONENTS_PACKAGE = "components.packages"; + + public static ComponentAnnotationConfigurator getInstance() { + return instance; + } + + private ComponentAnnotationConfigurator() { + + } + + public void fromConfig(JsonNode config) { + Set packages = Sets + .newHashSet("org.opentripplanner.updater", "org.opentripplanner.routing"); + if (config.has(COMPONENTS_PACKAGE) && config.path(COMPONENTS_PACKAGE).isArray()) { + config.path(COMPONENTS_PACKAGE).forEach(node -> packages.add(node.asText())); + } + scanPackages(packages); + } + + public void scanPackages(Collection packages) { + packages.stream().map(this::getClasses).flatMap(Collection::stream) + .forEach(this::setupRegisteredComponent); + } + + public T getComponentInstance(String key, ServiceType type) + throws IllegalAccessException, InstantiationException { + if (componentMaps.containsKey(type) && componentMaps.get(type).containsKey(key)) { + return (T) componentMaps.get(type).get(key).newInstance(); + } + return null; + } + + private void setupRegisteredComponent(Class clazz) { + Component annotation = clazz.getAnnotation(Component.class); + if (annotation != null) { + Map> classMap; + if (!componentMaps.containsKey(annotation.type())) { + classMap = new HashMap<>(); + } else { + classMap = componentMaps.get(annotation.type()); + } + classMap.put(annotation.key(), clazz); + componentMaps.put(annotation.type(), classMap); + } + } + + private Collection getClasses(String packageName) { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + assert classLoader != null; + String path = packageName.replace('.', '/'); + Enumeration resources = null; + try { + resources = classLoader.getResources(path); + } catch (IOException e) { + e.printStackTrace(); + return emptyList(); + } + List dirs = new ArrayList(); + while (resources.hasMoreElements()) { + URL resource = resources.nextElement(); + dirs.add(new File(resource.getFile())); + } + ArrayList classes = new ArrayList(); + for (File directory : dirs) { + classes.addAll(findClasses(directory, packageName)); + } + return classes; + } + + /** + * Recursive method used to find all classes in a given directory and subdirs. + * + * @param directory The base directory + * @param packageName The package name for classes found inside the base directory + * @return The classes + * @throws ClassNotFoundException + */ + + private List findClasses(File directory, String packageName) { + if (!directory.isDirectory()) { + if (isJarFile(directory)) { + return loadFromJar(directory, packageName); + } + return emptyList(); + } else { + return loadFromDirectory(directory, packageName); + } + } + + private List loadFromJar(File file, String packageName) { + Matcher matcher = jarPattern.matcher(file.getPath()); + if (matcher.matches()) { + String pathToJar = matcher.group(1); + String packageDirectory = packageName.replace(".", "/"); + try { + URL[] urls = {new URL("jar:file:" + pathToJar + "!/")}; + URLClassLoader cl = URLClassLoader.newInstance(urls); + JarFile jarFile = new JarFile(pathToJar); + return enumerationAsStream(jarFile.entries()) + .filter(entry -> entry.getName().startsWith(packageDirectory)) + .filter(entry -> entry.getName().endsWith(".class")) + .map(JarEntry::getName) + .map(name -> name.substring(0, name.length() - 6).replace("/", ".")) + .map(name -> { + try { + return cl.loadClass(name); + } catch (ClassNotFoundException e) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } catch (IOException e) { + + } + } + + return emptyList(); + } + + Pattern jarPattern = Pattern.compile("file:(.*?\\.jar)(?:!.*)?"); + + private boolean isJarFile(File directory) { + return jarPattern.matcher(directory.getPath()).matches(); + } + + private List loadFromDirectory(File directory, String packageName) { + List classes = new ArrayList(); + File[] files = directory.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + assert !file.getName().contains("."); + classes.addAll(loadFromDirectory(file, packageName + "." + file.getName())); + } else if (file.getName().endsWith(".class")) { + try { + classes.add(Class + .forName( + packageName + '.' + file.getName().substring(0, file.getName().length() - 6))); + } catch (ClassNotFoundException e) { + continue; + } + } + } + return classes; + } + + private Stream enumerationAsStream(Enumeration e) { + return StreamSupport.stream( + Spliterators.spliteratorUnknownSize( + new Iterator() { + public T next() { + return e.nextElement(); + } + + public boolean hasNext() { + return e.hasMoreElements(); + } + }, + Spliterator.ORDERED), false); + } +} diff --git a/src/main/java/org/opentripplanner/annotation/ServiceType.java b/src/main/java/org/opentripplanner/annotation/ServiceType.java new file mode 100644 index 00000000000..1ab28960929 --- /dev/null +++ b/src/main/java/org/opentripplanner/annotation/ServiceType.java @@ -0,0 +1,5 @@ +package org.opentripplanner.annotation; + +public enum ServiceType { + GraphUpdater, ServiceFactory; +} diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index 6330856dff3..77183337547 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.Lists; +import org.opentripplanner.annotation.ComponentAnnotationConfigurator; import org.opentripplanner.graph_builder.model.GtfsBundle; import org.opentripplanner.graph_builder.module.DirectTransferGenerator; import org.opentripplanner.graph_builder.module.EmbedConfig; @@ -45,7 +46,7 @@ * It is modular: GraphBuilderModules are placed in a list and run in sequence. */ public class GraphBuilder implements Runnable { - + private static Logger LOG = LoggerFactory.getLogger(GraphBuilder.class); public static final String BUILDER_CONFIG_FILENAME = "build-config.json"; @@ -53,13 +54,13 @@ public class GraphBuilder implements Runnable { private List _graphBuilderModules = new ArrayList(); private final File graphFile; - + private boolean _alwaysRebuild = true; private List modeList; - + private String baseGraph = null; - + private Graph graph = new Graph(); /** Should the graph be serialized to disk after being created or not? */ @@ -86,7 +87,7 @@ public void setGraphBuilders(List graphLoaders) { public void setAlwaysRebuild(boolean alwaysRebuild) { _alwaysRebuild = alwaysRebuild; } - + public void setBaseGraph(String baseGraph) { this.baseGraph = baseGraph; try { @@ -113,7 +114,7 @@ public void run() { long startTime = System.currentTimeMillis(); if (serializeGraph) { - + if (graphFile == null) { throw new RuntimeException("graphBuilderTask has no attribute graphFile."); } @@ -122,7 +123,7 @@ public void run() { LOG.info("graph already exists and alwaysRebuild=false => skipping graph build"); return; } - + try { if (!graphFile.getParentFile().exists()) { if (!graphFile.getParentFile().mkdirs()) { @@ -139,7 +140,7 @@ public void run() { for (GraphBuilderModule builder : _graphBuilderModules) { builder.checkInputs(); } - + HashMap, Object> extra = new HashMap, Object>(); for (GraphBuilderModule load : _graphBuilderModules) load.buildGraph(graph, extra); @@ -181,6 +182,7 @@ public static GraphBuilder forDirectory(CommandLineParameters params, File dir) } // Find and parse config files first to reveal syntax errors early without waiting for graph build. builderConfig = OTPMain.loadJson(new File(dir, BUILDER_CONFIG_FILENAME)); + ComponentAnnotationConfigurator.getInstance().fromConfig(builderConfig); GraphBuilderParameters builderParams = new GraphBuilderParameters(builderConfig); GraphBuilder graphBuilder = new GraphBuilder(dir, builderParams); diff --git a/src/main/java/org/opentripplanner/routing/bike_rental/TimeBasedBikeRentalFareService.java b/src/main/java/org/opentripplanner/routing/bike_rental/TimeBasedBikeRentalFareService.java index 6b34f337569..03655fc4f2b 100644 --- a/src/main/java/org/opentripplanner/routing/bike_rental/TimeBasedBikeRentalFareService.java +++ b/src/main/java/org/opentripplanner/routing/bike_rental/TimeBasedBikeRentalFareService.java @@ -6,7 +6,7 @@ import org.opentripplanner.common.model.P2; import org.opentripplanner.routing.core.Fare; -import org.opentripplanner.routing.core.Fare.FareType; +import org.opentripplanner.routing.core.StandardFareType; import org.opentripplanner.routing.core.State; import org.opentripplanner.routing.core.WrappedCurrency; import org.opentripplanner.routing.services.FareService; @@ -64,7 +64,7 @@ public Fare getCost(GraphPath path) { } Fare fare = new Fare(); - fare.addFare(FareType.regular, new WrappedCurrency(currency), cost); + fare.addFare(StandardFareType.regular, new WrappedCurrency(currency), cost); return fare; } } diff --git a/src/main/java/org/opentripplanner/routing/bike_rental/TimeBasedBikeRentalFareServiceFactory.java b/src/main/java/org/opentripplanner/routing/bike_rental/TimeBasedBikeRentalFareServiceFactory.java index 1d863bd449f..094900ff519 100644 --- a/src/main/java/org/opentripplanner/routing/bike_rental/TimeBasedBikeRentalFareServiceFactory.java +++ b/src/main/java/org/opentripplanner/routing/bike_rental/TimeBasedBikeRentalFareServiceFactory.java @@ -8,6 +8,8 @@ import java.util.List; import java.util.Map; +import org.opentripplanner.annotation.Component; +import org.opentripplanner.annotation.ServiceType; import org.opentripplanner.model.OtpTransitService; import org.opentripplanner.common.model.P2; import org.opentripplanner.routing.services.FareService; @@ -16,7 +18,7 @@ import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JsonNode; - +@Component(key = "bike-rental-time-based",type = ServiceType.ServiceFactory) public class TimeBasedBikeRentalFareServiceFactory implements FareServiceFactory { private static Logger log = LoggerFactory diff --git a/src/main/java/org/opentripplanner/routing/core/Fare.java b/src/main/java/org/opentripplanner/routing/core/Fare.java index b36ca0ebcda..9ab6ab58be9 100644 --- a/src/main/java/org/opentripplanner/routing/core/Fare.java +++ b/src/main/java/org/opentripplanner/routing/core/Fare.java @@ -1,7 +1,5 @@ package org.opentripplanner.routing.core; -import java.io.Serializable; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -14,10 +12,6 @@ */ public class Fare { - public static enum FareType implements Serializable { - regular, student, senior, tram, special, youth - } - /** * A mapping from {@link FareType} to {@link Money}. */ diff --git a/src/main/java/org/opentripplanner/routing/core/FareType.java b/src/main/java/org/opentripplanner/routing/core/FareType.java new file mode 100644 index 00000000000..d5b3a211932 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/core/FareType.java @@ -0,0 +1,10 @@ +package org.opentripplanner.routing.core; + +/** + * FareType should be extendable for third-party usage + * + * java.nio.file.OpenOption is a good example for this requirement + */ +public interface FareType { + String name(); +} diff --git a/src/main/java/org/opentripplanner/routing/core/StandardFareType.java b/src/main/java/org/opentripplanner/routing/core/StandardFareType.java new file mode 100644 index 00000000000..02e80779004 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/core/StandardFareType.java @@ -0,0 +1,8 @@ +package org.opentripplanner.routing.core; + +/** + * FareType defined by OpenTripPlanner + */ +public enum StandardFareType implements FareType{ + regular, student, senior, tram, special, youth +} diff --git a/src/main/java/org/opentripplanner/routing/fares/AddingMultipleFareService.java b/src/main/java/org/opentripplanner/routing/fares/AddingMultipleFareService.java index 995385ef174..f4cbceeba4c 100644 --- a/src/main/java/org/opentripplanner/routing/fares/AddingMultipleFareService.java +++ b/src/main/java/org/opentripplanner/routing/fares/AddingMultipleFareService.java @@ -4,8 +4,9 @@ import java.util.List; import org.opentripplanner.routing.core.Fare; -import org.opentripplanner.routing.core.Fare.FareType; +import org.opentripplanner.routing.core.FareType; import org.opentripplanner.routing.core.Money; +import org.opentripplanner.routing.core.StandardFareType; import org.opentripplanner.routing.services.FareService; import org.opentripplanner.routing.spt.GraphPath; @@ -37,7 +38,7 @@ public Fare getCost(GraphPath path) { // Merge subFare with existing fare // Must use a temporary as we need to keep fare clean during the loop on FareType Fare newFare = new Fare(fare); - for (FareType fareType : FareType.values()) { + for (FareType fareType : StandardFareType.values()) { Money cost = fare.getFare(fareType); Money subCost = subFare.getFare(fareType); if (cost == null && subCost == null) { @@ -52,10 +53,10 @@ public Fare getCost(GraphPath path) { * probably want the "regular" bike fare to be added to the "student" * transit fare too. Here we assume "regular" as a sane default value. */ - subCost = subFare.getFare(FareType.regular); + subCost = subFare.getFare(StandardFareType.regular); } else if (cost == null && subCost != null) { /* Same, but the other way around. */ - cost = fare.getFare(FareType.regular); + cost = fare.getFare(StandardFareType.regular); } if (cost != null && subCost != null) { diff --git a/src/main/java/org/opentripplanner/routing/fares/MultipleFareServiceFactory.java b/src/main/java/org/opentripplanner/routing/fares/MultipleFareServiceFactory.java index 1ecbaaed796..26c1db139f8 100644 --- a/src/main/java/org/opentripplanner/routing/fares/MultipleFareServiceFactory.java +++ b/src/main/java/org/opentripplanner/routing/fares/MultipleFareServiceFactory.java @@ -5,6 +5,8 @@ import java.util.List; import java.util.Map; +import org.opentripplanner.annotation.Component; +import org.opentripplanner.annotation.ServiceType; import org.opentripplanner.model.OtpTransitService; import org.opentripplanner.routing.impl.DefaultFareServiceFactory; import org.opentripplanner.routing.services.FareService; @@ -39,7 +41,7 @@ public void processGtfs(OtpTransitService transitService) { /** * Accept several ways to define fares to compose. Examples: - * + * *
      * { combinationStrategy : "additive",
      *   // An array of 'fares'
@@ -79,6 +81,7 @@ public void configure(JsonNode config) {
         }
     }
 
+    @Component(key = "composite:additive",type = ServiceType.ServiceFactory)
     public static class AddingMultipleFareServiceFactory extends MultipleFareServiceFactory {
         @Override
         protected FareService makeMultipleFareService(List subServices) {
diff --git a/src/main/java/org/opentripplanner/routing/graph/GraphIndex.java b/src/main/java/org/opentripplanner/routing/graph/GraphIndex.java
index 651b03d4464..87b807a848c 100644
--- a/src/main/java/org/opentripplanner/routing/graph/GraphIndex.java
+++ b/src/main/java/org/opentripplanner/routing/graph/GraphIndex.java
@@ -84,10 +84,11 @@
 import org.opentripplanner.routing.bike_park.BikePark;
 import org.opentripplanner.routing.bike_rental.BikeRentalStation;
 import org.opentripplanner.routing.car_park.CarPark;
-import org.opentripplanner.routing.core.Fare.FareType;
 import org.opentripplanner.routing.core.FareRuleSet;
+import org.opentripplanner.routing.core.FareType;
 import org.opentripplanner.routing.core.RoutingRequest;
 import org.opentripplanner.routing.core.ServiceDay;
+import org.opentripplanner.routing.core.StandardFareType;
 import org.opentripplanner.routing.core.State;
 import org.opentripplanner.routing.core.TicketType;
 import org.opentripplanner.routing.core.TraverseMode;
@@ -187,7 +188,7 @@ public GraphIndex (Graph graph) {
             DefaultFareServiceImpl defaultFareServiceImpl = (DefaultFareServiceImpl) fareService;
             Map> data = defaultFareServiceImpl.getFareRulesPerType();
             for(Entry> kv:data.entrySet()) {
-                if(FareType.regular == kv.getKey()) {
+                if(StandardFareType.regular == kv.getKey()) {
                     for(FareRuleSet rs: kv.getValue()) {
                         ticketTypesForId.put(rs.getFareAttribute().getId(), new TicketType(rs));
                     }
diff --git a/src/main/java/org/opentripplanner/routing/impl/DefaultFareServiceFactory.java b/src/main/java/org/opentripplanner/routing/impl/DefaultFareServiceFactory.java
index cee78c208b4..5bc7b1c83c3 100644
--- a/src/main/java/org/opentripplanner/routing/impl/DefaultFareServiceFactory.java
+++ b/src/main/java/org/opentripplanner/routing/impl/DefaultFareServiceFactory.java
@@ -1,15 +1,16 @@
 package org.opentripplanner.routing.impl;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import org.opentripplanner.annotation.Component;
+import org.opentripplanner.annotation.ComponentAnnotationConfigurator;
+import org.opentripplanner.annotation.ServiceType;
 import org.opentripplanner.model.FeedScopedId;
 import org.opentripplanner.model.FareAttribute;
 import org.opentripplanner.model.FareRule;
 import org.opentripplanner.model.Route;
 import org.opentripplanner.model.OtpTransitService;
-import org.opentripplanner.routing.bike_rental.TimeBasedBikeRentalFareServiceFactory;
-import org.opentripplanner.routing.core.Fare.FareType;
 import org.opentripplanner.routing.core.FareRuleSet;
-import org.opentripplanner.routing.fares.MultipleFareServiceFactory;
+import org.opentripplanner.routing.core.StandardFareType;
 import org.opentripplanner.routing.services.FareService;
 import org.opentripplanner.routing.services.FareServiceFactory;
 import org.slf4j.Logger;
@@ -26,6 +27,7 @@
  * @author novalis
  *
  */
+@Component(key = "default", type = ServiceType.ServiceFactory)
 public class DefaultFareServiceFactory implements FareServiceFactory {
 
     private static final Logger LOG = LoggerFactory.getLogger(DefaultFareServiceFactory.class);
@@ -34,7 +36,7 @@ public class DefaultFareServiceFactory implements FareServiceFactory {
 
     public FareService makeFareService() {
         DefaultFareServiceImpl fareService = new DefaultFareServiceImpl();
-        fareService.addFareRules(FareType.regular, regularFareRules.values());
+        fareService.addFareRules(StandardFareType.regular, regularFareRules.values());
         return fareService;
     }
 
@@ -149,36 +151,15 @@ public static FareServiceFactory fromConfig(JsonNode config) {
         }
         LOG.debug("Fare type = " + type);
         FareServiceFactory retval;
-        switch (type) {
-        case "default":
-            retval = new DefaultFareServiceFactory();
-            break;
-        case "composite:additive":
-            retval = new MultipleFareServiceFactory.AddingMultipleFareServiceFactory();
-            break;
-        case "bike-rental-time-based":
-            retval = new TimeBasedBikeRentalFareServiceFactory();
-            break;
-        case "dutch":
-            retval = new DutchFareServiceFactory();
-            break;
-        case "san-francisco":
-            retval = new SFBayFareServiceFactory();
-            break;
-        case "new-york":
-            retval = new NycFareServiceFactory();
-            break;
-        case "seattle":
-            retval = new SeattleFareServiceFactory();
-            break;
-        case "HSL":
-            retval = new HSLFareServiceFactory();
-            break;
-        default:
-            throw new IllegalArgumentException(String.format("Unknown fare type: '%s'", type));
+        try {
+            retval = ComponentAnnotationConfigurator.getInstance()
+                .getComponentInstance(type, ServiceType.ServiceFactory);
+            retval.configure(config);
+            return retval;
+        } catch (Exception e) {
+            throw new IllegalArgumentException(
+                String.format("Failed to initialize fare type: '%s'", type));
         }
-        retval.configure(config);
-        return retval;
     }
 
     public String toString() { return this.getClass().getSimpleName(); }
diff --git a/src/main/java/org/opentripplanner/routing/impl/DefaultFareServiceImpl.java b/src/main/java/org/opentripplanner/routing/impl/DefaultFareServiceImpl.java
index a1030598429..c4b326cf51f 100644
--- a/src/main/java/org/opentripplanner/routing/impl/DefaultFareServiceImpl.java
+++ b/src/main/java/org/opentripplanner/routing/impl/DefaultFareServiceImpl.java
@@ -17,10 +17,11 @@
 import org.opentripplanner.model.FareAttribute;
 import org.opentripplanner.model.Stop;
 import org.opentripplanner.routing.core.Fare;
-import org.opentripplanner.routing.core.Fare.FareType;
 import org.opentripplanner.routing.core.FareComponent;
 import org.opentripplanner.routing.core.FareRuleSet;
+import org.opentripplanner.routing.core.FareType;
 import org.opentripplanner.routing.core.Money;
+import org.opentripplanner.routing.core.StandardFareType;
 import org.opentripplanner.routing.core.State;
 import org.opentripplanner.routing.core.WrappedCurrency;
 import org.opentripplanner.routing.edgetype.HopEdge;
@@ -40,7 +41,7 @@ class Ride {
     FeedScopedId route;
 
     FeedScopedId trip;
-    
+
     Set zones;
 
     String startZone;
@@ -141,7 +142,7 @@ class FareAndId {
  * It cannot necessarily handle multi-feed graphs, because a rule-less fare attribute
  * might be applied to rides on routes in another feed, for example.
  * For more interesting fare structures like New York's MTA, or cities with multiple
- * feeds and inter-feed transfer rules, you get to implement your own FareService. 
+ * feeds and inter-feed transfer rules, you get to implement your own FareService.
  * See this thread on gtfs-changes explaining the proper interpretation of fares.txt:
  * http://groups.google.com/group/gtfs-changes/browse_thread/thread/8a4a48ae1e742517/4f81b826cb732f3b
  */
@@ -190,7 +191,7 @@ protected List createRides(GraphPath path) {
             ride.endZone  = ride.lastStop.getZoneId();
             ride.zones.add(ride.endZone);
             ride.endTime  = state.getTimeSeconds();
-            // in default fare service, classify rides by mode 
+            // in default fare service, classify rides by mode
             ride.classifier = state.getBackMode();
         }
         return rides;
@@ -244,10 +245,10 @@ public boolean journeyAllowed(GraphPath path, Set allowedFareIds) {
         if (rides.size() == 0) {
             return true;
         }
-        Collection fareRules = fareRulesPerType.get(FareType.regular).stream().
+        Collection fareRules = fareRulesPerType.get(StandardFareType.regular).stream().
             filter(f -> allowedFareIds.contains(f.getFareAttribute().getId().toString())).collect(Collectors.toList());
 
-        return fareIsKnown(FareType.regular, rides, fareRules);
+        return fareIsKnown(StandardFareType.regular, rides, fareRules);
     }
 
     protected static Money getMoney(Currency currency, float cost) {
@@ -387,7 +388,7 @@ protected FareAndId getBestFareAndId(FareType fareType, List rides,
         Set agencies = new HashSet();
         Set trips = new HashSet();
         int transfersUsed = -1;
-        
+
         Ride firstRide = rides.get(0);
         long   startTime = firstRide.startTime;
         String startZone = firstRide.startZone;
@@ -409,12 +410,12 @@ protected FareAndId getBestFareAndId(FareType fareType, List rides,
             trips.add(ride.trip);
             transfersUsed += 1;
         }
-        
+
         FareAttribute bestAttribute = null;
         float bestFare = Float.POSITIVE_INFINITY;
         long tripTime = lastRideStartTime - startTime;
         long journeyTime = lastRideEndTime - startTime;
-        	
+
         // find the best fare that matches this set of rides
         for (FareRuleSet ruleSet : fareRules) {
             FareAttribute attribute = ruleSet.getFareAttribute();
@@ -422,7 +423,7 @@ protected FareAndId getBestFareAndId(FareType fareType, List rides,
             // check only if the fare is not mapped to an agency
             if (!ruleSet.hasAgencyDefined() && !attribute.getId().getAgencyId().equals(feedId))
                 continue;
-            
+
             if (ruleSet.matches(agencies, startZone, endZone, zones, routes, trips)) {
                 // TODO Maybe move the code below in FareRuleSet::matches() ?
                 if (attribute.isTransfersSet() && attribute.getTransfers() < transfersUsed) {
@@ -430,11 +431,11 @@ protected FareAndId getBestFareAndId(FareType fareType, List rides,
                 }
                 // assume transfers are evaluated at boarding time,
                 // as trimet does
-                if (attribute.isTransferDurationSet() && 
+                if (attribute.isTransferDurationSet() &&
                     tripTime > attribute.getTransferDuration()) {
                     continue;
                 }
-                if (attribute.isJourneyDurationSet() && 
+                if (attribute.isJourneyDurationSet() &&
                     journeyTime > attribute.getJourneyDuration()) {
                     continue;
                 }
@@ -453,22 +454,17 @@ protected FareAndId getBestFareAndId(FareType fareType, List rides,
     }
 
     protected float getFarePrice(FareAttribute fare, FareType type) {
-    	switch(type) {
-		case senior:
-			if (fare.getSeniorPrice() >= 0) {
-				return fare.getSeniorPrice();
-			}
-			break;
-		case youth:
-			if (fare.getYouthPrice() >= 0) {
-				return fare.getYouthPrice();
-			}
-			break;
-		case regular:
-		default:
-			break;
-    	}
-    	return fare.getPrice();
+        if (StandardFareType.senior.equals(type)) {
+            if (fare.getSeniorPrice() >= 0) {
+                return fare.getSeniorPrice();
+            }
+        }
+        if (StandardFareType.youth.equals(type)) {
+            if (fare.getYouthPrice() >= 0) {
+                return fare.getYouthPrice();
+            }
+        }
+        return fare.getPrice();
     }
 
 }
diff --git a/src/main/java/org/opentripplanner/routing/impl/DutchFareServiceFactory.java b/src/main/java/org/opentripplanner/routing/impl/DutchFareServiceFactory.java
index cd8448fa3a9..c9cc229c0d7 100644
--- a/src/main/java/org/opentripplanner/routing/impl/DutchFareServiceFactory.java
+++ b/src/main/java/org/opentripplanner/routing/impl/DutchFareServiceFactory.java
@@ -1,10 +1,13 @@
 package org.opentripplanner.routing.impl;
 
+import org.opentripplanner.annotation.Component;
+import org.opentripplanner.annotation.ServiceType;
 import org.opentripplanner.routing.services.FareService;
 
+@Component(key = "dutch",type = ServiceType.ServiceFactory)
 public class DutchFareServiceFactory extends DefaultFareServiceFactory {
     @Override
-    public FareService makeFareService() { 
+    public FareService makeFareService() {
         return new DutchFareServiceImpl(regularFareRules.values());
     }
 }
diff --git a/src/main/java/org/opentripplanner/routing/impl/DutchFareServiceImpl.java b/src/main/java/org/opentripplanner/routing/impl/DutchFareServiceImpl.java
index b58e02ffb7b..c6fc9c6e330 100644
--- a/src/main/java/org/opentripplanner/routing/impl/DutchFareServiceImpl.java
+++ b/src/main/java/org/opentripplanner/routing/impl/DutchFareServiceImpl.java
@@ -7,8 +7,9 @@
 import org.opentripplanner.common.model.P2;
 import org.opentripplanner.routing.core.FareRuleSet;
 import org.opentripplanner.routing.core.Fare;
-import org.opentripplanner.routing.core.Fare.FareType;
+import org.opentripplanner.routing.core.FareType;
 import org.opentripplanner.routing.core.Money;
+import org.opentripplanner.routing.core.StandardFareType;
 import org.opentripplanner.routing.spt.GraphPath;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -16,7 +17,7 @@
 public class DutchFareServiceImpl extends DefaultFareServiceImpl {
 
     public DutchFareServiceImpl(Collection regularFareRules) {
-        addFareRules(FareType.regular, regularFareRules);
+        addFareRules(StandardFareType.regular, regularFareRules);
     }
 
     private static final long serialVersionUID = 1L;
@@ -26,14 +27,14 @@ public DutchFareServiceImpl(Collection regularFareRules) {
     public static final int TRANSFER_DURATION = 60 * 35; /* tranfers within 35 min won't require a new base fare */
 
     final Currency euros = Currency.getInstance("EUR");
-    
+
     /**
      * This overridden method completely ignores the Currency object supplied by the caller.
      * This is because the caller in the superclass assumes the input data uses only one currency.
      * However, Dutch data contains fares in both Euros and Dutch Railways fare units, with the added complexity
      * that these pseudo-currency units do not have sub-units in the way Euros have cents, which leads to
-     * incorrect rounding and scaling etc.  While the fare rules consulted by this fare service do have a mix of EUR 
-     * and train pseudo-units, this Fare object is accumulating the monetary fare returned to the user and is known 
+     * incorrect rounding and scaling etc.  While the fare rules consulted by this fare service do have a mix of EUR
+     * and train pseudo-units, this Fare object is accumulating the monetary fare returned to the user and is known
      * to always be in Euros. See issue #2679 for discussion.
      */
     @Override
@@ -46,7 +47,7 @@ protected boolean populateFare(Fare fare, Currency currency, FareType fareType,
         }
         return false;
     }
- 
+
     /* The Netherlands has an almost uniform system for electronic ticketing using a NFC-card, branded as OV-chipkaart.
      *
      * To travel through all modes in The Netherlands a uses has two products on their card:
@@ -120,7 +121,7 @@ private UnitsFareZone getUnitsByZones(String agencyId, String startZone, String
 
         return null;
     }
-    
+
     private float getCostByUnits(String fareZone, int units, int prevSumUnits, Collection fareRules) {
         if (units == 0) {
             return 0f;
diff --git a/src/main/java/org/opentripplanner/routing/impl/HSLFareServiceFactory.java b/src/main/java/org/opentripplanner/routing/impl/HSLFareServiceFactory.java
index 5e6408d7b2f..70e60e13862 100644
--- a/src/main/java/org/opentripplanner/routing/impl/HSLFareServiceFactory.java
+++ b/src/main/java/org/opentripplanner/routing/impl/HSLFareServiceFactory.java
@@ -13,17 +13,17 @@ the License, or (at your option) any later version.
 
 package org.opentripplanner.routing.impl;
 
+import org.opentripplanner.annotation.Component;
+import org.opentripplanner.annotation.ServiceType;
+import org.opentripplanner.routing.core.StandardFareType;
 import org.opentripplanner.routing.services.FareService;
-import org.opentripplanner.routing.services.FareServiceFactory;
-import org.opentripplanner.routing.core.Fare.FareType;
-
-import com.fasterxml.jackson.databind.JsonNode;
 
+@Component(key = "HSL",type = ServiceType.ServiceFactory)
 public class HSLFareServiceFactory extends DefaultFareServiceFactory {
 
     public FareService makeFareService() {
         HSLFareServiceImpl fareService = new HSLFareServiceImpl();
-        fareService.addFareRules(FareType.regular, regularFareRules.values());
+        fareService.addFareRules(StandardFareType.regular, regularFareRules.values());
         return fareService;
     }
 }
diff --git a/src/main/java/org/opentripplanner/routing/impl/HSLFareServiceImpl.java b/src/main/java/org/opentripplanner/routing/impl/HSLFareServiceImpl.java
index c07a2542f15..cf058ad6395 100644
--- a/src/main/java/org/opentripplanner/routing/impl/HSLFareServiceImpl.java
+++ b/src/main/java/org/opentripplanner/routing/impl/HSLFareServiceImpl.java
@@ -20,17 +20,14 @@ the License, or (at your option) any later version.
 import java.util.Set;
 import java.util.Map;
 
-import org.opentripplanner.model.FeedScopedId;
 import org.opentripplanner.model.FareAttribute;
-import org.opentripplanner.routing.core.Fare;
-import org.opentripplanner.routing.core.Fare.FareType;
 import org.opentripplanner.routing.core.FareRuleSet;
+import org.opentripplanner.routing.core.FareType;
 import org.opentripplanner.routing.core.State;
 import org.opentripplanner.routing.edgetype.HopEdge;
 import org.opentripplanner.routing.edgetype.TransitBoardAlight;
 import org.opentripplanner.routing.graph.Edge;
 import org.opentripplanner.routing.spt.GraphPath;
-import org.opentripplanner.routing.impl.DefaultFareServiceImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/src/main/java/org/opentripplanner/routing/impl/NycFareServiceFactory.java b/src/main/java/org/opentripplanner/routing/impl/NycFareServiceFactory.java
index c2da4730dec..3b2dd7889ec 100644
--- a/src/main/java/org/opentripplanner/routing/impl/NycFareServiceFactory.java
+++ b/src/main/java/org/opentripplanner/routing/impl/NycFareServiceFactory.java
@@ -1,11 +1,14 @@
 package org.opentripplanner.routing.impl;
 
+import org.opentripplanner.annotation.Component;
+import org.opentripplanner.annotation.ServiceType;
 import org.opentripplanner.model.OtpTransitService;
 import org.opentripplanner.routing.services.FareService;
 import org.opentripplanner.routing.services.FareServiceFactory;
 
 import com.fasterxml.jackson.databind.JsonNode;
 
+@Component(key = "new-york",type = ServiceType.ServiceFactory)
 public class NycFareServiceFactory implements FareServiceFactory {
 
     public FareService makeFareService() {
diff --git a/src/main/java/org/opentripplanner/routing/impl/NycFareServiceImpl.java b/src/main/java/org/opentripplanner/routing/impl/NycFareServiceImpl.java
index 482be8620cc..ef7979adb57 100644
--- a/src/main/java/org/opentripplanner/routing/impl/NycFareServiceImpl.java
+++ b/src/main/java/org/opentripplanner/routing/impl/NycFareServiceImpl.java
@@ -4,7 +4,7 @@
 import org.opentripplanner.model.Route;
 import org.opentripplanner.model.Trip;
 import org.opentripplanner.routing.core.Fare;
-import org.opentripplanner.routing.core.Fare.FareType;
+import org.opentripplanner.routing.core.StandardFareType;
 import org.opentripplanner.routing.core.State;
 import org.opentripplanner.routing.core.WrappedCurrency;
 import org.opentripplanner.routing.edgetype.DwellEdge;
@@ -23,14 +23,14 @@
 import java.util.List;
 
 enum NycFareState {
-	INIT, 
+	INIT,
 	SUBWAY_PRE_TRANSFER,
 	SUBWAY_PRE_TRANSFER_WALKED,
 	SUBWAY_POST_TRANSFER,
-	SIR_PRE_TRANSFER, 
+	SIR_PRE_TRANSFER,
 	SIR_POST_TRANSFER_FROM_SUBWAY,
-	SIR_POST_TRANSFER_FROM_BUS, 
-	EXPENSIVE_EXPRESS_BUS, 
+	SIR_POST_TRANSFER_FROM_BUS,
+	EXPENSIVE_EXPRESS_BUS,
 	BUS_PRE_TRANSFER, CANARSIE,
 }
 
@@ -39,7 +39,7 @@ enum NycFareState {
  * with the following limitations:
  * (1) the two hour limit on transfers is not enforced
  * (2) the b61/b62 special case is not handled
- * (3) MNR, LIRR, and LI Bus are not supported -- only subways and buses   
+ * (3) MNR, LIRR, and LI Bus are not supported -- only subways and buses
  */
 public class NycFareServiceImpl implements FareService, Serializable {
         private static final Logger LOG = LoggerFactory.getLogger(NycFareServiceImpl.class);
@@ -139,7 +139,7 @@ public Fare getCost(GraphPath path) {
 							|| shortName.startsWith("QM")
 							|| shortName.startsWith("BM")) {
 						newRide.classifier = EXPRESS_BUS; // Express bus
-					} 
+					}
 
 					newRide.startTime = state.getTimeSeconds();
 				}
@@ -269,10 +269,10 @@ public Fare getCost(GraphPath path) {
 					totalFare += EXPRESS_FARE - ORDINARY_FARE;
 					state = NycFareState.INIT;
 				} else if (ride.classifier.equals(EXPENSIVE_EXPRESS_BUS)) {
-					totalFare += EXPENSIVE_EXPRESS_FARE; 
+					totalFare += EXPENSIVE_EXPRESS_FARE;
 					// no transfers to the BxMM4C
 				}
-				
+
 				break;
 			case SIR_PRE_TRANSFER:
 				if (ride.classifier.equals(SUBWAY)) {
@@ -329,7 +329,7 @@ public Fare getCost(GraphPath path) {
 				break;
 		    case SUBWAY_POST_TRANSFER:
 		    	if (ride.classifier.equals(WALK)) {
-		    		if (!canarsieFreeTransfer) { 
+		    		if (!canarsieFreeTransfer) {
 			    		/* note: if we end up walking to another subway after alighting
 			    		 * at Canarsie, we will mistakenly not be charged, but nobody
 			    		 * would ever do this */
@@ -354,13 +354,13 @@ public Fare getCost(GraphPath path) {
 				} else if (ride.classifier.equals(EXPENSIVE_EXPRESS_BUS)) {
 					totalFare += EXPENSIVE_EXPRESS_FARE;
 					state = NycFareState.BUS_PRE_TRANSFER;
-				} 
+				}
 			}
 		}
 
 		Currency currency = Currency.getInstance("USD");
 		Fare fare = new Fare();
-		fare.addFare(FareType.regular, new WrappedCurrency(currency),
+		fare.addFare(StandardFareType.regular, new WrappedCurrency(currency),
 				(int) Math.round(totalFare
 						* Math.pow(10, currency.getDefaultFractionDigits())));
 		return fare;
diff --git a/src/main/java/org/opentripplanner/routing/impl/SFBayFareServiceFactory.java b/src/main/java/org/opentripplanner/routing/impl/SFBayFareServiceFactory.java
index dbfff8acd6a..bddb13e6cd9 100644
--- a/src/main/java/org/opentripplanner/routing/impl/SFBayFareServiceFactory.java
+++ b/src/main/java/org/opentripplanner/routing/impl/SFBayFareServiceFactory.java
@@ -1,10 +1,13 @@
 package org.opentripplanner.routing.impl;
 
+import org.opentripplanner.annotation.Component;
+import org.opentripplanner.annotation.ServiceType;
 import org.opentripplanner.routing.services.FareService;
 
+@Component(key = "san-francisco",type = ServiceType.ServiceFactory)
 public class SFBayFareServiceFactory extends DefaultFareServiceFactory {
     @Override
-    public FareService makeFareService() { 
+    public FareService makeFareService() {
         return new SFBayFareServiceImpl(regularFareRules.values());
     }
 }
diff --git a/src/main/java/org/opentripplanner/routing/impl/SFBayFareServiceImpl.java b/src/main/java/org/opentripplanner/routing/impl/SFBayFareServiceImpl.java
index 2f407dff187..ac977244fa1 100644
--- a/src/main/java/org/opentripplanner/routing/impl/SFBayFareServiceImpl.java
+++ b/src/main/java/org/opentripplanner/routing/impl/SFBayFareServiceImpl.java
@@ -10,15 +10,16 @@
 
 import org.opentripplanner.routing.core.Fare;
 import org.opentripplanner.routing.core.FareRuleSet;
+import org.opentripplanner.routing.core.FareType;
+import org.opentripplanner.routing.core.StandardFareType;
 import org.opentripplanner.routing.core.TraverseMode;
-import org.opentripplanner.routing.core.Fare.FareType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class SFBayFareServiceImpl extends DefaultFareServiceImpl {
 
     public SFBayFareServiceImpl(Collection regularFareRules) {
-        addFareRules(FareType.regular, regularFareRules);
+        addFareRules(StandardFareType.regular, regularFareRules);
     }
 
     private static final long serialVersionUID = 20120229L;
@@ -34,7 +35,7 @@ public SFBayFareServiceImpl(Collection regularFareRules) {
     public static final Set SFMTA_BART_TRANSFER_STOPS = new HashSet(Arrays.asList(
             "EMBR", "MONT", "POWL", "CIVC", "16TH", "24TH", "GLEN", "BALB", "DALY"));
     public static final String SFMTA_BART_FREE_TRANSFER_STOP = "DALY";
-    
+
     @Override
     protected boolean populateFare(Fare fare, Currency currency, FareType fareType, List rides,
             Collection fareRules) {
@@ -74,7 +75,7 @@ protected float getLowestCost(FareType fareType, List rides,
                     if (ride.classifier == TraverseMode.CABLE_CAR) {
                         // no transfers issued or accepted
                         cost += CABLE_CAR_FARE;
-                    } else if (sfmtaTransferIssued == null || 
+                    } else if (sfmtaTransferIssued == null ||
                         sfmtaTransferIssued + SFMTA_TRANSFER_DURATION < ride.endTime) {
                         sfmtaTransferIssued = ride.startTime;
                         if (alightedBart != null &&
@@ -86,9 +87,9 @@ protected float getLowestCost(FareType fareType, List rides,
                             } else {
                                 cost += SFMTA_BART_TRANSFER_FARE;
                             }
-                        } else { 
+                        } else {
                             // no transfer, basic fare
-                            cost += SFMTA_BASE_FARE; 
+                            cost += SFMTA_BASE_FARE;
                         }
                     } else {
                         // SFMTA-SFMTA non-cable-car transfer within time limit, no cost
@@ -101,8 +102,8 @@ protected float getLowestCost(FareType fareType, List rides,
         if (bartBlock != null) {
             // finalize outstanding bart block, if any
             cost += calculateCost(fareType, bartBlock, fareRules);
-        }        
+        }
         return cost;
     }
-    
+
 }
diff --git a/src/main/java/org/opentripplanner/routing/impl/SeattleFareServiceFactory.java b/src/main/java/org/opentripplanner/routing/impl/SeattleFareServiceFactory.java
index b1b1344c35d..8412bf09cdc 100644
--- a/src/main/java/org/opentripplanner/routing/impl/SeattleFareServiceFactory.java
+++ b/src/main/java/org/opentripplanner/routing/impl/SeattleFareServiceFactory.java
@@ -3,17 +3,20 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import org.opentripplanner.annotation.Component;
+import org.opentripplanner.annotation.ServiceType;
 import org.opentripplanner.model.FeedScopedId;
 import org.opentripplanner.model.Trip;
 import org.opentripplanner.model.OtpTransitService;
-import org.opentripplanner.routing.core.Fare.FareType;
 import org.opentripplanner.routing.core.FareRuleSet;
+import org.opentripplanner.routing.core.StandardFareType;
 import org.opentripplanner.routing.services.FareService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.databind.JsonNode;
 
+@Component(key = "seattle",type = ServiceType.ServiceFactory)
 public class SeattleFareServiceFactory extends DefaultFareServiceFactory {
 
     @SuppressWarnings("unused")
@@ -21,11 +24,11 @@ public class SeattleFareServiceFactory extends DefaultFareServiceFactory {
 
     @Override
     public FareService makeFareService() {
-    	
+
     	SeattleFareServiceImpl fareService = new SeattleFareServiceImpl();
-    	fareService.addFareRules(FareType.regular, regularFareRules.values());
-    	fareService.addFareRules(FareType.youth, regularFareRules.values());
-    	fareService.addFareRules(FareType.senior, regularFareRules.values());
+    	fareService.addFareRules(StandardFareType.regular, regularFareRules.values());
+    	fareService.addFareRules(StandardFareType.youth, regularFareRules.values());
+    	fareService.addFareRules(StandardFareType.senior, regularFareRules.values());
 
     	return fareService;
     }
@@ -34,33 +37,33 @@ public FareService makeFareService() {
     public void configure(JsonNode config) {
         // No config for the moment
     }
-    
+
     @Override
     public void processGtfs(OtpTransitService transitService) {
     	// Add custom extension: trips may have a fare ID specified in KCM GTFS.
     	// Need to ensure that we are scoped to feed when adding trips to FareRuleSet,
     	// since fare IDs may not be unique across feeds and trip agency IDsqq
     	// may not match fare attribute agency IDs (which are feed IDs).
-    	
+
     	Map feedFareRules = new HashMap<>();
     	fillFareRules(null, transitService.getAllFareAttributes(),
                 transitService.getAllFareRules(), feedFareRules);
-    	
+
     	regularFareRules.putAll(feedFareRules);
-    	
+
     	Map feedFareRulesById = new HashMap<>();
-            
+
         for (FareRuleSet rule : regularFareRules.values()) {
         	String id = rule.getFareAttribute().getId().getId();
         	feedFareRulesById.put(id, rule);
         }
-        
+
         for (Trip trip : transitService.getAllTrips()) {
         	String fareId = trip.getFareId();
         	FareRuleSet rule = feedFareRulesById.get(fareId);
         	if (rule != null)
-        		rule.addTrip(trip.getId());        	
+        		rule.addTrip(trip.getId());
         }
-        	
+
     }
 }
diff --git a/src/main/java/org/opentripplanner/updater/GraphUpdaterConfigurator.java b/src/main/java/org/opentripplanner/updater/GraphUpdaterConfigurator.java
index 97673cfedcc..50ab6b2cd35 100644
--- a/src/main/java/org/opentripplanner/updater/GraphUpdaterConfigurator.java
+++ b/src/main/java/org/opentripplanner/updater/GraphUpdaterConfigurator.java
@@ -1,24 +1,16 @@
 package org.opentripplanner.updater;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import org.opentripplanner.annotation.ComponentAnnotationConfigurator;
+import org.opentripplanner.annotation.ServiceType;
 import org.opentripplanner.routing.graph.Graph;
-import org.opentripplanner.updater.alerts.GtfsRealtimeAlertsUpdater;
-import org.opentripplanner.updater.bike_park.BikeParkUpdater;
-import org.opentripplanner.updater.bike_rental.BikeRentalUpdater;
-import org.opentripplanner.updater.car_park.CarParkUpdater;
-import org.opentripplanner.updater.example.ExampleGraphUpdater;
-import org.opentripplanner.updater.example.ExamplePollingGraphUpdater;
-import org.opentripplanner.updater.stoptime.MqttGtfsRealtimeUpdater;
-import org.opentripplanner.updater.stoptime.PollingStoptimeUpdater;
-import org.opentripplanner.updater.stoptime.WebsocketGtfsRealtimeUpdater;
-import org.opentripplanner.updater.street_notes.WinkkiPollingGraphUpdater;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * Upon loading a Graph, configure/decorate it using a JSON tree from Jackson. This mainly involves starting
  * graph updater processes (GTFS-RT, bike rental, etc.), hence the class name.
- * 
+ *
  * When a Graph is loaded, one should call setupGraph() with the JSON tree containing configuration for the Graph.
  * That method creates "graph updaters" according to the given JSON, which should contain an array or object field
  * called "updaters". Each child element represents one updater.
@@ -68,36 +60,13 @@ private static GraphUpdaterManager createManagerFromConfig(Graph graph, JsonNode
             // For each sub-node, determine which kind of updater is being created.
             String type = configItem.path("type").asText();
             GraphUpdater updater = null;
+
             if (type != null) {
-                if (type.equals("bike-rental")) {
-                    updater = new BikeRentalUpdater();
-                }
-                else if (type.equals("bike-park")) {
-                    updater = new BikeParkUpdater();
-                }
-                else if (type.equals("car-park")) {
-                    updater = new CarParkUpdater();
-                }
-                else if (type.equals("stop-time-updater")) {
-                    updater = new PollingStoptimeUpdater();
-                }
-                else if (type.equals("websocket-gtfs-rt-updater")) {
-                    updater = new WebsocketGtfsRealtimeUpdater();
-                }
-                else if (type.equals("MQTT-gtfs-rt-updater")) {
-                    updater = new MqttGtfsRealtimeUpdater();
-                }
-                else if (type.equals("real-time-alerts")) {
-                    updater = new GtfsRealtimeAlertsUpdater();
-                }
-                else if (type.equals("example-updater")) {
-                    updater = new ExampleGraphUpdater();
-                }
-                else if (type.equals("example-polling-updater")) {
-                    updater = new ExamplePollingGraphUpdater();
-                }
-                else if (type.equals("winkki-polling-updater")) {
-                    updater = new WinkkiPollingGraphUpdater();
+                try{
+                    updater = ComponentAnnotationConfigurator.getInstance().getComponentInstance(type,
+                        ServiceType.GraphUpdater);
+                }catch (Exception e){
+                    LOG.error("Unable to initialize updater type :" + type);
                 }
             }
 
diff --git a/src/main/java/org/opentripplanner/updater/alerts/GtfsRealtimeAlertsUpdater.java b/src/main/java/org/opentripplanner/updater/alerts/GtfsRealtimeAlertsUpdater.java
index f3f635f555c..23a27e9da81 100644
--- a/src/main/java/org/opentripplanner/updater/alerts/GtfsRealtimeAlertsUpdater.java
+++ b/src/main/java/org/opentripplanner/updater/alerts/GtfsRealtimeAlertsUpdater.java
@@ -3,6 +3,8 @@
 import java.io.InputStream;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import org.opentripplanner.annotation.Component;
+import org.opentripplanner.annotation.ServiceType;
 import org.opentripplanner.routing.graph.Graph;
 import org.opentripplanner.routing.impl.AlertPatchServiceImpl;
 import org.opentripplanner.routing.services.AlertPatchService;
@@ -29,6 +31,7 @@
  * myalert.feedId = TA
  * 
*/ +@Component(key = "real-time-alerts", type = ServiceType.GraphUpdater) public class GtfsRealtimeAlertsUpdater extends PollingGraphUpdater { private static final Logger LOG = LoggerFactory.getLogger(GtfsRealtimeAlertsUpdater.class); diff --git a/src/main/java/org/opentripplanner/updater/bike_park/BikeParkUpdater.java b/src/main/java/org/opentripplanner/updater/bike_park/BikeParkUpdater.java index ba68ab7face..95b0ca61f5b 100644 --- a/src/main/java/org/opentripplanner/updater/bike_park/BikeParkUpdater.java +++ b/src/main/java/org/opentripplanner/updater/bike_park/BikeParkUpdater.java @@ -12,6 +12,8 @@ import java.util.prefs.Preferences; import com.fasterxml.jackson.databind.JsonNode; +import org.opentripplanner.annotation.Component; +import org.opentripplanner.annotation.ServiceType; import org.opentripplanner.graph_builder.linking.SimpleStreetSplitter; import org.opentripplanner.routing.bike_park.BikePark; import org.opentripplanner.routing.bike_rental.BikeRentalStationService; @@ -31,10 +33,11 @@ * * Bike park-and-ride and "OV-fiets mode" development has been funded by GoAbout * (https://goabout.com/). - * + * * @author laurent * @author GoAbout */ +@Component(key = "bike-park", type = ServiceType.GraphUpdater) public class BikeParkUpdater extends PollingGraphUpdater { private static final Logger LOG = LoggerFactory.getLogger(BikeParkUpdater.class); diff --git a/src/main/java/org/opentripplanner/updater/bike_rental/BikeRentalUpdater.java b/src/main/java/org/opentripplanner/updater/bike_rental/BikeRentalUpdater.java index 46cc4b2afd4..9e8cf554dac 100644 --- a/src/main/java/org/opentripplanner/updater/bike_rental/BikeRentalUpdater.java +++ b/src/main/java/org/opentripplanner/updater/bike_rental/BikeRentalUpdater.java @@ -1,5 +1,7 @@ package org.opentripplanner.updater.bike_rental; +import static org.opentripplanner.annotation.ServiceType.GraphUpdater; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -10,6 +12,7 @@ import java.util.Set; import java.util.concurrent.ExecutionException; import com.fasterxml.jackson.databind.JsonNode; +import org.opentripplanner.annotation.Component; import org.opentripplanner.graph_builder.linking.SimpleStreetSplitter; import org.opentripplanner.routing.bike_rental.BikeRentalStation; import org.opentripplanner.routing.bike_rental.BikeRentalStationService; @@ -29,6 +32,7 @@ /** * Dynamic bike-rental station updater which updates the Graph with bike rental stations from one BikeRentalDataSource. */ +@Component(key = "bike-rental",type = GraphUpdater) public class BikeRentalUpdater extends PollingGraphUpdater { private static final Logger LOG = LoggerFactory.getLogger(BikeRentalUpdater.class); diff --git a/src/main/java/org/opentripplanner/updater/example/ExampleGraphUpdater.java b/src/main/java/org/opentripplanner/updater/example/ExampleGraphUpdater.java index 9fbf95b3d07..6d12fab031e 100644 --- a/src/main/java/org/opentripplanner/updater/example/ExampleGraphUpdater.java +++ b/src/main/java/org/opentripplanner/updater/example/ExampleGraphUpdater.java @@ -4,6 +4,8 @@ import java.util.prefs.Preferences; import com.fasterxml.jackson.databind.JsonNode; +import org.opentripplanner.annotation.Component; +import org.opentripplanner.annotation.ServiceType; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.updater.GraphUpdater; import org.opentripplanner.updater.GraphUpdaterManager; @@ -15,22 +17,23 @@ * This class shows an example of how to implement a graph updater. Besides implementing the methods * of the interface GraphUpdater, the updater also needs to be registered in the function * GraphUpdaterConfigurator.applyConfigurationToGraph. - * + * * This example is suited for streaming updaters. For polling updaters it is better to use the * abstract base class PollingGraphUpdater. The class ExamplePollingGraphUpdater shows an example of * this. - * + * * Usage example ('example' name is an example) in the file 'Graph.properties': - * + * *
  * example.type = example-updater
  * example.frequencySec = 60
  * example.url = https://api.updater.com/example-updater
  * 
- * + * * @see ExamplePollingGraphUpdater * @see GraphUpdaterConfigurator.applyConfigurationToGraph */ +@Component(key = "example-updater", type = ServiceType.GraphUpdater) public class ExampleGraphUpdater implements GraphUpdater { private static Logger LOG = LoggerFactory.getLogger(ExampleGraphUpdater.class); diff --git a/src/main/java/org/opentripplanner/updater/example/ExamplePollingGraphUpdater.java b/src/main/java/org/opentripplanner/updater/example/ExamplePollingGraphUpdater.java index 7a430b99ffd..97d20b19433 100644 --- a/src/main/java/org/opentripplanner/updater/example/ExamplePollingGraphUpdater.java +++ b/src/main/java/org/opentripplanner/updater/example/ExamplePollingGraphUpdater.java @@ -3,6 +3,8 @@ import java.util.prefs.Preferences; import com.fasterxml.jackson.databind.JsonNode; +import org.opentripplanner.annotation.Component; +import org.opentripplanner.annotation.ServiceType; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.updater.GraphUpdaterManager; import org.opentripplanner.updater.GraphWriterRunnable; @@ -14,21 +16,22 @@ * This class shows an example of how to implement a polling graph updater. Besides implementing the * methods of the interface PollingGraphUpdater, the updater also needs to be registered in the * function GraphUpdaterConfigurator.applyConfigurationToGraph. - * + * * This example is suited for polling updaters. For streaming updaters (aka push updaters) it is * better to use the GraphUpdater interface directly for this purpose. The class ExampleGraphUpdater * shows an example of how to implement this. - * + * * Usage example ('polling-example' name is an example) in file 'Graph.properties': - * + * *
  * polling-example.type = example-polling-updater
  * polling-example.frequencySec = 60
  * polling-example.url = https://api.updater.com/example-polling-updater
  * 
- * + * * @see ExampleGraphUpdater */ +@Component(key = "example-polling-updater", type = ServiceType.GraphUpdater) public class ExamplePollingGraphUpdater extends PollingGraphUpdater { private static Logger LOG = LoggerFactory.getLogger(ExamplePollingGraphUpdater.class); @@ -72,7 +75,7 @@ protected void runPolling() { public void teardown() { LOG.info("Teardown example polling updater"); } - + // This is a private GraphWriterRunnable that can be executed to modify the graph private class ExampleGraphWriter implements GraphWriterRunnable { @Override diff --git a/src/main/java/org/opentripplanner/updater/stoptime/MqttGtfsRealtimeUpdater.java b/src/main/java/org/opentripplanner/updater/stoptime/MqttGtfsRealtimeUpdater.java index 846b7cf65f2..79c74a3dc18 100644 --- a/src/main/java/org/opentripplanner/updater/stoptime/MqttGtfsRealtimeUpdater.java +++ b/src/main/java/org/opentripplanner/updater/stoptime/MqttGtfsRealtimeUpdater.java @@ -10,6 +10,8 @@ import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttMessage; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; +import org.opentripplanner.annotation.Component; +import org.opentripplanner.annotation.ServiceType; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.updater.GraphUpdater; import org.opentripplanner.updater.GraphUpdaterManager; @@ -36,6 +38,7 @@ * * */ +@Component(key = "MQTT-gtfs-rt-updater", type = ServiceType.GraphUpdater) public class MqttGtfsRealtimeUpdater implements GraphUpdater { private static Logger LOG = LoggerFactory.getLogger(MqttGtfsRealtimeUpdater.class); diff --git a/src/main/java/org/opentripplanner/updater/stoptime/PollingStoptimeUpdater.java b/src/main/java/org/opentripplanner/updater/stoptime/PollingStoptimeUpdater.java index a247a7dd5e0..a208132874d 100644 --- a/src/main/java/org/opentripplanner/updater/stoptime/PollingStoptimeUpdater.java +++ b/src/main/java/org/opentripplanner/updater/stoptime/PollingStoptimeUpdater.java @@ -5,6 +5,8 @@ import java.util.concurrent.ExecutionException; import com.fasterxml.jackson.databind.JsonNode; +import org.opentripplanner.annotation.Component; +import org.opentripplanner.annotation.ServiceType; import org.opentripplanner.updater.*; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.util.SentryUtilities; @@ -27,6 +29,7 @@ * * */ +@Component(key = "stop-time-updater", type = ServiceType.GraphUpdater) public class PollingStoptimeUpdater extends PollingGraphUpdater { private static final Logger LOG = LoggerFactory.getLogger(PollingStoptimeUpdater.class); diff --git a/src/main/java/org/opentripplanner/updater/stoptime/WebsocketGtfsRealtimeUpdater.java b/src/main/java/org/opentripplanner/updater/stoptime/WebsocketGtfsRealtimeUpdater.java index 8af6f49f979..9d1b1374833 100644 --- a/src/main/java/org/opentripplanner/updater/stoptime/WebsocketGtfsRealtimeUpdater.java +++ b/src/main/java/org/opentripplanner/updater/stoptime/WebsocketGtfsRealtimeUpdater.java @@ -5,6 +5,8 @@ import java.util.concurrent.ExecutionException; import com.fasterxml.jackson.databind.JsonNode; +import org.opentripplanner.annotation.Component; +import org.opentripplanner.annotation.ServiceType; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.updater.GraphUpdater; import org.opentripplanner.updater.GraphUpdaterManager; @@ -38,6 +40,7 @@ * * */ +@Component(key = "websocket-gtfs-rt-updater", type = ServiceType.GraphUpdater) public class WebsocketGtfsRealtimeUpdater implements GraphUpdater { /** * Number of seconds to wait before checking again whether we are still connected @@ -159,7 +162,7 @@ public void onMessage(byte[] message) { // Decode message feedMessage = FeedMessage.PARSER.parseFrom(message); feedEntityList = feedMessage.getEntityList(); - + // Change fullDataset value if this is an incremental update if (feedMessage.hasHeader() && feedMessage.getHeader().hasIncrementality() @@ -167,7 +170,7 @@ public void onMessage(byte[] message) { .equals(GtfsRealtime.FeedHeader.Incrementality.DIFFERENTIAL)) { fullDataset = false; } - + // Create List of TripUpdates updates = new ArrayList(feedEntityList.size()); for (FeedEntity feedEntity : feedEntityList) { diff --git a/src/main/java/org/opentripplanner/updater/street_notes/WinkkiPollingGraphUpdater.java b/src/main/java/org/opentripplanner/updater/street_notes/WinkkiPollingGraphUpdater.java index a390fdcbd1d..77479676cfc 100644 --- a/src/main/java/org/opentripplanner/updater/street_notes/WinkkiPollingGraphUpdater.java +++ b/src/main/java/org/opentripplanner/updater/street_notes/WinkkiPollingGraphUpdater.java @@ -1,6 +1,8 @@ package org.opentripplanner.updater.street_notes; import org.opengis.feature.simple.SimpleFeature; +import org.opentripplanner.annotation.Component; +import org.opentripplanner.annotation.ServiceType; import org.opentripplanner.routing.alertpatch.Alert; import org.opentripplanner.util.NonLocalizedString; @@ -20,7 +22,7 @@ * */ - +@Component(key = "winkki-polling-updater", type = ServiceType.GraphUpdater) public class WinkkiPollingGraphUpdater extends WFSNotePollingGraphUpdater { protected Alert getNote(SimpleFeature feature) { Alert alert = Alert.createSimpleAlerts("winkki:" + feature.getAttribute("licence_type")); @@ -30,4 +32,4 @@ protected Alert getNote(SimpleFeature feature) { (Date) feature.getAttribute("event_startdate") : (Date) feature.getAttribute("licence_startdate"); return alert; } -} \ No newline at end of file +} diff --git a/src/test/java/external/service/ExternalGraphUpdater.java b/src/test/java/external/service/ExternalGraphUpdater.java new file mode 100644 index 00000000000..3bca4c40d4b --- /dev/null +++ b/src/test/java/external/service/ExternalGraphUpdater.java @@ -0,0 +1,37 @@ +package external.service; + +import com.fasterxml.jackson.databind.JsonNode; +import org.opentripplanner.annotation.Component; +import org.opentripplanner.annotation.ServiceType; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.updater.GraphUpdater; +import org.opentripplanner.updater.GraphUpdaterManager; + +@Component(key = "external.updater", type = ServiceType.GraphUpdater) +public class ExternalGraphUpdater implements GraphUpdater { + + @Override + public void setGraphUpdaterManager(GraphUpdaterManager updaterManager) { + + } + + @Override + public void setup(Graph graph) throws Exception { + + } + + @Override + public void run() throws Exception { + + } + + @Override + public void teardown() { + + } + + @Override + public void configure(Graph graph, JsonNode jsonNode) throws Exception { + + } +} diff --git a/src/test/java/org/opentripplanner/annotation/ComponentAnnotationConfiguratorTest.java b/src/test/java/org/opentripplanner/annotation/ComponentAnnotationConfiguratorTest.java new file mode 100644 index 00000000000..e7a26f3f940 --- /dev/null +++ b/src/test/java/org/opentripplanner/annotation/ComponentAnnotationConfiguratorTest.java @@ -0,0 +1,58 @@ +package org.opentripplanner.annotation; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import com.fasterxml.jackson.databind.JsonNode; +import external.service.ExternalGraphUpdater; +import java.io.File; +import java.util.Collections; +import org.junit.Test; +import org.opentripplanner.annotation.StaticClassComponent.FinalStaticClassComponent; +import org.opentripplanner.routing.impl.NycFareServiceFactory; +import org.opentripplanner.routing.services.FareServiceFactory; +import org.opentripplanner.standalone.OTPMain; +import org.opentripplanner.updater.GraphUpdater; + +public class ComponentAnnotationConfiguratorTest { + + ComponentAnnotationConfigurator configurator = ComponentAnnotationConfigurator.getInstance(); + + @Test + public void testNormalGraphUpdaterComponent() + throws InstantiationException, IllegalAccessException { + configurator.scanPackages(Collections.singletonList("org.opentripplanner.annotation")); + GraphUpdater component = configurator + .getComponentInstance("test.compoent", ServiceType.GraphUpdater); + assertNotNull(component); + assertTrue(TestComponent.class.isAssignableFrom(component.getClass())); + } + + @Test + public void testStaticInnerClass() throws InstantiationException, IllegalAccessException { + configurator.scanPackages(Collections.singletonList("org.opentripplanner.annotation")); + GraphUpdater component = configurator + .getComponentInstance("test.staticClassComponent", ServiceType.GraphUpdater); + assertNotNull(component); + assertTrue(StaticClassComponent.class.isAssignableFrom(component.getClass())); + assertTrue(FinalStaticClassComponent.class.isAssignableFrom(component.getClass())); + } + + @Test + public void testFromBuildConfig() throws InstantiationException, IllegalAccessException { + JsonNode builderConfig = OTPMain + .loadJson(new File("./src/test/resources/", "build-config.json")); + ComponentAnnotationConfigurator.getInstance().fromConfig(builderConfig); + GraphUpdater component = configurator + .getComponentInstance("external.updater", ServiceType.GraphUpdater); + assertNotNull(component); + assertTrue(GraphUpdater.class.isAssignableFrom(component.getClass())); + assertTrue(ExternalGraphUpdater.class.isAssignableFrom(component.getClass())); + + FareServiceFactory factory = configurator + .getComponentInstance("new-york", ServiceType.ServiceFactory); + assertNotNull(factory); + assertTrue(NycFareServiceFactory.class.isAssignableFrom(factory.getClass())); + assertTrue(FareServiceFactory.class.isAssignableFrom(factory.getClass())); + } +} diff --git a/src/test/java/org/opentripplanner/annotation/StaticClassComponent.java b/src/test/java/org/opentripplanner/annotation/StaticClassComponent.java new file mode 100644 index 00000000000..ad656d81453 --- /dev/null +++ b/src/test/java/org/opentripplanner/annotation/StaticClassComponent.java @@ -0,0 +1,38 @@ +package org.opentripplanner.annotation; + +import com.fasterxml.jackson.databind.JsonNode; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.updater.GraphUpdater; +import org.opentripplanner.updater.GraphUpdaterManager; + +public abstract class StaticClassComponent implements GraphUpdater { + + @Component(key = "test.staticClassComponent", type = ServiceType.GraphUpdater) + public static class FinalStaticClassComponent extends StaticClassComponent { + + @Override + public void setGraphUpdaterManager(GraphUpdaterManager updaterManager) { + + } + + @Override + public void setup(Graph graph) throws Exception { + + } + + @Override + public void run() throws Exception { + + } + + @Override + public void teardown() { + + } + + @Override + public void configure(Graph graph, JsonNode jsonNode) throws Exception { + + } + } +} diff --git a/src/test/java/org/opentripplanner/annotation/TestComponent.java b/src/test/java/org/opentripplanner/annotation/TestComponent.java new file mode 100644 index 00000000000..f0702e08b2f --- /dev/null +++ b/src/test/java/org/opentripplanner/annotation/TestComponent.java @@ -0,0 +1,36 @@ +package org.opentripplanner.annotation; + +import static org.opentripplanner.annotation.ServiceType.GraphUpdater; + +import com.fasterxml.jackson.databind.JsonNode; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.updater.GraphUpdaterManager; + +@Component(key = "test.compoent", type = GraphUpdater) +public class TestComponent implements org.opentripplanner.updater.GraphUpdater { + + @Override + public void setGraphUpdaterManager(GraphUpdaterManager updaterManager) { + + } + + @Override + public void setup(Graph graph) throws Exception { + + } + + @Override + public void run() throws Exception { + + } + + @Override + public void teardown() { + + } + + @Override + public void configure(Graph graph, JsonNode jsonNode) throws Exception { + + } +} diff --git a/src/test/java/org/opentripplanner/api/resource/GraphPathToTripPlanConverterTest.java b/src/test/java/org/opentripplanner/api/resource/GraphPathToTripPlanConverterTest.java index 921733bff8d..23ed43caad5 100644 --- a/src/test/java/org/opentripplanner/api/resource/GraphPathToTripPlanConverterTest.java +++ b/src/test/java/org/opentripplanner/api/resource/GraphPathToTripPlanConverterTest.java @@ -33,10 +33,10 @@ import org.opentripplanner.routing.alertpatch.TimePeriod; import org.opentripplanner.routing.bike_rental.BikeRentalStation; import org.opentripplanner.routing.core.Fare; -import org.opentripplanner.routing.core.Fare.FareType; import org.opentripplanner.routing.core.RoutingContext; import org.opentripplanner.routing.core.RoutingRequest; import org.opentripplanner.routing.core.ServiceDay; +import org.opentripplanner.routing.core.StandardFareType; import org.opentripplanner.routing.core.State; import org.opentripplanner.routing.core.TraverseMode; import org.opentripplanner.routing.core.WrappedCurrency; @@ -673,7 +673,7 @@ private GraphPath[] buildPaths() { // Create dummy TimetableSnapshot TimetableSnapshot snapshot = new TimetableSnapshot(); - + // Mock TimetableSnapshotSource to return dummy TimetableSnapshot TimetableSnapshotSource timetableSnapshotSource = mock(TimetableSnapshotSource.class); @@ -978,11 +978,11 @@ private void compareItinerary(Itinerary itinerary, Type type) { /** Compare the computed fare to its expected value. */ private void compareFare(Fare fare) { - assertEquals(0, fare.getFare(FareType.regular).getCents()); - assertEquals(1, fare.getFare(FareType.student).getCents()); - assertEquals(2, fare.getFare(FareType.senior).getCents()); - assertEquals(4, fare.getFare(FareType.tram).getCents()); - assertEquals(8, fare.getFare(FareType.special).getCents()); + assertEquals(0, fare.getFare(StandardFareType.regular).getCents()); + assertEquals(1, fare.getFare(StandardFareType.student).getCents()); + assertEquals(2, fare.getFare(StandardFareType.senior).getCents()); + assertEquals(4, fare.getFare(StandardFareType.tram).getCents()); + assertEquals(8, fare.getFare(StandardFareType.special).getCents()); } /** Compare all simple leg fields to their expected values, leg by leg. */ @@ -2036,11 +2036,11 @@ private static final class FareServiceStub implements FareService { @Override public Fare getCost(GraphPath path) { Fare fare = new Fare(); - fare.addFare(FareType.regular, new WrappedCurrency(), 0); - fare.addFare(FareType.student, new WrappedCurrency(), 1); - fare.addFare(FareType.senior, new WrappedCurrency(), 2); - fare.addFare(FareType.tram, new WrappedCurrency(), 4); - fare.addFare(FareType.special, new WrappedCurrency(), 8); + fare.addFare(StandardFareType.regular, new WrappedCurrency(), 0); + fare.addFare(StandardFareType.student, new WrappedCurrency(), 1); + fare.addFare(StandardFareType.senior, new WrappedCurrency(), 2); + fare.addFare(StandardFareType.tram, new WrappedCurrency(), 4); + fare.addFare(StandardFareType.special, new WrappedCurrency(), 8); return fare; } } diff --git a/src/test/java/org/opentripplanner/routing/algorithm/TestFares.java b/src/test/java/org/opentripplanner/routing/algorithm/TestFares.java index 13a53048b1c..f6661283ff4 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/TestFares.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/TestFares.java @@ -7,10 +7,10 @@ import org.opentripplanner.gtfs.GtfsContext; import org.opentripplanner.gtfs.GtfsLibrary; import org.opentripplanner.routing.core.Fare; -import org.opentripplanner.routing.core.Fare.FareType; import org.opentripplanner.routing.core.FareComponent; import org.opentripplanner.routing.core.Money; import org.opentripplanner.routing.core.RoutingRequest; +import org.opentripplanner.routing.core.StandardFareType; import org.opentripplanner.routing.core.WrappedCurrency; import org.opentripplanner.routing.edgetype.factory.PatternHopFactory; import org.opentripplanner.routing.graph.Graph; @@ -28,7 +28,7 @@ public class TestFares extends TestCase { private AStar aStar = new AStar(); - + public void testBasic() throws Exception { Graph gg = new Graph(); @@ -51,9 +51,9 @@ public void testBasic() throws Exception { path = spt.getPath(gg.getVertex(feedId + ":Mountain View Caltrain"), true); FareService fareService = gg.getService(FareService.class); - + Fare cost = fareService.getCost(path); - assertEquals(cost.getFare(FareType.regular), new Money(new WrappedCurrency("USD"), 425)); + assertEquals(cost.getFare(StandardFareType.regular), new Money(new WrappedCurrency("USD"), 425)); } public void testPortland() throws Exception { @@ -74,7 +74,7 @@ public void testPortland() throws Exception { FareService fareService = gg.getService(FareService.class); Fare cost = fareService.getCost(path); - assertEquals(new Money(new WrappedCurrency("USD"), 200), cost.getFare(FareType.regular)); + assertEquals(new Money(new WrappedCurrency("USD"), 200), cost.getFare(StandardFareType.regular)); // long trip @@ -86,9 +86,9 @@ public void testPortland() throws Exception { path = spt.getPath(gg.getVertex(feedId + ":1252"), true); assertNotNull(path); cost = fareService.getCost(path); - + //assertEquals(cost.getFare(FareType.regular), new Money(new WrappedCurrency("USD"), 460)); - + // complex trip options.maxTransfers = 5; startTime = TestUtils.dateInSeconds("America/Los_Angeles", 2009, 11, 1, 14, 0, 0); @@ -104,16 +104,16 @@ public void testPortland() throws Exception { // thread on gtfs-changes. // assertEquals(cost.getFare(FareType.regular), new Money(new WrappedCurrency("USD"), 430)); } - - + + public void testKCM() throws Exception { - + Graph gg = new Graph(); GtfsContext context = GtfsLibrary.readGtfs(new File(ConstantsForTests.KCM_GTFS)); - + PatternHopFactory factory = new PatternHopFactory(context); factory.setFareServiceFactory(new SeattleFareServiceFactory()); - + factory.run(gg); gg.putService( CalendarServiceData.class, @@ -121,14 +121,14 @@ public void testKCM() throws Exception { ); RoutingRequest options = new RoutingRequest(); String feedId = gg.getFeedIds().iterator().next(); - + String vertex0 = feedId + ":2010"; String vertex1 = feedId + ":2140"; ShortestPathTree spt; GraphPath path = null; - - FareService fareService = gg.getService(FareService.class); - + + FareService fareService = gg.getService(FareService.class); + long offPeakStartTime = TestUtils.dateInSeconds("America/Los_Angeles", 2016, 5, 24, 5, 0, 0); options.dateTime = offPeakStartTime; options.setRoutingContext(gg, vertex0, vertex1); @@ -136,8 +136,8 @@ public void testKCM() throws Exception { path = spt.getPath(gg.getVertex(vertex1), true); Fare costOffPeak = fareService.getCost(path); - assertEquals(costOffPeak.getFare(FareType.regular), new Money(new WrappedCurrency("USD"), 250)); - + assertEquals(costOffPeak.getFare(StandardFareType.regular), new Money(new WrappedCurrency("USD"), 250)); + long onPeakStartTime = TestUtils.dateInSeconds("America/Los_Angeles", 2016, 5, 24, 8, 0, 0); options.dateTime = onPeakStartTime; options.setRoutingContext(gg, vertex0, vertex1); @@ -145,8 +145,8 @@ public void testKCM() throws Exception { path = spt.getPath(gg.getVertex(vertex1), true); Fare costOnPeak = fareService.getCost(path); - assertEquals(costOnPeak.getFare(FareType.regular), new Money(new WrappedCurrency("USD"), 275)); - + assertEquals(costOnPeak.getFare(StandardFareType.regular), new Money(new WrappedCurrency("USD"), 275)); + } public void testFareComponent() throws Exception { @@ -174,7 +174,7 @@ public void testFareComponent() throws Exception { spt = aStar.getShortestPathTree(options); path = spt.getPath(gg.getVertex(feedId+":B"), true); fare = fareService.getCost(path); - fareComponents = fare.getDetails(FareType.regular); + fareComponents = fare.getDetails(StandardFareType.regular); assertEquals(fareComponents.size(), 1); assertEquals(fareComponents.get(0).price, tenUSD); assertEquals(fareComponents.get(0).fareId, new FeedScopedId(feedId, "AB")); @@ -192,7 +192,7 @@ public void testFareComponent() throws Exception { spt = aStar.getShortestPathTree(options); path = spt.getPath(gg.getVertex(feedId+":C"), true); fare = fareService.getCost(path); - fareComponents = fare.getDetails(FareType.regular); + fareComponents = fare.getDetails(StandardFareType.regular); assertEquals(fareComponents.size(), 2); assertEquals(fareComponents.get(0).price, tenUSD); assertEquals(fareComponents.get(0).fareId, new FeedScopedId(feedId, "AB")); @@ -206,7 +206,7 @@ public void testFareComponent() throws Exception { spt = aStar.getShortestPathTree(options); path = spt.getPath(gg.getVertex(feedId+":D"), true); fare = fareService.getCost(path); - fareComponents = fare.getDetails(FareType.regular); + fareComponents = fare.getDetails(StandardFareType.regular); assertEquals(fareComponents.size(), 1); assertEquals(fareComponents.get(0).price, tenUSD); assertEquals(fareComponents.get(0).fareId, new FeedScopedId(feedId, "BD")); @@ -218,7 +218,7 @@ public void testFareComponent() throws Exception { spt = aStar.getShortestPathTree(options); path = spt.getPath(gg.getVertex(feedId+":G"), true); fare = fareService.getCost(path); - fareComponents = fare.getDetails(FareType.regular); + fareComponents = fare.getDetails(StandardFareType.regular); assertEquals(fareComponents.size(), 1); assertEquals(fareComponents.get(0).price, tenUSD); assertEquals(fareComponents.get(0).fareId, new FeedScopedId(feedId, "EG")); @@ -230,7 +230,7 @@ public void testFareComponent() throws Exception { spt = aStar.getShortestPathTree(options); path = spt.getPath(gg.getVertex(feedId+":E"), true); fare = fareService.getCost(path); - fareComponents = fare.getDetails(FareType.regular); + fareComponents = fare.getDetails(StandardFareType.regular); assertEquals(fareComponents.size(), 1); assertEquals(fareComponents.get(0).price, tenUSD); assertEquals(fareComponents.get(0).fareId, new FeedScopedId(feedId, "CD")); @@ -241,7 +241,7 @@ public void testFareComponent() throws Exception { spt = aStar.getShortestPathTree(options); path = spt.getPath(gg.getVertex(feedId+":G"), true); fare = fareService.getCost(path); - fareComponents = fare.getDetails(FareType.regular); + fareComponents = fare.getDetails(StandardFareType.regular); assertEquals(fareComponents.size(), 1); assertEquals(fareComponents.get(0).price, tenUSD); assertEquals(fareComponents.get(0).fareId, new FeedScopedId(feedId, "EG")); @@ -253,7 +253,7 @@ public void testFareComponent() throws Exception { spt = aStar.getShortestPathTree(options); path = spt.getPath(gg.getVertex(feedId+":D"), true); fare = fareService.getCost(path); - fareComponents = fare.getDetails(FareType.regular); + fareComponents = fare.getDetails(StandardFareType.regular); assertEquals(fareComponents.size(), 2); assertEquals(fareComponents.get(0).price, tenUSD); assertEquals(fareComponents.get(0).fareId, new FeedScopedId(feedId, "AB")); diff --git a/src/test/java/org/opentripplanner/routing/fares/MultipleFareServiceTest.java b/src/test/java/org/opentripplanner/routing/fares/MultipleFareServiceTest.java index 452f59b4456..156513cbdb2 100644 --- a/src/test/java/org/opentripplanner/routing/fares/MultipleFareServiceTest.java +++ b/src/test/java/org/opentripplanner/routing/fares/MultipleFareServiceTest.java @@ -6,13 +6,13 @@ import junit.framework.TestCase; import org.opentripplanner.routing.core.Fare; -import org.opentripplanner.routing.core.Fare.FareType; +import org.opentripplanner.routing.core.StandardFareType; import org.opentripplanner.routing.core.WrappedCurrency; import org.opentripplanner.routing.services.FareService; import org.opentripplanner.routing.spt.GraphPath; /** - * + * * @author laurent */ public class MultipleFareServiceTest extends TestCase { @@ -34,12 +34,12 @@ public Fare getCost(GraphPath path) { public void testAddingMultipleFareService() { Fare fare1 = new Fare(); - fare1.addFare(FareType.regular, new WrappedCurrency("EUR"), 100); + fare1.addFare(StandardFareType.regular, new WrappedCurrency("EUR"), 100); FareService fs1 = new SimpleFareService(fare1); Fare fare2 = new Fare(); - fare2.addFare(FareType.regular, new WrappedCurrency("EUR"), 140); - fare2.addFare(FareType.student, new WrappedCurrency("EUR"), 120); + fare2.addFare(StandardFareType.regular, new WrappedCurrency("EUR"), 140); + fare2.addFare(StandardFareType.student, new WrappedCurrency("EUR"), 120); FareService fs2 = new SimpleFareService(fare2); /* @@ -47,7 +47,7 @@ public void testAddingMultipleFareService() { * "regular" fare in case you want to add bike and transit fares. */ Fare fare3 = new Fare(); - fare3.addFare(FareType.student, new WrappedCurrency("EUR"), 80); + fare3.addFare(StandardFareType.student, new WrappedCurrency("EUR"), 80); FareService fs3 = new SimpleFareService(fare3); AddingMultipleFareService mfs = new AddingMultipleFareService(new ArrayList()); @@ -56,37 +56,37 @@ public void testAddingMultipleFareService() { mfs = new AddingMultipleFareService(Arrays.asList(fs1)); fare = mfs.getCost(null); - assertEquals(100, fare.getFare(FareType.regular).getCents()); - assertEquals(null, fare.getFare(FareType.student)); + assertEquals(100, fare.getFare(StandardFareType.regular).getCents()); + assertEquals(null, fare.getFare(StandardFareType.student)); mfs = new AddingMultipleFareService(Arrays.asList(fs2)); fare = mfs.getCost(null); - assertEquals(140, fare.getFare(FareType.regular).getCents()); - assertEquals(120, fare.getFare(FareType.student).getCents()); + assertEquals(140, fare.getFare(StandardFareType.regular).getCents()); + assertEquals(120, fare.getFare(StandardFareType.student).getCents()); mfs = new AddingMultipleFareService(Arrays.asList(fs1, fs2)); fare = mfs.getCost(null); - assertEquals(240, fare.getFare(FareType.regular).getCents()); - assertEquals(220, fare.getFare(FareType.student).getCents()); + assertEquals(240, fare.getFare(StandardFareType.regular).getCents()); + assertEquals(220, fare.getFare(StandardFareType.student).getCents()); mfs = new AddingMultipleFareService(Arrays.asList(fs2, fs1)); fare = mfs.getCost(null); - assertEquals(240, fare.getFare(FareType.regular).getCents()); - assertEquals(220, fare.getFare(FareType.student).getCents()); + assertEquals(240, fare.getFare(StandardFareType.regular).getCents()); + assertEquals(220, fare.getFare(StandardFareType.student).getCents()); mfs = new AddingMultipleFareService(Arrays.asList(fs1, fs3)); fare = mfs.getCost(null); - assertEquals(100, fare.getFare(FareType.regular).getCents()); - assertEquals(180, fare.getFare(FareType.student).getCents()); + assertEquals(100, fare.getFare(StandardFareType.regular).getCents()); + assertEquals(180, fare.getFare(StandardFareType.student).getCents()); mfs = new AddingMultipleFareService(Arrays.asList(fs3, fs1)); fare = mfs.getCost(null); - assertEquals(100, fare.getFare(FareType.regular).getCents()); - assertEquals(180, fare.getFare(FareType.student).getCents()); + assertEquals(100, fare.getFare(StandardFareType.regular).getCents()); + assertEquals(180, fare.getFare(StandardFareType.student).getCents()); mfs = new AddingMultipleFareService(Arrays.asList(fs1, fs2, fs3)); fare = mfs.getCost(null); - assertEquals(240, fare.getFare(FareType.regular).getCents()); - assertEquals(300, fare.getFare(FareType.student).getCents()); + assertEquals(240, fare.getFare(StandardFareType.regular).getCents()); + assertEquals(300, fare.getFare(StandardFareType.student).getCents()); } } diff --git a/src/test/java/org/opentripplanner/routing/flex/VermontFlexRoutingTest.java b/src/test/java/org/opentripplanner/routing/flex/VermontFlexRoutingTest.java index 9efad8720cb..677374f881b 100644 --- a/src/test/java/org/opentripplanner/routing/flex/VermontFlexRoutingTest.java +++ b/src/test/java/org/opentripplanner/routing/flex/VermontFlexRoutingTest.java @@ -6,6 +6,7 @@ import org.opentripplanner.routing.algorithm.AStar; import org.opentripplanner.routing.core.Fare; import org.opentripplanner.routing.core.RoutingRequest; +import org.opentripplanner.routing.core.StandardFareType; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.impl.GraphPathFinder; import org.opentripplanner.routing.services.FareService; @@ -253,7 +254,7 @@ private void checkFare(GraphPath path) { FareService fareService = graph.getService(FareService.class); Fare cost = fareService.getCost(path); assertNotNull(cost); - assertEquals("920", cost.getDetails(Fare.FareType.regular).iterator().next().fareId.getId()); + assertEquals("920", cost.getDetails(StandardFareType.regular).iterator().next().fareId.getId()); } private void assertDateEquals(long timeSeconds, String date, String time, TimeZone timeZone) { diff --git a/src/test/resources/build-config.json b/src/test/resources/build-config.json new file mode 100644 index 00000000000..a7b593eb614 --- /dev/null +++ b/src/test/resources/build-config.json @@ -0,0 +1,3 @@ +{ + "components.packages": ["external.service"] +}