com.flipkart.phantom.runtime.impl.server.oio.UDSOIOServer.java Source code

Java tutorial

Introduction

Here is the source code for com.flipkart.phantom.runtime.impl.server.oio.UDSOIOServer.java

Source

/*
 * Copyright 2012-2015, 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 com.flipkart.phantom.runtime.impl.server.oio;

import com.flipkart.phantom.event.ServiceProxyEvent;
import com.flipkart.phantom.event.ServiceProxyEventProducer;
import com.flipkart.phantom.runtime.impl.server.AbstractNetworkServer;
import com.flipkart.phantom.runtime.impl.server.concurrent.NamedThreadFactory;
import com.flipkart.phantom.runtime.impl.server.netty.handler.command.CommandInterpreter;
import com.flipkart.phantom.task.impl.TaskHandler;
import com.flipkart.phantom.task.impl.TaskHandlerExecutor;
import com.flipkart.phantom.task.impl.TaskRequestWrapper;
import com.flipkart.phantom.task.impl.TaskResult;
import com.flipkart.phantom.task.spi.repository.ExecutorRepository;
import org.newsclub.net.unix.AFUNIXServerSocket;
import org.newsclub.net.unix.AFUNIXSocketAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.trpr.platform.runtime.impl.config.FileLocator;

import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * <code>UDSOIOServer</code> is a concrete implementation of the {@link AbstractNetworkServer}
 * for Unix Domain Sockets. Note that this server has to be initialized with a UDS socket file rather than a port no.
 *
 * @author Regunath B
 * @version 1.0, 25 Jun 2013
 */
public class UDSOIOServer extends AbstractNetworkServer {

    /** Logger for this class*/
    private static final Logger LOGGER = LoggerFactory.getLogger(UDSOIOServer.class);

    /** The default counts (invalid one) for worker pool count*/
    private static final int INVALID_POOL_SIZE = -1;

    /** The default timeout for client socket inactivity*/
    private int DEFAULT_CLIENT_TIMEOUT_MILLIS = 300;

    /** The default directory name containing junix native libraries*/
    private static final String DEFAULT_JUNIX_NATIVE_DIRECTORY = "uds-lib";

    /** The System property to be set with Junix native lib path*/
    private static final String JUNIX_LIB_SYSTEM_PROPERTY = "org.newsclub.net.unix.library.path";

    /** The worker thread pool sizes*/
    private int workerPoolSize = INVALID_POOL_SIZE;

    /** The client socket inactivity timeout in millis*/
    private int clientSocketTimeoutMillis = DEFAULT_CLIENT_TIMEOUT_MILLIS;

    /** The worker ExecutorService instances*/
    private ExecutorService workerExecutors;

    /** The directory name containing junix native libraries*/
    private String junixNativeLibDirectoryName = DEFAULT_JUNIX_NATIVE_DIRECTORY;

    /** The name of the socket file for this server (UDS) */
    private String socketName;

    /** The directory containing the socket file */
    private String socketDir;

    /** The socket file */
    private File socketFile;

    /** The UNIX domain socket instance */
    public AFUNIXServerSocket socket;

    /** The TaskRepository to lookup TaskHandlerExecutors from */
    private ExecutorRepository repository;

    /** The publisher used to broadcast events to Service Proxy Subscribers */
    private ServiceProxyEventProducer eventProducer;

    /** Event Type for publishing all events which are generated here */
    private final static String COMMAND_HANDLER = "COMMAND_HANDLER";

    /**
     * Interface method implementation. Returns {@link TRANSMISSION_PROTOCOL#UDS} (Unix domain Sockets)
     * @see com.flipkart.phantom.runtime.spi.server.NetworkServer#getTransmissionProtocol()
     */
    public TransmissionProtocol getTransmissionProtocol() {
        return TRANSMISSION_PROTOCOL.UDS;
    }

    /**
     * Interface method implementation. Creates worker thread pool if required and then calls {@link #afterPropertiesSet()} on the super class
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    public void afterPropertiesSet() throws Exception {
        File[] junixDirectories = FileLocator.findDirectories(this.junixNativeLibDirectoryName, null);
        if (junixDirectories == null || junixDirectories.length == 0) {
            throw new RuntimeException("Did not find junixDirectory: " + junixNativeLibDirectoryName);
        }
        LOGGER.info("Found junixDirectory: " + junixDirectories[0].getAbsolutePath());
        System.setProperty(JUNIX_LIB_SYSTEM_PROPERTY, junixDirectories[0].getAbsolutePath());
        //Required properties
        Assert.notNull(this.socketDir, "socketDir is a required property for UDSNetworkServer");
        Assert.notNull(this.socketName, "socketName is a required property for UDSNetworkServer");
        //Create the socket file
        this.socketFile = new File(new File(this.socketDir), this.socketName);

        //Create socket address
        LOGGER.info("Socket file: " + this.socketFile.getAbsolutePath());
        try {
            this.socketAddress = new AFUNIXSocketAddress(this.socketFile);
            this.socket = AFUNIXServerSocket.newInstance();
            this.socket.bind(this.socketAddress);
        } catch (IOException e) {
            throw new RuntimeException("Error creating Socket Address. ", e);
        }
        if (this.getWorkerExecutors() == null) { // no executors have been set for workers
            if (this.getWorkerPoolSize() != UDSOIOServer.INVALID_POOL_SIZE) { // thread pool size has been set. create and use a fixed thread pool
                this.setWorkerExecutors(Executors.newFixedThreadPool(this.getWorkerPoolSize(),
                        new NamedThreadFactory("UDSOIOServer-Worker")));
            } else { // default behavior of creating and using a cached thread pool
                this.setWorkerExecutors(
                        Executors.newCachedThreadPool(new NamedThreadFactory("UDSOIOServer-Worker")));
            }
        }
        super.afterPropertiesSet();
        LOGGER.info("UDS Server startup complete");
    }

    /**
     * Overriden super class method. Returns a readable string for this UDSNetworkServer
     * @see java.lang.Object#toString()
     */
    public String toString() {
        return "UDSOIONetworkServer [socketFile=" + socketFile.getAbsolutePath() + "] ";
    }

    /**
     * Overriden superclass method. Starts up a ServerSocket for listening to client connection requests
     * @see com.flipkart.phantom.runtime.impl.server.AbstractNetworkServer#doStartServer()
     */
    protected void doStartServer() throws RuntimeException {
        new SocketListener();
    }

    /**
     * Overriden superclass method. Shuts down the ServerSocket and stops accepting any new client connection requests
     * @see com.flipkart.phantom.runtime.impl.server.AbstractNetworkServer#doStopServer()
     */
    protected void doStopServer() throws RuntimeException {
        try {
            this.socket.close();
            this.workerExecutors.shutdown();
        } catch (IOException e) {
            throw new RuntimeException("Error shutting down UDS server : " + this.toString(), e);
        }
    }

    @Override
    public String getServerType() {
        return "UDS OIO Server";
    }

    @Override
    public String getServerEndpoint() {
        return this.socketFile.toString();
    }

    /**
     * The Socket listener thread
     */
    class SocketListener extends Thread {
        SocketListener() {
            this.setName("UDSOIO_Listener");
            this.start();
        }

        public void run() {
            while (true) {
                Socket client = null;
                try {
                    client = socket.accept();
                    client.setSoTimeout(getClientSocketTimeoutMillis()); // set this timeout to protect server from clients that become inactive
                    workerExecutors.execute(new CommandProcessor(client));
                } catch (IOException e) {
                    throw new RuntimeException("Error accepting client socket connections : " + e.getMessage(), e);
                }
            }
        }
    }

    /**
     * Helper class that reads and processes Commands from a client Socket. This runs inside a Worker thread.
     */
    class CommandProcessor implements Runnable {
        Socket client;

        CommandProcessor(Socket client) {
            this.client = client;
        }

        public void run() {
            long receiveTime = System.currentTimeMillis();
            TaskHandlerExecutor executor = null;
            CommandInterpreter.ProxyCommand readCommand = null;
            try {
                CommandInterpreter commandInterpreter = new CommandInterpreter();
                readCommand = commandInterpreter.readCommand(client.getInputStream());
                LOGGER.debug("Read Command : " + readCommand);
                String pool = readCommand.getCommandParams().get("pool");

                // Prepare the request Wrapper
                TaskRequestWrapper taskRequestWrapper = new TaskRequestWrapper();
                taskRequestWrapper.setData(readCommand.getCommandData());
                taskRequestWrapper.setParams(readCommand.getCommandParams());

                /*Try to execute command using ThreadPool, if "pool" is found in the command, else the command name */
                if (pool != null) {
                    executor = (TaskHandlerExecutor) repository.getExecutor(readCommand.getCommand(), pool,
                            taskRequestWrapper);
                } else {
                    executor = (TaskHandlerExecutor) repository.getExecutor(readCommand.getCommand(),
                            readCommand.getCommand(), taskRequestWrapper);
                }
                TaskResult result;
                /* execute */
                if (executor.getCallInvocationType() == TaskHandler.SYNC_CALL) {
                    result = executor.execute();
                } else {
                    /* dont wait for the result. send back a response that the call has been dispatched for async execution */
                    executor.queue();
                    result = new TaskResult(true, TaskHandlerExecutor.ASYNC_QUEUED);
                }
                LOGGER.debug("The output is: " + result);

                // write the results to the socket output
                commandInterpreter.writeCommandExecutionResponse(client.getOutputStream(), result);
            } catch (Exception e) {
                throw new RuntimeException("Error in processing command : " + e.getMessage(), e);
            } finally {
                if (eventProducer != null) {
                    // Publishes event both in case of success and failure.
                    final Map<String, String> params = readCommand.getCommandParams();
                    ServiceProxyEvent.Builder eventBuilder;
                    if (executor == null)
                        eventBuilder = new ServiceProxyEvent.Builder(readCommand.getCommand(), COMMAND_HANDLER)
                                .withEventSource(getClass().getName());
                    else
                        eventBuilder = executor.getEventBuilder().withCommandData(executor)
                                .withEventSource(executor.getClass().getName());
                    eventBuilder.withRequestId(params.get("requestID")).withRequestReceiveTime(receiveTime);
                    if (params.containsKey("requestSentTime"))
                        eventBuilder.withRequestSentTime(Long.valueOf(params.get("requestSentTime")));

                    eventProducer.publishEvent(eventBuilder.build());
                } else
                    LOGGER.debug("eventProducer not set, not publishing event");

                if (client != null) {
                    try {
                        client.close();
                    } catch (IOException e) {
                        LOGGER.error("Error closing client socket : " + e.getMessage(), e);
                    }
                }
            }
        }
    }

    /** Start Getter/Setter methods */
    public int getWorkerPoolSize() {
        return this.workerPoolSize;
    }

    public void setWorkerPoolSize(int workerPoolSize) {
        this.workerPoolSize = workerPoolSize;
    }

    public int getClientSocketTimeoutMillis() {
        return this.clientSocketTimeoutMillis;
    }

    public void setClientSocketTimeoutMillis(int clientSocketTimeoutMillis) {
        this.clientSocketTimeoutMillis = clientSocketTimeoutMillis;
    }

    public ExecutorService getWorkerExecutors() {
        return this.workerExecutors;
    }

    public void setWorkerExecutors(ExecutorService workerExecutors) {
        this.workerExecutors = workerExecutors;
    }

    public String getSocketDir() {
        return socketDir;
    }

    public void setSocketDir(String socketDir) {
        this.socketDir = socketDir;
    }

    public String getSocketName() {
        return socketName;
    }

    public void setSocketName(String socketName) {
        this.socketName = socketName;
    }

    public String getJunixNativeLibDirectoryName() {
        return junixNativeLibDirectoryName;
    }

    public void setJunixNativeLibDirectoryName(String junixNativeLibDirectoryName) {
        this.junixNativeLibDirectoryName = junixNativeLibDirectoryName;
    }

    public ExecutorRepository getRepository() {
        return this.repository;
    }

    public void setRepository(ExecutorRepository repository) {
        this.repository = repository;
    }

    public void setEventProducer(ServiceProxyEventProducer eventProducer) {
        this.eventProducer = eventProducer;
    }
    /** End Getter/Setter methods */

}