Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Public/master #323

Open
wants to merge 5 commits into
base: dev-1.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<groupId>org.opentripplanner</groupId>
<artifactId>otp</artifactId>
<version>1.5.0-SNAPSHOT</version>
<version>1.5.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<licenses>
Expand Down Expand Up @@ -242,21 +242,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>sign-artifacts</id>
<!-- We sign in the verify phase, which means it will happen for install and deploy but not package. -->
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/org/opentripplanner/annotation/Component.java
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
@@ -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<ServiceType, Map<String, Class<?>>> 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<String> 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<String> packages) {
packages.stream().map(this::getClasses).flatMap(Collection::stream)
.forEach(this::setupRegisteredComponent);
}

public <T> 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<String, Class<?>> 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<Class> getClasses(String packageName) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
assert classLoader != null;
String path = packageName.replace('.', '/');
Enumeration<URL> resources = null;
try {
resources = classLoader.getResources(path);
} catch (IOException e) {
e.printStackTrace();
return emptyList();
}
List<File> 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<Class> findClasses(File directory, String packageName) {
if (!directory.isDirectory()) {
if (isJarFile(directory)) {
return loadFromJar(directory, packageName);
}
return emptyList();
} else {
return loadFromDirectory(directory, packageName);
}
}

private List<Class> 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<Class> loadFromDirectory(File directory, String packageName) {
List<Class> 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 <T> Stream<T> enumerationAsStream(Enumeration<T> e) {
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(
new Iterator<T>() {
public T next() {
return e.nextElement();
}

public boolean hasNext() {
return e.hasMoreElements();
}
},
Spliterator.ORDERED), false);
}
}
5 changes: 5 additions & 0 deletions src/main/java/org/opentripplanner/annotation/ServiceType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.opentripplanner.annotation;

public enum ServiceType {
GraphUpdater, ServiceFactory;
}
18 changes: 10 additions & 8 deletions src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -45,21 +46,21 @@
* 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";

private List<GraphBuilderModule> _graphBuilderModules = new ArrayList<GraphBuilderModule>();

private final File graphFile;

private boolean _alwaysRebuild = true;

private List<RoutingRequest> modeList;

private String baseGraph = null;

private Graph graph = new Graph();

/** Should the graph be serialized to disk after being created or not? */
Expand All @@ -86,7 +87,7 @@ public void setGraphBuilders(List<GraphBuilderModule> graphLoaders) {
public void setAlwaysRebuild(boolean alwaysRebuild) {
_alwaysRebuild = alwaysRebuild;
}

public void setBaseGraph(String baseGraph) {
this.baseGraph = baseGraph;
try {
Expand All @@ -113,7 +114,7 @@ public void run() {
long startTime = System.currentTimeMillis();

if (serializeGraph) {

if (graphFile == null) {
throw new RuntimeException("graphBuilderTask has no attribute graphFile.");
}
Expand All @@ -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()) {
Expand All @@ -139,7 +140,7 @@ public void run() {
for (GraphBuilderModule builder : _graphBuilderModules) {
builder.checkInputs();
}

HashMap<Class<?>, Object> extra = new HashMap<Class<?>, Object>();
for (GraphBuilderModule load : _graphBuilderModules)
load.buildGraph(graph, extra);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
6 changes: 0 additions & 6 deletions src/main/java/org/opentripplanner/routing/core/Fare.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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}.
*/
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/org/opentripplanner/routing/core/FareType.java
Original file line number Diff line number Diff line change
@@ -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();
}
Loading