package org.opentripplanner.standalone;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import com.fasterxml.jackson.jaxrs.xml.JacksonXMLProvider;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.opentripplanner.api.common.OTPExceptionMapper;
import org.opentripplanner.api.model.JSONObjectMapperProvider;
import org.opentripplanner.api.resource.AlertPatcher;
import org.opentripplanner.api.resource.BikeRental;
import org.opentripplanner.api.resource.ExternalGeocoderResource;
import org.opentripplanner.api.resource.GraphInspectorTileResource;
import org.opentripplanner.api.resource.Metadata;
import org.opentripplanner.api.resource.Planner;
import org.opentripplanner.api.resource.PointSetResource;
import org.opentripplanner.api.resource.ProfileResource;
import org.opentripplanner.api.resource.Routers;
import org.opentripplanner.api.resource.ServerInfo;
import org.opentripplanner.api.resource.LIsochrone;
import org.opentripplanner.api.resource.LegendResource;
import org.opentripplanner.api.resource.Raster;
import org.opentripplanner.api.resource.SIsochrone;
import org.opentripplanner.api.resource.SimpleIsochrone;
import org.opentripplanner.api.resource.SurfaceResource;
import org.opentripplanner.api.resource.TileService;
import org.opentripplanner.api.resource.TimeGridWs;
import org.opentripplanner.index.GeocoderResource;
import org.opentripplanner.index.IndexAPI;
import org.slf4j.bridge.SLF4JBridgeHandler;
import javax.ws.rs.core.Application;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
/**
* A JAX-RS Application subclass which provides hard-wired configuration of an OTP server.
* Avoids auto-scanning of any kind, and keeps injection to a bare minimum using HK2, the injection
* library Jersey itself uses.
*
* Jersey has its own ResourceConfig class which is a subclass of Application.
* We can get away with not using any Jersey-specific "conveniences" and stick with stock JAX-RS.
*/
public class OTPApplication extends Application {
static {
// Remove existing handlers attached to the j.u.l root logger
SLF4JBridgeHandler.removeHandlersForRootLogger();
// Bridge j.u.l (used by Jersey) to the SLF4J root logger, so all logging goes through the same API
SLF4JBridgeHandler.install();
}
/* This object groups together all the modules for a single running OTP server. */
public final OTPServer server;
/* If secure is true, OTP will require Basic authentication over HTTPS when accessing dangerous web services. */
private final boolean secure;
/**
* The OTPServer provides entry points to OTP routing functionality for a collection of OTPRouters.
* It provides a Java API, not an HTTP API.
* The OTPApplication wraps an OTPServer in a Jersey (JAX-RS) Application, configuring an HTTP API.
* @param server The OTP server to wrap
* @param secure Should this server require authentication over HTTPS to access secure resources, e.g. /routers?
*/
public OTPApplication (OTPServer server, boolean secure) {
this.server = server;
this.secure = secure;
}
/**
* This method registers classes with Jersey to define web resources and enable custom features.
* These are classes (not instances) that will be instantiated by Jersey for each request (they are request-scoped).
* Types that have been confirmed to work are: annotated resources, ContextResolver<ObjectMapper> implementation,
* ContainerResponseFilter and ContainerRequestFilter.
* Note that the listed classes do not need to be annotated with @Provider -- that is for scanning config.
*/
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = Sets.newHashSet();
classes.addAll(Arrays.asList(
/* Jersey resource classes: define web services, i.e. an HTTP API. */
Planner.class,
IndexAPI.class,
ExternalGeocoderResource.class,
GeocoderResource.class,
SimpleIsochrone.class,
TileService.class,
BikeRental.class,
LIsochrone.class,
ExternalGeocoderResource.class,
TimeGridWs.class,
AlertPatcher.class,
Planner.class,
SIsochrone.class,
Routers.class,
Raster.class,
LegendResource.class,
Metadata.class,
ProfileResource.class,
SimpleIsochrone.class,
ServerInfo.class,
SurfaceResource.class,
PointSetResource.class,
GraphInspectorTileResource.class,
/* Features and Filters: extend Jersey, manipulate requests and responses. */
CorsFilter.class
));
if (this.secure) {
// A filter that converts HTTP Basic authentication headers into a Jersey SecurityContext
classes.add(AuthFilter.class);
// Enforce roles annotations defined by JSR-250 (allow access to API methods based on the SecurityContext)
classes.add(RolesAllowedDynamicFeature.class);
}
return classes;
}
/**
* Like getClasses, this method declares web resources, providers, and features to the JAX-RS implementation.
* However, these are single instances that will be reused for all requests (they are singleton-scoped).
* See https://jersey.java.net/apidocs/latest/jersey/javax/ws/rs/core/Application.html#getSingletons()
* Leave <Object> out of method signature to avoid confusing the Guava type inference.
*/
@Override
public Set<Object> getSingletons() {
return Sets.newHashSet (
// Show exception messages in responses
new OTPExceptionMapper(),
// Enable Jackson JSON response serialization
new JacksonJsonProvider(),
// Enable Jackson XML response serialization
new JacksonXMLProvider(),
// Serialize POJOs (unannotated) JSON using Jackson
new JSONObjectMapperProvider(),
// Allow injecting the OTP server object into Jersey resource classes
server.makeBinder()
);
}
/**
* Enabling tracing allows us to see how web resource names were matched from the client, in headers.
* Disable auto-discovery of features because it's extremely obnoxious to debug and interacts
* in confusing ways with manually registered features.
*/
// @Override
public Map<String, Object> getProperties() {
Map<String, Object> props = Maps.newHashMap();
props.put(ServerProperties.TRACING, Boolean.TRUE);
props.put(CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE, Boolean.TRUE);
return props;
}
}