Skip to content

Commit

Permalink
Add support for shutting down the app (closes #196).
Browse files Browse the repository at this point in the history
  • Loading branch information
ldaley committed Dec 11, 2013
1 parent 723befb commit 42b2b8e
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 11 deletions.
Expand Up @@ -30,6 +30,7 @@
import ratpack.server.internal.RatpackChannelInitializer;
import ratpack.server.internal.ServiceBackedServer;
import ratpack.util.Factory;
import ratpack.util.Transformer;

import java.io.File;

Expand All @@ -50,14 +51,19 @@ private RatpackServerBuilder() {
* @return A new, not yet started, Ratpack server.
*/
public static RatpackServer build(LaunchConfig launchConfig) {
ChannelInitializer<SocketChannel> channelInitializer = buildChannelInitializer(launchConfig);
Transformer<Stopper, ChannelInitializer<SocketChannel>> channelInitializer = buildChannelInitializer(launchConfig);
NettyRatpackService service = new NettyRatpackService(launchConfig, channelInitializer);
return new ServiceBackedServer(service, launchConfig);
}


private static ChannelInitializer<SocketChannel> buildChannelInitializer(LaunchConfig launchConfig) {
return new RatpackChannelInitializer(launchConfig, createHandler(launchConfig));
private static Transformer<Stopper, ChannelInitializer<SocketChannel>> buildChannelInitializer(final LaunchConfig launchConfig) {
return new Transformer<Stopper, ChannelInitializer<SocketChannel>>() {
@Override
public ChannelInitializer<SocketChannel> transform(Stopper stopper) {
return new RatpackChannelInitializer(launchConfig, createHandler(launchConfig), stopper);
}
};
}

private static Handler createHandler(final LaunchConfig launchConfig) {
Expand Down
26 changes: 26 additions & 0 deletions ratpack-core/src/main/java/ratpack/server/Stopper.java
@@ -0,0 +1,26 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 ratpack.server;

import ratpack.api.NonBlocking;

public interface Stopper {

@NonBlocking
void stop();

}
Expand Up @@ -53,8 +53,7 @@
import ratpack.registry.RegistryBuilder;
import ratpack.render.CharSequenceRenderer;
import ratpack.render.internal.DefaultCharSequenceRenderer;
import ratpack.server.BindAddress;
import ratpack.server.PublicAddress;
import ratpack.server.*;
import ratpack.util.Action;

import java.io.IOException;
Expand All @@ -72,13 +71,14 @@ public class NettyHandlerAdapter extends SimpleChannelInboundHandler<FullHttpReq

private Registry registry;

public NettyHandlerAdapter(Handler handler, LaunchConfig launchConfig, ListeningExecutorService blockingExecutorService) {
public NettyHandlerAdapter(Stopper stopper, Handler handler, LaunchConfig launchConfig, ListeningExecutorService blockingExecutorService) {
this.blockingExecutorService = blockingExecutorService;
this.handlers = new Handler[]{new ErrorCatchingHandler(handler)};
this.return404 = new ClientErrorForwardingHandler(NOT_FOUND.code());

this.registry = RegistryBuilder.builder()
// If you update this list, update the class level javadoc on Context.
.add(Stopper.class, stopper)
.add(FileSystemBinding.class, new DefaultFileSystemBinding(launchConfig.getBaseDir()))
.add(MimeTypes.class, new ActivationBackedMimeTypes())
.add(PublicAddress.class, new DefaultPublicAddress(launchConfig.getPublicAddress(), launchConfig.getSSLContext() == null ? "http" : "https"))
Expand Down
Expand Up @@ -27,31 +27,49 @@
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.DefaultThreadFactory;
import ratpack.launch.LaunchConfig;
import ratpack.server.Stopper;
import ratpack.util.Transformer;

import java.net.InetSocketAddress;
import java.util.logging.Level;
import java.util.logging.Logger;

import static ratpack.util.ExceptionUtils.uncheck;

public class NettyRatpackService extends AbstractIdleService implements RatpackService {

private final Logger logger = Logger.getLogger(getClass().getName());

private final LaunchConfig launchConfig;
private final Transformer<Stopper, ChannelInitializer<SocketChannel>> channelInitializerTransformer;

private InetSocketAddress boundAddress;
private final ChannelInitializer<SocketChannel> channelInitializer;
private Channel channel;
private EventLoopGroup group;

public NettyRatpackService(LaunchConfig launchConfig, ChannelInitializer<SocketChannel> channelInitializer) {
public NettyRatpackService(LaunchConfig launchConfig, Transformer<Stopper, ChannelInitializer<SocketChannel>> channelInitializerTransformer) {
this.launchConfig = launchConfig;
this.channelInitializer = channelInitializer;
this.channelInitializerTransformer = channelInitializerTransformer;
}

@Override
protected void startUp() throws Exception {
Stopper stopper = new Stopper() {
@Override
public void stop() {
try {
NettyRatpackService.this.stop();
} catch (Exception e) {
throw uncheck(e);
}
}
};

ServerBootstrap bootstrap = new ServerBootstrap();
group = new NioEventLoopGroup(launchConfig.getMainThreads(), new DefaultThreadFactory("ratpack-group", Thread.MAX_PRIORITY));

ChannelInitializer<SocketChannel> channelInitializer = channelInitializerTransformer.transform(stopper);

bootstrap
.group(group)
.channel(NioServerSocketChannel.class)
Expand Down
Expand Up @@ -28,19 +28,23 @@
import io.netty.handler.stream.ChunkedWriteHandler;
import ratpack.handling.Handler;
import ratpack.launch.LaunchConfig;
import ratpack.server.*;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;

public class RatpackChannelInitializer extends ChannelInitializer<SocketChannel> {

private final Stopper stopper;

private NettyHandlerAdapter nettyHandlerAdapter;
private SSLContext sslContext;
private int maxContentLength;

public RatpackChannelInitializer(LaunchConfig launchConfig, Handler handler) {
public RatpackChannelInitializer(LaunchConfig launchConfig, Handler handler, Stopper stopper) {
ListeningExecutorService backgroundExecutorService = MoreExecutors.listeningDecorator(launchConfig.getBackgroundExecutorService());
this.nettyHandlerAdapter = new NettyHandlerAdapter(handler, launchConfig, backgroundExecutorService);
this.stopper = stopper;
this.nettyHandlerAdapter = new NettyHandlerAdapter(stopper, handler, launchConfig, backgroundExecutorService);
this.sslContext = launchConfig.getSSLContext();
this.maxContentLength = launchConfig.getMaxContentLength();
}
Expand Down
50 changes: 50 additions & 0 deletions ratpack-core/src/test/groovy/ratpack/server/ShutdownSpec.groovy
@@ -0,0 +1,50 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 ratpack.server

import ratpack.test.internal.RatpackGroovyDslSpec
import spock.util.concurrent.PollingConditions

class ShutdownSpec extends RatpackGroovyDslSpec {

private PollingConditions conditions = new PollingConditions()

def "can invoke shutdown"() {
when:
app {
handlers {
get {
get(Stopper).stop()
response.send("ok")
}
}
}

then:
text == "ok"

and:
conditions.eventually { !server.running }

when:
text == "ok"

then:
thrown ConnectException
}

}

0 comments on commit 42b2b8e

Please sign in to comment.