Package org.apache.openejb.server.ejbd

Source Code of org.apache.openejb.server.ejbd.KeepAliveServer$KeepAliveTimer

/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.openejb.server.ejbd;

import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.server.ServerService;
import org.apache.openejb.server.ServiceException;
import org.apache.openejb.server.ServicePool;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;
import org.apache.openejb.util.Exceptions;
import org.apache.openejb.client.KeepAliveStyle;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* @version $Rev$ $Date$
*/
public class KeepAliveServer implements ServerService {

    private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB_SERVER.createChild("keepalive"), KeepAliveServer.class);
    private final ServerService service;
    private final long timeout = (1000 * 3);

    private final AtomicBoolean stop = new AtomicBoolean();
    private final KeepAliveTimer keepAliveTimer;
    private Timer timer;

    public KeepAliveServer() {
        this(new EjbServer());
    }

    public KeepAliveServer(ServerService service) {
        this.service = service;

        keepAliveTimer = new KeepAliveTimer();

        timer = new Timer("KeepAliveTimer", true);
        timer.scheduleAtFixedRate(keepAliveTimer, timeout, timeout / 2);
    }


    public class KeepAliveTimer extends TimerTask {

        // Doesn't need to be a map.  Could be a set if Session.equals/hashCode only referenced the Thread.
        private final Map<Thread, Session> sessions = new ConcurrentHashMap<Thread, Session>();

        private BlockingQueue<Runnable> queue;

        public void run() {
            if (!stop.get()) {
                closeInactiveSessions();
            }
        }

        private void closeInactiveSessions() {
            BlockingQueue<Runnable> queue = getQueue();
            if (queue == null) return;

            int backlog = queue.size();
            if (backlog <= 0) return;

            long now = System.currentTimeMillis();

            for (Session session : sessions.values()) {

                if (session.usage.tryLock()) {
                    try {
                        if (now - session.lastRequest > timeout) {
                            try {
                                backlog--;
                                session.socket.close();
                            } catch (IOException e) {
                                logger.info("Error closing socket.", e);
                            } finally {
                                removeSession(session);
                            }
                        }
                    } finally {
                        session.usage.unlock();
                    }
                }

                if (backlog <= 0) return;
            }
        }

        public void closeSessions() {

            // Close the ones we can
            for (Session session : sessions.values()) {
                if (session.usage.tryLock()) {
                    try {
                        session.socket.close();
                    } catch (IOException e) {
                        logger.info("Error closing socket.", e);
                    } finally {
                        removeSession(session);
                        session.usage.unlock();
                    }
                } else {
                    logger.info("Allowing graceful shutdown of " + session.socket.getInetAddress());
                }
            }
        }

        private BlockingQueue<Runnable> getQueue() {
            if (queue == null) {
                // this can be null if timer fires before service is fully initialized
                ServicePool incoming = SystemInstance.get().getComponent(ServicePool.class);
                if (incoming == null) return null;
                ThreadPoolExecutor threadPool = incoming.getThreadPool();
                queue = threadPool.getQueue();
            }
            return queue;
        }

        public Session addSession(Session session) {
            return sessions.put(session.thread, session);
        }

        public Session removeSession(Session session) {
            return sessions.remove(session.thread);
        }
    }

    private class Session {

        private final Thread thread;
        private final Lock usage = new ReentrantLock();

        // only used inside the Lock
        private long lastRequest;

        // only used inside the Lock
        private final Socket socket;

        public Session(Socket socket) {
            this.socket = socket;
            this.lastRequest = System.currentTimeMillis();
            this.thread = Thread.currentThread();
        }

        public void service(Socket socket) throws ServiceException, IOException {
            keepAliveTimer.addSession(this);

            int i = -1;

            try {
                InputStream in = new BufferedInputStream(socket.getInputStream());
                OutputStream out = new BufferedOutputStream(socket.getOutputStream());

                while (!stop.get()) {
                    try {
                        i = in.read();
                    } catch (SocketException e) {
                        // Socket closed.
                        break;
                    }
                    if (i == -1){
                        // client hung up
                        break;
                    }
                    KeepAliveStyle style = KeepAliveStyle.values()[i];

                    try {
                        usage.lock();

                        switch(style){
                            case PING_PING: {
                                in.read();
                            }
                            break;
                            case PING_PONG: {
                                out.write(style.ordinal());
                                out.flush();
                            }
                        }

                        service.service(new Input(in), new Output(out));
                        out.flush();
                    } finally {
                        this.lastRequest = System.currentTimeMillis();
                        usage.unlock();
                    }
                }
            } catch (ArrayIndexOutOfBoundsException e){
                throw new IOException("Unexpected byte " + i);
            } catch (InterruptedIOException e) {
                Thread.interrupted();
            } finally {
                keepAliveTimer.removeSession(this);
            }
        }
    }


    public void service(Socket socket) throws ServiceException, IOException {
        Session session = new Session(socket);
        session.service(socket);
    }

    public void service(InputStream in, OutputStream out) throws ServiceException, IOException {
    }

    public String getIP() {
        return service.getIP();
    }

    public String getName() {
        return service.getName();
    }

    public int getPort() {
        return service.getPort();
    }

    public void start() throws ServiceException {
        stop.set(false);

//        service.start();
    }


    public void stop() throws ServiceException {
        stop.set(true);
        keepAliveTimer.closeSessions();
//        service.stop();
    }

    public void init(Properties props) throws Exception {
        service.init(props);
    }

    public class Input extends java.io.FilterInputStream {

        public Input(InputStream in) {
            super(in);
        }

        public void close() throws IOException {
        }
    }

    public class Output extends java.io.FilterOutputStream {
        public Output(OutputStream out) {
            super(out);
        }

        public void close() throws IOException {
            flush();
        }
    }

}
TOP

Related Classes of org.apache.openejb.server.ejbd.KeepAliveServer$KeepAliveTimer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.