org.acmsl.queryj.debugging.netty.NettyServerDebuggingService.java Source code

Java tutorial

Introduction

Here is the source code for org.acmsl.queryj.debugging.netty.NettyServerDebuggingService.java

Source

/*
                    QueryJ Template Debugging
    
Copyright (C) 2002-today  Jose San Leandro Armendariz
                          chous@acm-sl.org
    
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or any later version.
    
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.
    
You should have received a copy of the GNU General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    
Thanks to ACM S.L. for distributing this library under the GPL license.
Contact info: jose.sanleandro@acm-sl.com
    
 ******************************************************************************
 *
 * Filename: NettyServerDebuggingService.java
 *
 * Author: Jose San Leandro Armendariz
 *
 * Description: Netty-based TCP/IP server which drives the template debugging
 *              process.
 *
 * Date: 2014/06/26
 * Time: 18:43
 *
 */
package org.acmsl.queryj.debugging.netty;

/*
 * Importing QueryJ Core classes.
 */
import org.acmsl.queryj.QueryJCommand;
import org.acmsl.queryj.api.TemplateContext;
import org.acmsl.queryj.api.exceptions.DevelopmentModeException;
import org.acmsl.queryj.debugging.TemplateDebuggingCommand;
import org.acmsl.queryj.debugging.TemplateDebuggingListener;
import org.acmsl.queryj.debugging.TemplateDebuggingService;

/*
 * Importing Netty classes.
 */
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/*
 * Importing Apache Commons Logging classes.
 */
import org.acmsl.queryj.tools.handlers.QueryJCommandHandler;
import org.apache.commons.logging.LogFactory;

/*
 * Importing StringTemplate classes.
 */
import org.stringtemplate.v4.ST;

/*
 * Importing JetBrains annotations.
 */
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/*
 * Importing checkthread.org annotations.
 */
import org.checkthread.annotations.ThreadSafe;

/*
 * Importing JDK classes.
 */
import java.io.IOException;

/**
 * Netty-based TCP/IP server which drives the
 * template debugging process.
 * @param <C> the template context.
 * @author <a href="mailto:queryj@acm-sl.org">Jose San Leandro</a>
 * @since 3.0
 * Created: 2014/06/26 18:43
 */
@ThreadSafe
public class NettyServerDebuggingService<C extends TemplateContext>
        implements TemplateDebuggingService<C>, TemplateDebuggingListener {
    /**
     * The system property for debugging templates.
     */
    public static final String QUERYJ_TEMPLATE_DEBUG_PORT = "queryj.template.debug.port";

    /**
     * The server bootstrap.
     */
    private ServerBootstrap m__ServerBootstrap;

    /**
     * The event loop group.
     */
    private EventLoopGroup m__EventLoopGroup;

    /**
     * The channel future.
     */
    private ChannelFuture m__ChannelFuture;

    /**
     * The last command received.
     */
    private TemplateDebuggingCommand m__Command;

    /**
     * Specifies the {@link ServerBootstrap}.
     * @param bootstrap the bootstrap.
     */
    protected final void immutableSetServerBootstrap(@NotNull final ServerBootstrap bootstrap) {
        this.m__ServerBootstrap = bootstrap;
    }

    /**
     * Specifies the {@link ServerBootstrap}.
     * @param bootstrap the bootstrap.
     */
    @SuppressWarnings("unused")
    protected void setServerBootstrap(@NotNull final ServerBootstrap bootstrap) {
        immutableSetServerBootstrap(bootstrap);
    }

    /**
     * Retrieves the {@link ServerBootstrap}.
     * @return such bootstrap.
     */
    @SuppressWarnings("unused")
    @Nullable
    protected ServerBootstrap getServerBootstrap() {
        return this.m__ServerBootstrap;
    }

    /**
     * Specifies the event loop group.
     * @param group such {@link EventLoopGroup}.
     */
    protected final void immutableSetEventLoopGroup(@NotNull final EventLoopGroup group) {
        this.m__EventLoopGroup = group;
    }

    /**
     * Specifies the event loop group.
     * @param group such {@link EventLoopGroup}.
     */
    protected void setEventLoopGroup(@NotNull final EventLoopGroup group) {
        immutableSetEventLoopGroup(group);
    }

    /**
     * Retrieves the event loop group.
     * @return such {@link EventLoopGroup}.
     */
    protected EventLoopGroup getEventLoopGroup() {
        return m__EventLoopGroup;
    }

    /**
     * Specifies the channel future.
     * @param future such {@link ChannelFuture}.
     */
    protected final void immutableSetChannelFuture(@NotNull final ChannelFuture future) {
        this.m__ChannelFuture = future;
    }

    /**
     * Specifies the channel future.
     * @param future such {@link ChannelFuture}.
     */
    @SuppressWarnings("unused")
    protected void setChannelFuture(@NotNull final ChannelFuture future) {
        immutableSetChannelFuture(future);
    }

    /**
     * Retrieves the channel future.
     * @return such {@link ChannelFuture}.
     */
    @SuppressWarnings("unused")
    protected ChannelFuture getChannelFuture() {
        return this.m__ChannelFuture;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @NotNull
    public TemplateDebuggingCommand debugTemplate(@NotNull final ST template, @NotNull final C context,
            @NotNull final String output) throws DevelopmentModeException {
        try {
            final ChannelFuture future = launchServer();

            future.sync();
        } catch (@NotNull final InterruptedException | IOException interruption) {
            throw new DevelopmentModeException(template.groupThatCreatedThisInstance);
        }

        return this.m__Command;
    }

    /**
     * {@inheritDoc}
     */
    @NotNull
    @Override
    public TemplateDebuggingCommand debug(@NotNull final QueryJCommandHandler<QueryJCommand> handler,
            @NotNull final QueryJCommand command) {
        return TemplateDebuggingCommand.NEXT;
    }

    /**
     * Launches the server.
     * @return the {@link ChannelFuture}.
     * @throws InterruptedException if the server gets interrupted.
     * @throws IOException if the socket cannot be bound.
     */
    public ChannelFuture launchServer() throws InterruptedException, IOException {
        final int t_iPort;

        @Nullable
        final String t_strPort = System.getProperty(QUERYJ_TEMPLATE_DEBUG_PORT);

        if (t_strPort != null) {
            t_iPort = Integer.valueOf(t_strPort);
        } else {
            t_iPort = 0;
        }

        return launchServer(t_iPort);
    }

    /**
     * Launches the server.
     * @param port the port.
     * @return the {@link ChannelFuture}.
     * @throws InterruptedException if the server gets interrupted.
     * @throws IOException if the socket cannot be bound.
     */
    public ChannelFuture launchServer(final int port) throws InterruptedException, IOException {

        return launchServer(port, new NettyServerChannelHandler(this));
    }

    /**
     * Launches the server.
     * @param port the port.
     * @param handler the {@link ChannelHandlerAdapter handler} to handle incoming connections.
     * @return the {@link ChannelFuture}.
     * @throws InterruptedException if the server gets interrupted.
     * @throws IOException if the socket cannot be bound.
     */
    @NotNull
    protected ChannelFuture launchServer(final int port, @NotNull final ChannelHandlerAdapter handler)
            throws InterruptedException, IOException {
        @NotNull
        final ChannelFuture result;

        @Nullable
        ChannelFuture aux = null;

        @NotNull
        final EventLoopGroup bossGroup = new NioEventLoopGroup();
        setEventLoopGroup(bossGroup);
        @NotNull
        final EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            @NotNull
            final ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                        /**
                         * {@inheritDoc}
                         */
                        @Override
                        public void initChannel(@NotNull final SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(handler);
                        }
                    }).option(ChannelOption.SO_BACKLOG, 128) // (5)
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
                    .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

            // Bind and start to accept incoming connections.
            aux = b.bind(port).sync(); // (7)

            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            //            result.channel().closeFuture().sync();
        } catch (@NotNull final Throwable throwable) {
            LogFactory.getLog(NettyServerDebuggingService.class).fatal("Cannot run the template debugging server",
                    throwable);
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }

        if (aux == null) {
            throw new RuntimeException("Cannot run server");
        } else {
            result = aux;
        }

        return result;
    }

    /**
     * Stops the server.
     * @throws InterruptedException if the server cannot be stopped.
     */
    @SuppressWarnings("unused")
    public void stopServer() throws InterruptedException {
        stopServer(getEventLoopGroup());
    }

    /**
     * Stops the server.
     * @param group the {@link EventLoopGroup group}.
     * @throws InterruptedException if the server cannot be stopped.
     */
    protected void stopServer(@NotNull final EventLoopGroup group) throws InterruptedException {
        group.shutdownGracefully().sync();
    }

    /**
     * Gets notified whenever a "reload" operation has been
     * requested.
     */
    @Override
    public void reloadRequested() {
        this.m__Command = TemplateDebuggingCommand.RELOAD;
    }

    /**
     * {@inheritDoc}
     */
    @NotNull
    @Override
    public String toString() {
        return "{ \"lastCommand\": \"" + m__Command + '"' + ", \"channelFuture\": \"" + m__ChannelFuture.hashCode()
                + '"' + ", \"serverBootstrap\": \"" + m__ServerBootstrap.hashCode() + '"'
                + ", \"eventLoopGroup\": \"" + m__EventLoopGroup.hashCode() + '"' + ", \"class\": \""
                + NettyServerDebuggingService.class.getSimpleName() + '"' + ", \"package\": \""
                + NettyServerDebuggingService.class.getPackage() + "\" }";
    }
}