Skip to content

Instantly share code, notes, and snippets.

@mkarg
Last active May 29, 2018 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mkarg/a38a68f6025f1ef6ddb4916022bd150d to your computer and use it in GitHub Desktop.
Save mkarg/a38a68f6025f1ef6ddb4916022bd150d to your computer and use it in GitHub Desktop.
Minimal Example for JAX-RS Bootstrapping on Java SE 8
/*
* 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 jaxrs.examples.bootstrap;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import javax.ws.rs.JAXRS;
import javax.ws.rs.JAXRS.Instance;
/**
* Minimum Java SE Bootstrap Example
*
* @author Markus KARG (markus@headcrashing.eu)
*/
public class MinimumSeBootstrapExample {
public void main(final String[] args) throws IOException, InterruptedException, ExecutionException {
final CompletableFuture<Instance> boot = JAXRS.start(new HelloWorld(), JAXRS.Configuration.builder().build()).toCompletableFuture();
final Instance instance = boot.get();
System.out.println("Press any key to shutdown.");
System.in.read();
instance.stop().toCompletableFuture().join();
}
}
@mkarg
Copy link
Author

mkarg commented May 23, 2018

@RogerGL This example is the bare minimum how to fire up a JAX-RS 2.2 compatible container using bare Java SE 8. A lot can be configured using the new JAX-RS bootstrap API we are currently developing. It would be great if we could have several examples so that all possibilities -including parallel loading of two containers etc. - would be covered.

Copy link

ghost commented May 23, 2018

Ok, that ives java.lang.ClassNotFoundException: Provider for javax.ws.rs.ext.RuntimeDelegate cannot be found, although I added

    <dependency>
          <groupId>org.glassfish.jersey.core</groupId>
          <artifactId>jersey-common</artifactId>
          <version>2.27</version>
      </dependency>

@mkarg
Copy link
Author

mkarg commented May 23, 2018

@RogerGL You will not be able to execute that example certainly, as there is no implementation existing for that API yet. This API is under such heavy development that no vendor will invest time into that without having seen the examples you are about to provide.

Besides that, the correct way to include Jersey is:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.glassfish.jersey</groupId>
                <artifactId>jersey-bom</artifactId>
                <version>${jersey.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-grizzly2-http</artifactId>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.inject</groupId>
            <artifactId>jersey-hk2</artifactId>
        </dependency>

         <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-binding</artifactId>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-sse</artifactId>
        </dependency>
    </dependencies>

(You might not need the JSON and SSE stuff when using latest Jersey as it should be enabled by default meanwhile; the above snippet is one year old.)

Also, you need the HelloWorld class:

package jaxrs.examples.bootstrap;

import java.util.Collections;
import java.util.Set;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;

@ApplicationPath("helloworld")
@Path("hello")
public class HelloWorld extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        return Collections.singleton(HelloWorld.class);
    }

    @GET
    public String sayHello() {
        return "Hello, World!";
    }
}

Copy link

ghost commented May 23, 2018

Ok, so I'm writing examples for an API without being able to verify if that the usage makes sense ? I suppose that the verify is than done by some code review step ?

@spericas
Copy link

spericas commented May 24, 2018

@mkarg Looks good to me. After further review, perhaps we should drop the async behavior of stop(), it seems much less useful that that of start(). Here is the example I had in mind:

JAXRS.start(...).thenAccept(instance -> {
    System.out.println("Instance has started " + instance);
    Runtime.getRuntime().addShutdownHook(new Thread(() ->  instance.stop()));      // sync stop
    try { Thread.currentThread().join(); } catch (Exception e) {};    // blocks for signal
}).toCompletableFuture().get();

@mkarg
Copy link
Author

mkarg commented May 25, 2018

@RogerGL The examples initially are helpful for the vendors when implementing the API. Certainly later they serve for users. But without the examples it is hard to further discuss among the vendors which direction we want to go, where problems occur, etc. To make your life a little easier I simply patched Jersey so you can actually run your examples now. Stay tuned, I will push a branch here soon.

@mkarg
Copy link
Author

mkarg commented May 25, 2018

@RogerGL See https://github.com/mkarg/jersey/tree/wip-poc-javase-bootstrap. Using this branch you can run your examples. At least the following works for me using that Jersey patch:

/*
 * 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 jaxrs.examples.bootstrap;

import java.io.IOException;
import java.net.URI;
import java.util.concurrent.CompletionException;
import java.util.function.BiFunction;
import java.util.function.Consumer;

import javax.ws.rs.JAXRS;

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;

/**
 * Java SE Bootstrap Example.
 *
 * @author Markus KARG (markus@headcrashing.eu)
 */
public final class JavaSeBootstrapExample {

    /**
     * Starts the example.
     *
     * @param args
     *            Command line arguments
     */
    public static final void main(final String[] args) {
        JAXRS.start(new HelloWorld(), JAXRS.Configuration.builder()
                .property(ServerProperties.HTTP_SERVER_PROVIDER,
                        (BiFunction<URI, ResourceConfig, HttpServer>) GrizzlyHttpServerFactory::createHttpServer)
                .property(ServerProperties.HTTP_SERVER_ANNIHILATOR, (Consumer<HttpServer>) HttpServer::shutdownNow)
                .build()).thenCompose(instance -> {
                    try {
                        System.out.println("Press any key to shutdown.");
                        System.in.read();
                        return instance.stop();
                    } catch (final IOException e) {
                        throw new CompletionException(e);
                    }
                }).toCompletableFuture().join();
    }

}

@mkarg
Copy link
Author

mkarg commented May 26, 2018

@spericas To keep the threads together, I just answered on your proposal on stop not being asynchronous anymore in the original PR.

@mkarg
Copy link
Author

mkarg commented May 26, 2018

@mkarg
Copy link
Author

mkarg commented May 26, 2018

@spericas Regarding the example you had in mind I do not understand why you catch exceptions?

Copy link

ghost commented May 26, 2018

@mkark probably I misconfigured something but now I get:

Error:(54, 43) java: cannot find symbol
  symbol:   variable HTTP_SERVER_ANNIHILATOR
  location: class org.glassfish.jersey.server.ServerProperties
Error:(52, 43) java: cannot find symbol
  symbol:   variable HTTP_SERVER_PROVIDER
  location: class org.glassfish.jersey.server.ServerProperties

Copy link

ghost commented May 26, 2018

Ok, got the wrong commit. Now I'm only having some problems with the IDE to recognize the new Jersey version.

Copy link

ghost commented May 26, 2018

Ok, almost there. Now I get (running on Mac OS X):

Exception in thread "main" java.util.concurrent.CompletionException: javax.ws.rs.ProcessingException: Failed to start Grizzly HTTP server: Permission denied
	at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273)
	at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280)
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1592)
	at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1582)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: javax.ws.rs.ProcessingException: Failed to start Grizzly HTTP server: Permission denied
	at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory.createHttpServer(GrizzlyHttpServerFactory.java:270)
	at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory.createHttpServer(GrizzlyHttpServerFactory.java:93)
	at org.glassfish.jersey.server.internal.RuntimeDelegateImpl.lambda$0(RuntimeDelegateImpl.java:107)
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
	... 5 more
Caused by: java.net.SocketException: Permission denied
	at sun.nio.ch.Net.bind0(Native Method)
	at sun.nio.ch.Net.bind(Net.java:433)
	at sun.nio.ch.Net.bind(Net.java:425)
	at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)
	at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
	at org.glassfish.grizzly.nio.transport.TCPNIOBindingHandler.bindToChannelAndAddress(TCPNIOBindingHandler.java:131)
	at org.glassfish.grizzly.nio.transport.TCPNIOBindingHandler.bind(TCPNIOBindingHandler.java:88)
	at org.glassfish.grizzly.nio.transport.TCPNIOTransport.bind(TCPNIOTransport.java:239)
	at org.glassfish.grizzly.nio.transport.TCPNIOTransport.bind(TCPNIOTransport.java:219)
	at org.glassfish.grizzly.nio.transport.TCPNIOTransport.bind(TCPNIOTransport.java:210)
	at org.glassfish.grizzly.http.server.NetworkListener.start(NetworkListener.java:735)
	at org.glassfish.grizzly.http.server.HttpServer.start(HttpServer.java:280)
	at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory.createHttpServer(GrizzlyHttpServerFactory.java:267)
	... 8 more

Copy link

ghost commented May 27, 2018

Ok this one is working now. BTW: The @ApplicationPath("helloworld") seems to be ignored.

 JAXRS.start(new HelloWorld(), JAXRS.Configuration.builder()
                .property(ServerProperties.HTTP_SERVER_PROVIDER,
                        (BiFunction<URI, ResourceConfig, HttpServer>) GrizzlyHttpServerFactory::createHttpServer)
                .property(ServerProperties.HTTP_SERVER_ANNIHILATOR, (Consumer<HttpServer>) HttpServer::shutdownNow)
                .property(JAXRS.Configuration.PORT, 8080)
                .build())
                .thenCompose((JAXRS.Instance instance) -> {
                    try {
                        System.out.println("Press any key to shutdown.");
                        System.in.read();
                        return instance.stop();
                    } catch (final IOException e) {
                        throw new CompletionException(e);
                    }
                }).toCompletableFuture().join();

Copy link

ghost commented May 27, 2018

I would like to have more separate building blocks in the examples. What do you think ?

 /**
     * Starts the example.
     *
     * @param args Command line arguments
     */
    public static void main(final String[] args) {

        CompletionStage<JAXRS.Instance> startedInstance = JAXRS.start(new HelloWorld(), createConfiguration());

        startedInstance.thenCompose((JAXRS.Instance instance) -> {
            try {
                System.out.println("Press any key to shutdown.");
                System.in.read();
                return instance.stop();
            } catch (final IOException e) {
                throw new CompletionException(e);
            }
        }).toCompletableFuture().join();
    }

    private static JAXRS.Configuration createConfiguration() {
        return JAXRS.Configuration.builder()
                .property(ServerProperties.HTTP_SERVER_PROVIDER,
                        (BiFunction<URI, ResourceConfig, HttpServer>) GrizzlyHttpServerFactory::createHttpServer)
                .property(ServerProperties.HTTP_SERVER_ANNIHILATOR, (Consumer<HttpServer>) HttpServer::shutdownNow)
                .property(JAXRS.Configuration.PORT, 8080)
                .build();
    }

@mkarg
Copy link
Author

mkarg commented May 28, 2018

On some operating system port 80 can only be used by root; use explicitly 8080 there.

"startedIntance" is semantically wrong; it is the process that currently is starting, not necessarily an instance that was already started.

There is no need to give explicitly "(JAXRS.Instance instance)"; Java already knows the type.

Separate blocks is fine for me, but not necessarily better than inline code, as this is just an example that shall outline the steps to perform.

Copy link

ghost commented May 28, 2018

"startedIntance" is semantically wrong;

Than how is the flow ?

-> stage completes (instance started) -> callback called with (JAXRS.Instance instance))

And the first completed stage joins the main thread ? Or what is the purpose of 'toCompletableFuture().join()' ?

There is no need to give explicitly "(JAXRS.Instance instance)"; Java already knows the type.

I know that, but I like examples to be more explicit. But I can remove it...

Separate blocks is fine for me, but not necessarily better than inline code, as this is just an example that shall outline the steps to perform.

Just judging from the discussion regarding 'startedIntance' it may at least help to understand the concepts more clearly ;-)

Copy link

ghost commented May 28, 2018

BTW: I think this one is wrong;

default Builder port(String port) {
return property(PORT, port);
}

Shouldn't that be an Integer? At least I get : java.lang.String cannot be cast to java.lang.Integer when using the builder.

@spericas
Copy link

@mkarg I'm OK with keep stop async, gives it a nice symmetry even if not as useful IMO.

@spericas
Copy link

@mkarg Newer versions of example are much nicer. I still find System.in.read() unrealistic; process should stop on a signal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment