/** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.aurora.scheduler.http.api; import javax.inject.Singleton; import javax.ws.rs.core.MediaType; import com.google.common.collect.ImmutableMap; import com.google.inject.Provides; import com.google.inject.servlet.ServletModule; import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; import org.apache.aurora.common.args.Arg; import org.apache.aurora.common.args.CmdLine; import org.apache.aurora.gen.AuroraAdmin; import org.apache.aurora.scheduler.http.CorsFilter; import org.apache.aurora.scheduler.http.JettyServerModule; import org.apache.aurora.scheduler.http.LeaderRedirectFilter; import org.apache.aurora.scheduler.http.api.TContentAwareServlet.ContentFactoryPair; import org.apache.aurora.scheduler.http.api.TContentAwareServlet.InputConfig; import org.apache.aurora.scheduler.http.api.TContentAwareServlet.OutputConfig; import org.apache.aurora.scheduler.thrift.aop.AnnotatedAuroraAdmin; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TJSONProtocol; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.util.resource.Resource; import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; public class ApiModule extends ServletModule { public static final String API_PATH = "/api"; private static final MediaType GENERIC_THRIFT = new MediaType("application", "x-thrift"); private static final MediaType THRIFT_JSON = new MediaType("application", "vnd.apache.thrift.json"); private static final MediaType THRIFT_BINARY = new MediaType("application", "vnd.apache.thrift.binary"); /** * Set the {@code Access-Control-Allow-Origin} header for API requests. See * http://www.w3.org/TR/cors/ */ @CmdLine(name = "enable_cors_for", help = "List of domains for which CORS support should be enabled.") private static final Arg<String> ENABLE_CORS_FOR = Arg.create(null); private static final String API_CLIENT_ROOT = Resource .newClassPathResource("org/apache/aurora/scheduler/gen/client") .toString(); @Override protected void configureServlets() { if (ENABLE_CORS_FOR.get() != null) { filter(API_PATH).through(new CorsFilter(ENABLE_CORS_FOR.get())); } serve(API_PATH).with(TContentAwareServlet.class); filter(ApiBeta.PATH, ApiBeta.PATH + "/*").through(LeaderRedirectFilter.class); filter(ApiBeta.PATH, ApiBeta.PATH + "/*") .through(GuiceContainer.class, JettyServerModule.GUICE_CONTAINER_PARAMS); bind(ApiBeta.class); serve("/apiclient", "/apiclient/*") .with(new DefaultServlet(), ImmutableMap.<String, String>builder() .put("resourceBase", API_CLIENT_ROOT) .put("pathInfoOnly", "true") .put("dirAllowed", "false") .build()); } @Provides @Singleton TContentAwareServlet provideApiThriftServlet(AnnotatedAuroraAdmin schedulerThriftInterface) { /* * For backwards compatibility the servlet is configured to assume `application/x-thrift` and * `application/json` have TJSON bodies. * * Requests that have the registered MIME type for apache thrift are mapped to their respective * protocols. See * http://www.iana.org/assignments/media-types/application/vnd.apache.thrift.binary and * http://www.iana.org/assignments/media-types/application/vnd.apache.thrift.json for details. * * Responses have the registered MIME type so the client can decode appropriately. * * The Accept header is used to determine the response type. By default JSON is sent for any * value except for the binary thrift header. */ ContentFactoryPair jsonFactory = new ContentFactoryPair( new TJSONProtocol.Factory(), THRIFT_JSON); ContentFactoryPair binFactory = new ContentFactoryPair( new TBinaryProtocol.Factory(), THRIFT_BINARY); // Which factory to use based on the Content-Type header of the request for reading the request. InputConfig inputConfig = new InputConfig(GENERIC_THRIFT, ImmutableMap.of( GENERIC_THRIFT, jsonFactory, THRIFT_JSON, jsonFactory, APPLICATION_JSON_TYPE, jsonFactory, THRIFT_BINARY, binFactory )); // Which factory to use based on the Accept header of the request for the response. OutputConfig outputConfig = new OutputConfig(APPLICATION_JSON_TYPE, ImmutableMap.of( APPLICATION_JSON_TYPE, jsonFactory, GENERIC_THRIFT, jsonFactory, THRIFT_JSON, jsonFactory, THRIFT_BINARY, binFactory )); // A request without a Content-Type (like from curl) should be treated as GENERIC_THRIFT return new TContentAwareServlet( new AuroraAdmin.Processor<>(schedulerThriftInterface), inputConfig, outputConfig); } }