Skip to content
New issue

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

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

Already on GitHub? # to your account

Support for JAX-RS 2.2 Java SE Bootstrapping API #3839

Merged
merged 12 commits into from
Apr 12, 2019
4 changes: 4 additions & 0 deletions containers/grizzly2-http/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
<groupId>org.glassfish.grizzly</groupId>
<artifactId>grizzly-http-server</artifactId>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2018 Markus KARG. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.grizzly2.httpserver;

import static java.lang.Boolean.TRUE;
import static javax.ws.rs.JAXRS.Configuration.SSLClientAuthentication.MANDATORY;
import static javax.ws.rs.JAXRS.Configuration.SSLClientAuthentication.OPTIONAL;

import java.io.IOException;
import java.net.URI;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;

import javax.net.ssl.SSLContext;
import javax.ws.rs.JAXRS;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.UriBuilder;

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.server.spi.Server;

/**
* Jersey {@code Server} implementation based on Grizzly {@link HttpServer}.
*
* @author Markus KARG (markus@headcrashing.eu)
* @since 2.30
*/
public final class GrizzlyHttpServer implements Server {

private final GrizzlyHttpContainer container;

private final HttpServer httpServer;

GrizzlyHttpServer(final Application application, final JAXRS.Configuration configuration) {
final String protocol = configuration.protocol();
final String host = configuration.host();
final int port = configuration.port();
final String rootPath = configuration.rootPath();
final SSLContext sslContext = configuration.sslContext();
final JAXRS.Configuration.SSLClientAuthentication sslClientAuthentication = configuration
.sslClientAuthentication();
final boolean autoStart = Optional.ofNullable((Boolean) configuration.property(ServerProperties.AUTO_START))
.orElse(TRUE);
final URI uri = UriBuilder.newInstance().scheme(protocol.toLowerCase()).host(host).port(port).path(rootPath)
.build();

this.container = new GrizzlyHttpContainer(application);
this.httpServer = GrizzlyHttpServerFactory.createHttpServer(uri, this.container, "HTTPS".equals(protocol),
new SSLEngineConfigurator(sslContext, false, sslClientAuthentication == OPTIONAL,
sslClientAuthentication == MANDATORY),
autoStart);
}

@Override
public final GrizzlyHttpContainer container() {
return this.container;
}

@Override
public final int port() {
return this.httpServer.getListener("grizzly").getPort();
}

@Override
public final CompletableFuture<Void> start() {
return CompletableFuture.runAsync(() -> {
try {
this.httpServer.start();
} catch (final IOException e) {
throw new CompletionException(e);
}
});
}

@Override
public final CompletableFuture<Void> stop() {
return CompletableFuture.runAsync(this.httpServer::shutdownNow);
}

@Override
public final <T> T unwrap(final Class<T> nativeClass) {
return nativeClass.cast(this.httpServer);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2018 Markus KARG. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.grizzly2.httpserver;

import javax.ws.rs.JAXRS;
import javax.ws.rs.core.Application;

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.server.spi.Server;
import org.glassfish.jersey.server.spi.ServerProvider;

/**
* Server provider for servers based on Grizzly {@link HttpServer}.
*
* @author Markus KARG (markus@headcrashing.eu)
* @since 2.30
*/
public final class GrizzlyHttpServerProvider implements ServerProvider {

@Override
public final <T extends Server> T createServer(final Class<T> type, final Application application,
final JAXRS.Configuration configuration) {
return GrizzlyHttpServer.class == type || Server.class == type
? type.cast(new GrizzlyHttpServer(application, configuration))
: null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerProvider
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright (c) 2018 Markus KARG. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.grizzly2.httpserver;

import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertThat;

import java.security.AccessController;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.net.ssl.SSLContext;
import javax.ws.rs.GET;
import javax.ws.rs.JAXRS;
import javax.ws.rs.JAXRS.Configuration.SSLClientAuthentication;
import javax.ws.rs.Path;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.UriBuilder;

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.internal.util.PropertiesHelper;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.server.spi.Container;
import org.glassfish.jersey.server.spi.Server;
import org.glassfish.jersey.server.spi.ServerProvider;
import org.junit.Test;

/**
* Unit tests for {@link GrizzlyHttpServerProvider}.
*
* @author Markus KARG (markus@headcrashing.eu)
* @since 2.30
*/
public final class GrizzlyHttpServerProviderTest {

@Test(timeout = 15000)
public final void shouldProvideServer() throws InterruptedException, ExecutionException {
// given
final ServerProvider serverProvider = new GrizzlyHttpServerProvider();
final Resource resource = new Resource();
final Application application = new Application() {
@Override
public final Set<Object> getSingletons() {
return Collections.singleton(resource);
}
};
final JAXRS.Configuration configuration = name -> {
switch (name) {
case JAXRS.Configuration.PROTOCOL:
return "HTTP";
case JAXRS.Configuration.HOST:
return "localhost";
case JAXRS.Configuration.PORT:
return getPort();
case JAXRS.Configuration.ROOT_PATH:
return "/";
case JAXRS.Configuration.SSL_CLIENT_AUTHENTICATION:
return SSLClientAuthentication.NONE;
case JAXRS.Configuration.SSL_CONTEXT:
try {
return SSLContext.getDefault();
} catch (final NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
case ServerProperties.AUTO_START:
return FALSE;
default:
return null;
}
};

// when
final Server server = serverProvider.createServer(Server.class, application, configuration);
final Object nativeHandle = server.unwrap(Object.class);
final CompletionStage<?> start = server.start();
final Object startResult = start.toCompletableFuture().get();
final Container container = server.container();
final int port = server.port();
final String entity = ClientBuilder.newClient()
.target(UriBuilder.newInstance().scheme("http").host("localhost").port(port).build()).request()
.get(String.class);
final CompletionStage<?> stop = server.stop();
final Object stopResult = stop.toCompletableFuture().get();

// then
assertThat(server, is(instanceOf(GrizzlyHttpServer.class)));
assertThat(nativeHandle, is(instanceOf(HttpServer.class)));
assertThat(startResult, is(nullValue()));
assertThat(container, is(instanceOf(GrizzlyHttpContainer.class)));
assertThat(port, is(greaterThan(0)));
assertThat(entity, is(resource.toString()));
assertThat(stopResult, is(nullValue()));
}

@Path("/")
protected static final class Resource {
@GET
@Override
public final String toString() {
return super.toString();
}
}

private static final Logger LOGGER = Logger.getLogger(GrizzlyHttpServerProviderTest.class.getName());

private static final int DEFAULT_PORT = 0;

private static final int getPort() {
final String value = AccessController
.doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port"));
if (value != null) {
try {
final int i = Integer.parseInt(value);
if (i < 0) {
throw new NumberFormatException("Value is negative.");
}
return i;
} catch (final NumberFormatException e) {
LOGGER.log(Level.CONFIG,
"Value of 'jersey.config.test.container.port'"
+ " property is not a valid non-negative integer [" + value + "]."
+ " Reverting to default [" + DEFAULT_PORT + "].",
e);
}
}

return DEFAULT_PORT;
}

@Test(timeout = 15000)
public final void shouldScanFreePort() throws InterruptedException, ExecutionException {
// given
final ServerProvider serverProvider = new GrizzlyHttpServerProvider();
final Application application = new Application();
final JAXRS.Configuration configuration = name -> {
switch (name) {
case JAXRS.Configuration.PROTOCOL:
return "HTTP";
case JAXRS.Configuration.HOST:
return "localhost";
case JAXRS.Configuration.PORT:
return JAXRS.Configuration.FREE_PORT;
case JAXRS.Configuration.ROOT_PATH:
return "/";
case JAXRS.Configuration.SSL_CLIENT_AUTHENTICATION:
return SSLClientAuthentication.NONE;
case JAXRS.Configuration.SSL_CONTEXT:
try {
return SSLContext.getDefault();
} catch (final NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
case ServerProperties.AUTO_START:
return TRUE;
default:
return null;
}
};

// when
final Server server = serverProvider.createServer(Server.class, application, configuration);

// then
assertThat(server.port(), is(greaterThan(0)));
}

}
4 changes: 4 additions & 0 deletions containers/jdk-http/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
<artifactId>guava</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Loading