diff --git a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/BrokerAdminApiApplication.java b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/BrokerAdminApiApplication.java index c09302e0b4a9..8dedc8c72b3b 100644 --- a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/BrokerAdminApiApplication.java +++ b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/BrokerAdminApiApplication.java @@ -19,10 +19,8 @@ package org.apache.pinot.broker.broker; import com.google.common.util.concurrent.ThreadFactoryBuilder; -import io.swagger.jaxrs.config.BeanConfig; +import io.swagger.jaxrs.listing.SwaggerSerializers; import java.io.IOException; -import java.net.URL; -import java.net.URLClassLoader; import java.time.Instant; import java.util.List; import java.util.concurrent.Executor; @@ -35,7 +33,8 @@ import org.apache.pinot.broker.requesthandler.BrokerRequestHandler; import org.apache.pinot.broker.routing.BrokerRoutingManager; import org.apache.pinot.common.metrics.BrokerMetrics; -import org.apache.pinot.common.utils.PinotStaticHttpHandler; +import org.apache.pinot.common.swagger.SwaggerApiListingResource; +import org.apache.pinot.common.swagger.SwaggerSetupUtils; import org.apache.pinot.common.utils.log.DummyLogFileServer; import org.apache.pinot.common.utils.log.LocalLogFileServer; import org.apache.pinot.common.utils.log.LogFileServer; @@ -47,8 +46,6 @@ import org.apache.pinot.spi.env.PinotConfiguration; import org.apache.pinot.spi.utils.CommonConstants; import org.apache.pinot.spi.utils.PinotReflectionUtils; -import org.glassfish.grizzly.http.server.CLStaticHttpHandler; -import org.glassfish.grizzly.http.server.HttpHandler; import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.hk2.utilities.binding.AbstractBinder; import org.glassfish.jersey.jackson.JacksonFeature; @@ -122,8 +119,8 @@ protected void configure() { register(buildBrokerManagedAsyncExecutorProvider(brokerConf, brokerMetrics)); } register(JacksonFeature.class); - registerClasses(io.swagger.jaxrs.listing.ApiListingResource.class); - registerClasses(io.swagger.jaxrs.listing.SwaggerSerializers.class); + register(SwaggerApiListingResource.class); + register(SwaggerSerializers.class); register(AuthenticationFilter.class); } @@ -137,38 +134,13 @@ public void start(List listenerConfigs) { } if (_swaggerBrokerEnabled) { - PinotReflectionUtils.runWithLock(this::setupSwagger); + PinotReflectionUtils.runWithLock(() -> + SwaggerSetupUtils.setupSwagger("Broker", _brokerResourcePackages, _useHttps, "/", _httpServer)); } else { LOGGER.info("Hiding Swagger UI for Broker, by {}", CommonConstants.Broker.CONFIG_OF_SWAGGER_BROKER_ENABLED); } } - private void setupSwagger() { - BeanConfig beanConfig = new BeanConfig(); - beanConfig.setTitle("Pinot Broker API"); - beanConfig.setDescription("APIs for accessing Pinot broker information"); - beanConfig.setContact("https://github.com/apache/pinot"); - beanConfig.setVersion("1.0"); - beanConfig.setExpandSuperTypes(false); - if (_useHttps) { - beanConfig.setSchemes(new String[]{CommonConstants.HTTPS_PROTOCOL}); - } else { - beanConfig.setSchemes(new String[]{CommonConstants.HTTP_PROTOCOL, CommonConstants.HTTPS_PROTOCOL}); - } - beanConfig.setBasePath("/"); - beanConfig.setResourcePackage(_brokerResourcePackages); - beanConfig.setScan(true); - - HttpHandler httpHandler = new CLStaticHttpHandler(BrokerAdminApiApplication.class.getClassLoader(), "/api/"); - // map both /api and /help to swagger docs. /api because it looks nice. /help for backward compatibility - _httpServer.getServerConfiguration().addHttpHandler(httpHandler, "/api/", "/help/"); - - URL swaggerDistLocation = - BrokerAdminApiApplication.class.getClassLoader().getResource(CommonConstants.CONFIG_OF_SWAGGER_RESOURCES_PATH); - CLStaticHttpHandler swaggerDist = new PinotStaticHttpHandler(new URLClassLoader(new URL[]{swaggerDistLocation})); - _httpServer.getServerConfiguration().addHttpHandler(swaggerDist, "/swaggerui-dist/"); - } - private BrokerManagedAsyncExecutorProvider buildBrokerManagedAsyncExecutorProvider(PinotConfiguration brokerConf, BrokerMetrics brokerMetrics) { int corePoolSize = diff --git a/pinot-common/src/main/java/org/apache/pinot/common/swagger/SwaggerApiListingResource.java b/pinot-common/src/main/java/org/apache/pinot/common/swagger/SwaggerApiListingResource.java new file mode 100644 index 000000000000..8bb995cefe47 --- /dev/null +++ b/pinot-common/src/main/java/org/apache/pinot/common/swagger/SwaggerApiListingResource.java @@ -0,0 +1,62 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.pinot.common.swagger; + +import io.swagger.annotations.ApiOperation; +import io.swagger.jaxrs.listing.BaseApiListingResource; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import org.apache.commons.lang3.StringUtils; + + +/* + This class is required to avoid the jersey2 warning messages regarding servlet config constructor missing while + injecting the config. Please refer to Pinot issue 13047 & 5306 for more context. + In this implementation, we added the ServletConfig as the class level member instead of injecting it. +*/ +@Path("/swagger.{type:json|yaml}") +public class SwaggerApiListingResource extends BaseApiListingResource { + @Context + ServletContext _context; + + @Context + ServletConfig _servletConfig; + + @GET + @Produces({MediaType.APPLICATION_JSON, "application/yaml"}) + @ApiOperation(value = "The swagger definition in either JSON or YAML", hidden = true) + public Response getListing(@Context Application app, @Context HttpHeaders headers, @Context UriInfo uriInfo, + @PathParam("type") String type) { + if (StringUtils.isNotBlank(type) && type.trim().equalsIgnoreCase("yaml")) { + return getListingYamlResponse(app, _context, _servletConfig, headers, uriInfo); + } else { + return getListingJsonResponse(app, _context, _servletConfig, headers, uriInfo); + } + } +} diff --git a/pinot-common/src/main/java/org/apache/pinot/common/swagger/SwaggerSetupUtils.java b/pinot-common/src/main/java/org/apache/pinot/common/swagger/SwaggerSetupUtils.java new file mode 100644 index 000000000000..e2e74b3ad4ee --- /dev/null +++ b/pinot-common/src/main/java/org/apache/pinot/common/swagger/SwaggerSetupUtils.java @@ -0,0 +1,68 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.pinot.common.swagger; + +import io.swagger.jaxrs.config.BeanConfig; +import java.net.InetAddress; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.UnknownHostException; +import org.apache.pinot.common.utils.PinotStaticHttpHandler; +import org.apache.pinot.spi.utils.CommonConstants; +import org.glassfish.grizzly.http.server.CLStaticHttpHandler; +import org.glassfish.grizzly.http.server.HttpServer; + + +public class SwaggerSetupUtils { + private SwaggerSetupUtils() { + } + + public static void setupSwagger(String componentType, String resourcePackage, boolean useHttps, String basePath, + HttpServer httpServer) { + BeanConfig beanConfig = new BeanConfig(); + beanConfig.setTitle(String.format("Pinot %s API", componentType)); + beanConfig.setDescription(String.format("APIs for accessing Pinot %s information", componentType)); + beanConfig.setContact("https://github.com/apache/pinot"); + beanConfig.setVersion("1.0"); + beanConfig.setExpandSuperTypes(false); + if (useHttps) { + beanConfig.setSchemes(new String[]{CommonConstants.HTTPS_PROTOCOL}); + } else { + beanConfig.setSchemes(new String[]{CommonConstants.HTTP_PROTOCOL, CommonConstants.HTTPS_PROTOCOL}); + } + beanConfig.setBasePath(basePath); + beanConfig.setResourcePackage(resourcePackage); + beanConfig.setScan(true); + + try { + beanConfig.setHost(InetAddress.getLocalHost().getHostName()); + } catch (UnknownHostException e) { + throw new RuntimeException("Cannot get localhost name"); + } + + ClassLoader classLoader = SwaggerSetupUtils.class.getClassLoader(); + CLStaticHttpHandler staticHttpHandler = new CLStaticHttpHandler(classLoader, "/api/"); + // map both /api and /help to swagger docs. /api because it looks nice. /help for backward compatibility + httpServer.getServerConfiguration().addHttpHandler(staticHttpHandler, "/api/", "/help/"); + + URL swaggerDistLocation = classLoader.getResource(CommonConstants.CONFIG_OF_SWAGGER_RESOURCES_PATH); + CLStaticHttpHandler swaggerDist = new PinotStaticHttpHandler(new URLClassLoader(new URL[]{swaggerDistLocation})); + httpServer.getServerConfiguration().addHttpHandler(swaggerDist, "/swaggerui-dist/"); + } +} diff --git a/pinot-controller/src/main/java/org/apache/pinot/controller/api/ControllerAdminApiApplication.java b/pinot-controller/src/main/java/org/apache/pinot/controller/api/ControllerAdminApiApplication.java index c431e55f52fa..b04e6a75d97d 100644 --- a/pinot-controller/src/main/java/org/apache/pinot/controller/api/ControllerAdminApiApplication.java +++ b/pinot-controller/src/main/java/org/apache/pinot/controller/api/ControllerAdminApiApplication.java @@ -18,16 +18,15 @@ */ package org.apache.pinot.controller.api; -import io.swagger.jaxrs.config.BeanConfig; +import io.swagger.jaxrs.listing.SwaggerSerializers; import java.io.IOException; -import java.net.URL; -import java.net.URLClassLoader; import java.util.List; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; -import org.apache.pinot.common.utils.PinotStaticHttpHandler; +import org.apache.pinot.common.swagger.SwaggerApiListingResource; +import org.apache.pinot.common.swagger.SwaggerSetupUtils; import org.apache.pinot.controller.ControllerConf; import org.apache.pinot.controller.api.access.AuthenticationFilter; import org.apache.pinot.core.api.ServiceAutoDiscoveryFeature; @@ -66,8 +65,8 @@ public ControllerAdminApiApplication(ControllerConf conf) { } register(JacksonFeature.class); register(MultiPartFeature.class); - registerClasses(io.swagger.jaxrs.listing.ApiListingResource.class); - registerClasses(io.swagger.jaxrs.listing.SwaggerSerializers.class); + register(SwaggerApiListingResource.class); + register(SwaggerSerializers.class); register(new CorsFilter()); register(AuthenticationFilter.class); // property("jersey.config.server.tracing.type", "ALL"); @@ -86,9 +85,9 @@ public void start(List listenerConfigs) { } catch (IOException e) { throw new RuntimeException("Failed to start http server", e); } - PinotReflectionUtils.runWithLock(this::setupSwagger); - ClassLoader classLoader = ControllerAdminApiApplication.class.getClassLoader(); + PinotReflectionUtils.runWithLock(() -> + SwaggerSetupUtils.setupSwagger("Controller", _controllerResourcePackages, _useHttps, "/", _httpServer)); // This is ugly from typical patterns to setup static resources but all our APIs are // at path "/". So, configuring static handler for path "/" does not work well. @@ -103,33 +102,6 @@ public void start(List listenerConfigs) { _httpServer.getServerConfiguration().addHttpHandler(new CLStaticHttpHandler(classLoader, "/webapp/js/"), "/js/"); } - private void setupSwagger() { - BeanConfig beanConfig = new BeanConfig(); - beanConfig.setTitle("Pinot Controller API"); - beanConfig.setDescription("APIs for accessing Pinot Controller information"); - beanConfig.setContact("https://github.com/apache/pinot"); - beanConfig.setVersion("1.0"); - beanConfig.setExpandSuperTypes(false); - if (_useHttps) { - beanConfig.setSchemes(new String[]{CommonConstants.HTTPS_PROTOCOL}); - } else { - beanConfig.setSchemes(new String[]{CommonConstants.HTTP_PROTOCOL, CommonConstants.HTTPS_PROTOCOL}); - } - beanConfig.setBasePath("/"); - beanConfig.setResourcePackage(_controllerResourcePackages); - beanConfig.setScan(true); - - ClassLoader loader = this.getClass().getClassLoader(); - CLStaticHttpHandler apiStaticHttpHandler = new CLStaticHttpHandler(loader, "/api/"); - // map both /api and /help to swagger docs. /api because it looks nice. /help for backward compatibility - _httpServer.getServerConfiguration().addHttpHandler(apiStaticHttpHandler, "/api/"); - _httpServer.getServerConfiguration().addHttpHandler(apiStaticHttpHandler, "/help/"); - - URL swaggerDistLocation = loader.getResource(CommonConstants.CONFIG_OF_SWAGGER_RESOURCES_PATH); - CLStaticHttpHandler swaggerDist = new PinotStaticHttpHandler(new URLClassLoader(new URL[]{swaggerDistLocation})); - _httpServer.getServerConfiguration().addHttpHandler(swaggerDist, "/swaggerui-dist/"); - } - public void stop() { if (!_httpServer.isStarted()) { return; diff --git a/pinot-integration-test-base/src/test/java/org/apache/pinot/integration/tests/ClusterTest.java b/pinot-integration-test-base/src/test/java/org/apache/pinot/integration/tests/ClusterTest.java index 49edf86264ed..fc76171ec260 100644 --- a/pinot-integration-test-base/src/test/java/org/apache/pinot/integration/tests/ClusterTest.java +++ b/pinot-integration-test-base/src/test/java/org/apache/pinot/integration/tests/ClusterTest.java @@ -103,6 +103,7 @@ public abstract class ClusterTest extends ControllerTest { protected BaseMinionStarter _minionStarter; private String _brokerBaseApiUrl; + private String _minionBaseApiUrl; private boolean _useMultiStageQueryEngine = false; @@ -129,6 +130,10 @@ protected String getBrokerBaseApiUrl() { return _brokerBaseApiUrl; } + public String getMinionBaseApiUrl() { + return _minionBaseApiUrl; + } + protected boolean useMultiStageQueryEngine() { return _useMultiStageQueryEngine; } @@ -321,9 +326,10 @@ protected void startMinion() PinotConfiguration minionConf = getDefaultMinionConfiguration(); minionConf.setProperty(Helix.CONFIG_OF_CLUSTER_NAME, getHelixClusterName()); minionConf.setProperty(Helix.CONFIG_OF_ZOOKEEPR_SERVER, getZkUrl()); - minionConf.setProperty(CommonConstants.Helix.KEY_OF_MINION_PORT, - NetUtils.findOpenPort(CommonConstants.Minion.DEFAULT_HELIX_PORT)); + int minionPort = NetUtils.findOpenPort(CommonConstants.Minion.DEFAULT_HELIX_PORT); + minionConf.setProperty(CommonConstants.Helix.KEY_OF_MINION_PORT, minionPort); minionConf.setProperty(CommonConstants.CONFIG_OF_TIMEZONE, "UTC"); + _minionBaseApiUrl = "http://localhost:" + minionPort; _minionStarter = new MinionStarter(); _minionStarter.init(minionConf); _minionStarter.start(); diff --git a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/AdminConsoleIntegrationTest.java b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/AdminConsoleIntegrationTest.java index 589fac4d7dae..3859313ac3ee 100644 --- a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/AdminConsoleIntegrationTest.java +++ b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/AdminConsoleIntegrationTest.java @@ -18,10 +18,13 @@ */ package org.apache.pinot.integration.tests; +import java.nio.charset.StandardCharsets; +import java.util.Objects; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.pinot.broker.broker.BrokerAdminApiApplication; import org.apache.pinot.controller.api.ControllerAdminApiApplication; +import org.apache.pinot.minion.MinionAdminApiApplication; import org.apache.pinot.util.TestUtils; import org.testng.Assert; import org.testng.annotations.AfterClass; @@ -44,6 +47,7 @@ public void setUp() startController(); startBroker(); startServer(); + startMinion(); } @AfterClass @@ -52,21 +56,25 @@ public void tearDown() stopServer(); stopBroker(); stopController(); + stopMinion(); stopZk(); FileUtils.deleteQuietly(_tempDir); } /** - * Tests resposnes to /api and /help. + * Tests responses to /api and /help. */ @Test public void testApiHelp() throws Exception { + String apiIndexHtmlPage = "api/index.html"; + // test controller String response = sendGetRequest(getControllerBaseApiUrl() + "/help"); String expected = - IOUtils.toString(ControllerAdminApiApplication.class.getClassLoader().getResourceAsStream("api/index.html"), - "UTF-8"); + IOUtils.toString(Objects.requireNonNull( + ControllerAdminApiApplication.class.getClassLoader().getResourceAsStream(apiIndexHtmlPage)), + StandardCharsets.UTF_8); Assert.assertEquals(response, expected); // help and api map to the same content response = sendGetRequest(getControllerBaseApiUrl() + "/api"); @@ -74,22 +82,34 @@ public void testApiHelp() // test broker response = sendGetRequest(getBrokerBaseApiUrl() + "/help"); - expected = IOUtils.toString(BrokerAdminApiApplication.class.getClassLoader().getResourceAsStream("api/index.html"), - "UTF-8"); + expected = IOUtils.toString( + Objects.requireNonNull(BrokerAdminApiApplication.class.getClassLoader().getResourceAsStream(apiIndexHtmlPage)), + StandardCharsets.UTF_8); Assert.assertEquals(response, expected); // help and api map to the same content response = sendGetRequest(getBrokerBaseApiUrl() + "/api"); Assert.assertEquals(response, expected); - String serverBaseApiUrl = "http://localhost:" + getServerAdminApiPort(); + // test server + String serverBaseApiUrl = "http://localhost:" + getServerAdminApiPort(); response = sendGetRequest(serverBaseApiUrl + "/help"); - expected = IOUtils.toString(BrokerAdminApiApplication.class.getClassLoader().getResourceAsStream("api/index.html"), - "UTF-8"); + expected = IOUtils.toString( + Objects.requireNonNull(BrokerAdminApiApplication.class.getClassLoader().getResourceAsStream(apiIndexHtmlPage)), + StandardCharsets.UTF_8); Assert.assertEquals(response, expected); - // help and api map to the same content response = sendGetRequest(serverBaseApiUrl + "/api"); Assert.assertEquals(response, expected); + + // test minion + response = sendGetRequest(getMinionBaseApiUrl() + "/help"); + expected = IOUtils.toString( + Objects.requireNonNull(MinionAdminApiApplication.class.getClassLoader().getResourceAsStream(apiIndexHtmlPage)), + StandardCharsets.UTF_8); + Assert.assertEquals(response, expected); + // help and api map to the same content + response = sendGetRequest(getMinionBaseApiUrl() + "/api"); + Assert.assertEquals(response, expected); } @Test(dataProvider = "endpointBase") @@ -112,7 +132,8 @@ public Object[][] endpointBase() { return new Object[][] { new Object[] { "controller", getControllerBaseApiUrl() }, new Object[] { "broker", getBrokerBaseApiUrl() }, - new Object[] { "server", "http://localhost:" + getServerAdminApiPort() } + new Object[] { "server", "http://localhost:" + getServerAdminApiPort() }, + new Object[] { "minion", getMinionBaseApiUrl()}, }; } } diff --git a/pinot-minion/src/main/java/org/apache/pinot/minion/MinionAdminApiApplication.java b/pinot-minion/src/main/java/org/apache/pinot/minion/MinionAdminApiApplication.java index 45ca4272ed05..9e8ce9571335 100644 --- a/pinot-minion/src/main/java/org/apache/pinot/minion/MinionAdminApiApplication.java +++ b/pinot-minion/src/main/java/org/apache/pinot/minion/MinionAdminApiApplication.java @@ -18,13 +18,12 @@ */ package org.apache.pinot.minion; -import io.swagger.jaxrs.config.BeanConfig; +import io.swagger.jaxrs.listing.SwaggerSerializers; import java.io.IOException; -import java.net.URL; -import java.net.URLClassLoader; import java.time.Instant; import java.util.List; -import org.apache.pinot.common.utils.PinotStaticHttpHandler; +import org.apache.pinot.common.swagger.SwaggerApiListingResource; +import org.apache.pinot.common.swagger.SwaggerSetupUtils; import org.apache.pinot.common.utils.log.DummyLogFileServer; import org.apache.pinot.common.utils.log.LocalLogFileServer; import org.apache.pinot.common.utils.log.LogFileServer; @@ -33,8 +32,6 @@ import org.apache.pinot.spi.env.PinotConfiguration; import org.apache.pinot.spi.utils.CommonConstants; import org.apache.pinot.spi.utils.PinotReflectionUtils; -import org.glassfish.grizzly.http.server.CLStaticHttpHandler; -import org.glassfish.grizzly.http.server.HttpHandler; import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.server.ResourceConfig; @@ -77,8 +74,8 @@ protected void configure() { } }); - registerClasses(io.swagger.jaxrs.listing.ApiListingResource.class); - registerClasses(io.swagger.jaxrs.listing.SwaggerSerializers.class); + register(SwaggerApiListingResource.class); + register(SwaggerSerializers.class); } public void start(List listenerConfigs) { @@ -89,33 +86,8 @@ public void start(List listenerConfigs) { } catch (IOException e) { throw new RuntimeException("Failed to start http server", e); } - PinotReflectionUtils.runWithLock(this::setupSwagger); - } - - private void setupSwagger() { - BeanConfig beanConfig = new BeanConfig(); - beanConfig.setTitle("Pinot Minion API"); - beanConfig.setDescription("APIs for accessing Pinot Minion information"); - beanConfig.setContact("https://github.com/apache/pinot"); - beanConfig.setVersion("1.0"); - beanConfig.setExpandSuperTypes(false); - if (_useHttps) { - beanConfig.setSchemes(new String[]{CommonConstants.HTTPS_PROTOCOL}); - } else { - beanConfig.setSchemes(new String[]{CommonConstants.HTTP_PROTOCOL, CommonConstants.HTTPS_PROTOCOL}); - } - beanConfig.setBasePath("/"); - beanConfig.setResourcePackage(RESOURCE_PACKAGE); - beanConfig.setScan(true); - - HttpHandler httpHandler = new CLStaticHttpHandler(MinionAdminApiApplication.class.getClassLoader(), "/api/"); - // map both /api and /help to swagger docs. /api because it looks nice. /help for backward compatibility - _httpServer.getServerConfiguration().addHttpHandler(httpHandler, "/api/", "/help/"); - - URL swaggerDistLocation = - MinionAdminApiApplication.class.getClassLoader().getResource(CommonConstants.CONFIG_OF_SWAGGER_RESOURCES_PATH); - CLStaticHttpHandler swaggerDist = new PinotStaticHttpHandler(new URLClassLoader(new URL[]{swaggerDistLocation})); - _httpServer.getServerConfiguration().addHttpHandler(swaggerDist, "/swaggerui-dist/"); + PinotReflectionUtils.runWithLock(() -> + SwaggerSetupUtils.setupSwagger("Minion", RESOURCE_PACKAGE, _useHttps, "/", _httpServer)); } public void stop() { diff --git a/pinot-server/src/main/java/org/apache/pinot/server/api/AdminApiApplication.java b/pinot-server/src/main/java/org/apache/pinot/server/api/AdminApiApplication.java index a1b324c68ad5..520b95cf064e 100644 --- a/pinot-server/src/main/java/org/apache/pinot/server/api/AdminApiApplication.java +++ b/pinot-server/src/main/java/org/apache/pinot/server/api/AdminApiApplication.java @@ -18,12 +18,8 @@ */ package org.apache.pinot.server.api; -import io.swagger.jaxrs.config.BeanConfig; +import io.swagger.jaxrs.listing.SwaggerSerializers; import java.io.IOException; -import java.net.InetAddress; -import java.net.URL; -import java.net.URLClassLoader; -import java.net.UnknownHostException; import java.time.Instant; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -32,7 +28,8 @@ import javax.ws.rs.container.ContainerResponseFilter; import org.apache.helix.HelixManager; import org.apache.pinot.common.metrics.ServerMetrics; -import org.apache.pinot.common.utils.PinotStaticHttpHandler; +import org.apache.pinot.common.swagger.SwaggerApiListingResource; +import org.apache.pinot.common.swagger.SwaggerSetupUtils; import org.apache.pinot.common.utils.log.DummyLogFileServer; import org.apache.pinot.common.utils.log.LocalLogFileServer; import org.apache.pinot.common.utils.log.LogFileServer; @@ -43,7 +40,6 @@ import org.apache.pinot.spi.env.PinotConfiguration; import org.apache.pinot.spi.utils.CommonConstants; import org.apache.pinot.spi.utils.PinotReflectionUtils; -import org.glassfish.grizzly.http.server.CLStaticHttpHandler; import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.hk2.utilities.binding.AbstractBinder; import org.glassfish.jersey.jackson.JacksonFeature; @@ -64,6 +60,7 @@ public class AdminApiApplication extends ResourceConfig { private HttpServer _httpServer; private final String _adminApiResourcePackages; + public AdminApiApplication(ServerInstance instance, AccessControlFactory accessControlFactory, PinotConfiguration serverConf) { _serverInstance = instance; @@ -95,8 +92,8 @@ protected void configure() { register(JacksonFeature.class); - registerClasses(io.swagger.jaxrs.listing.ApiListingResource.class); - registerClasses(io.swagger.jaxrs.listing.SwaggerSerializers.class); + register(SwaggerApiListingResource.class); + register(SwaggerSerializers.class); register(new ContainerResponseFilter() { @Override public void filter(ContainerRequestContext containerRequestContext, @@ -123,44 +120,14 @@ public boolean start(List listenerConfigs) { if (pinotConfiguration.getProperty(CommonConstants.Server.CONFIG_OF_SWAGGER_SERVER_ENABLED, CommonConstants.Server.DEFAULT_SWAGGER_SERVER_ENABLED)) { LOGGER.info("Starting swagger for the Pinot server."); - PinotReflectionUtils.runWithLock(() -> setupSwagger(pinotConfiguration)); + boolean useHttps = Boolean.parseBoolean( + pinotConfiguration.getProperty(CommonConstants.Server.CONFIG_OF_SWAGGER_USE_HTTPS)); + PinotReflectionUtils.runWithLock(() -> + SwaggerSetupUtils.setupSwagger("Server", _adminApiResourcePackages, useHttps, "/", _httpServer)); } return true; } - private void setupSwagger(PinotConfiguration pinotConfiguration) { - BeanConfig beanConfig = new BeanConfig(); - beanConfig.setTitle("Pinot Server API"); - beanConfig.setDescription("APIs for accessing Pinot server information"); - beanConfig.setContact("https://github.com/apache/pinot"); - beanConfig.setVersion("1.0"); - beanConfig.setExpandSuperTypes(false); - if (Boolean.parseBoolean(pinotConfiguration.getProperty(CommonConstants.Server.CONFIG_OF_SWAGGER_USE_HTTPS))) { - beanConfig.setSchemes(new String[]{CommonConstants.HTTPS_PROTOCOL}); - } else { - beanConfig.setSchemes(new String[]{CommonConstants.HTTP_PROTOCOL, CommonConstants.HTTPS_PROTOCOL}); - } - beanConfig.setBasePath("/"); - beanConfig.setResourcePackage(_adminApiResourcePackages); - beanConfig.setScan(true); - try { - beanConfig.setHost(InetAddress.getLocalHost().getHostName()); - } catch (UnknownHostException e) { - throw new RuntimeException("Cannot get localhost name"); - } - - CLStaticHttpHandler staticHttpHandler = - new CLStaticHttpHandler(AdminApiApplication.class.getClassLoader(), "/api/"); - // map both /api and /help to swagger docs. /api because it looks nice. /help for backward compatibility - _httpServer.getServerConfiguration().addHttpHandler(staticHttpHandler, "/api/"); - _httpServer.getServerConfiguration().addHttpHandler(staticHttpHandler, "/help/"); - - URL swaggerDistLocation = - AdminApiApplication.class.getClassLoader().getResource(CommonConstants.CONFIG_OF_SWAGGER_RESOURCES_PATH); - CLStaticHttpHandler swaggerDist = new PinotStaticHttpHandler(new URLClassLoader(new URL[]{swaggerDistLocation})); - _httpServer.getServerConfiguration().addHttpHandler(swaggerDist, "/swaggerui-dist/"); - } - /** * Starts shutting down the HTTP server, which rejects all requests except for the liveness check. */ diff --git a/pinot-tools/src/main/java/org/apache/pinot/tools/service/PinotServiceManagerAdminApiApplication.java b/pinot-tools/src/main/java/org/apache/pinot/tools/service/PinotServiceManagerAdminApiApplication.java index 9176817e289a..625b7384e2f7 100644 --- a/pinot-tools/src/main/java/org/apache/pinot/tools/service/PinotServiceManagerAdminApiApplication.java +++ b/pinot-tools/src/main/java/org/apache/pinot/tools/service/PinotServiceManagerAdminApiApplication.java @@ -19,15 +19,11 @@ package org.apache.pinot.tools.service; import com.google.common.base.Preconditions; -import io.swagger.jaxrs.config.BeanConfig; +import io.swagger.jaxrs.listing.SwaggerSerializers; import java.net.URI; -import java.net.URL; -import java.net.URLClassLoader; -import org.apache.pinot.common.utils.PinotStaticHttpHandler; -import org.apache.pinot.spi.utils.CommonConstants; +import org.apache.pinot.common.swagger.SwaggerApiListingResource; +import org.apache.pinot.common.swagger.SwaggerSetupUtils; import org.apache.pinot.spi.utils.PinotReflectionUtils; -import org.glassfish.grizzly.http.server.CLStaticHttpHandler; -import org.glassfish.grizzly.http.server.HttpHandler; import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.hk2.utilities.binding.AbstractBinder; import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; @@ -50,37 +46,16 @@ protected void configure() { } }); register(JacksonFeature.class); - registerClasses(io.swagger.jaxrs.listing.ApiListingResource.class); - registerClasses(io.swagger.jaxrs.listing.SwaggerSerializers.class); + register(SwaggerApiListingResource.class); + register(SwaggerSerializers.class); } public void start(int httpPort) { Preconditions.checkArgument(httpPort > 0); _baseUri = URI.create("http://0.0.0.0:" + httpPort + "/"); _httpServer = GrizzlyHttpServerFactory.createHttpServer(_baseUri, this); - PinotReflectionUtils.runWithLock(this::setupSwagger); - } - - private void setupSwagger() { - BeanConfig beanConfig = new BeanConfig(); - beanConfig.setTitle("Pinot Starter API"); - beanConfig.setDescription("APIs for accessing Pinot Starter information"); - beanConfig.setContact("https://github.com/apache/pinot"); - beanConfig.setVersion("1.0"); - beanConfig.setSchemes(new String[]{CommonConstants.HTTP_PROTOCOL, CommonConstants.HTTPS_PROTOCOL}); - beanConfig.setBasePath(_baseUri.getPath()); - beanConfig.setResourcePackage(RESOURCE_PACKAGE); - beanConfig.setScan(true); - beanConfig.setExpandSuperTypes(false); - HttpHandler httpHandler = - new CLStaticHttpHandler(PinotServiceManagerAdminApiApplication.class.getClassLoader(), "/api/"); - // map both /api and /help to swagger docs. /api because it looks nice. /help for backward compatibility - _httpServer.getServerConfiguration().addHttpHandler(httpHandler, "/api/", "/help/"); - - URL swaggerDistLocation = PinotServiceManagerAdminApiApplication.class.getClassLoader() - .getResource(CommonConstants.CONFIG_OF_SWAGGER_RESOURCES_PATH); - CLStaticHttpHandler swaggerDist = new PinotStaticHttpHandler(new URLClassLoader(new URL[]{swaggerDistLocation})); - _httpServer.getServerConfiguration().addHttpHandler(swaggerDist, "/swaggerui-dist/"); + PinotReflectionUtils.runWithLock(() -> + SwaggerSetupUtils.setupSwagger("Starter", RESOURCE_PACKAGE, false, _baseUri.getPath(), _httpServer)); } public void stop() {