/*
* @(#) $Id: SocketAcceptor.java 389042 2006-03-27 07:49:41Z trustin $
*
* Copyright 2004 The Apache Software Foundation
*
* 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 org.openamq.bio;
import org.apache.mina.common.IoHandler;
import org.apache.mina.common.IoServiceConfig;
import org.apache.mina.common.support.BaseIoAcceptor;
import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
import org.apache.mina.transport.socket.nio.SocketSessionConfig;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.channels.ByteChannel;
import java.nio.channels.ServerSocketChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*/
public class SocketAcceptor extends BaseIoAcceptor
{
private static final Sequence acceptorSeq = new Sequence();
private final int id = acceptorSeq.nextId();
private final String threadName = "SocketAcceptor-" + id;
private final IoServiceConfig defaultConfig = new SocketAcceptorConfig();
private final Map services = new HashMap();//SocketAddress => SocketBinding
public SocketAcceptor()
{
}
/**
* Binds to the specified <code>address</code> and handles incoming connections with the specified
* <code>handler</code>. Backlog value is configured to the value of <code>backlog</code> property.
*
* @throws IOException if failed to bind
*/
public void bind(SocketAddress address, IoHandler handler, IoServiceConfig config) throws IOException
{
if (address == null)
{
throw new NullPointerException("address");
}
if (handler == null)
{
throw new NullPointerException("handler");
}
if (!(address instanceof InetSocketAddress))
{
throw new IllegalArgumentException("Unexpected address type: " + address.getClass());
}
if (((InetSocketAddress) address).getPort() == 0)
{
throw new IllegalArgumentException("Unsupported port number: 0");
}
if (config == null)
{
config = getDefaultConfig();
}
SocketBinding service = new SocketBinding(address, handler, config);
synchronized (services)
{
services.put(address, service);
}
service.start();
}
public Set getManagedSessions(SocketAddress address)
{
if (address == null)
{
throw new NullPointerException("address");
}
SocketBinding service = (SocketBinding) services.get(address);
if (service == null)
{
throw new IllegalArgumentException("Address not bound: " + address);
}
return Collections.unmodifiableSet(new HashSet(service.sessions));
}
public void unbind(SocketAddress address)
{
if (address == null)
{
throw new NullPointerException("address");
}
SocketBinding service;
synchronized (services)
{
service = (SocketBinding) services.remove(address);
}
if (service == null)
{
throw new IllegalArgumentException("Address not bound: " + address);
}
try
{
service.unbind();
}
catch (IOException e)
{
//TODO: handle properly
e.printStackTrace();
}
}
public void unbindAll()
{
synchronized (services)
{
for (Iterator i = services.entrySet().iterator(); i.hasNext();)
{
SocketBinding service = (SocketBinding) i.next();
try
{
service.unbind();
}
catch (IOException e)
{
//TODO: handle properly
e.printStackTrace();
}
i.remove();
}
}
}
public boolean isBound(SocketAddress address)
{
synchronized (services)
{
return services.containsKey(address);
}
}
public IoServiceConfig getDefaultConfig()
{
return defaultConfig;
}
private class SocketBinding implements Runnable
{
private final SocketAddress address;
private final ServerSocketChannel service;
//private final ServerSocket service;
private final IoServiceConfig config;
private final IoHandler handler;
private final List sessions = new ArrayList();
private volatile boolean stopped = false;
private Thread runner;
SocketBinding(SocketAddress address, IoHandler handler, IoServiceConfig config) throws IOException
{
this.address = address;
this.handler = handler;
this.config = config;
service = ServerSocketChannel.open();
service.socket().bind(address);
//service = new ServerSocket();
//service.bind(address);
}
void unbind() throws IOException
{
stopped = true;
//shutdown all sessions
for (Iterator i = sessions.iterator(); i.hasNext();)
{
((SocketSessionImpl) i.next()).close();
i.remove();
}
//close server socket
service.close();
if (runner != null)
{
try
{
runner.join();
}
catch (InterruptedException e)
{
//ignore and return
System.err.println("Warning: interrupted on unbind(" + address + ")");
}
}
}
void start()
{
runner = new Thread(this);
runner.start();
}
public void run()
{
while (!stopped)
{
try
{
accept();
}
catch (Exception e)
{
//handle this better...
e.printStackTrace();
}
}
}
private void accept() throws Exception
{
//accept(new SimpleSocketChannel(service.accept()));
accept(service.accept());
}
private void accept(ByteChannel channel) throws Exception
{
//SocketChannel channel;
//start session
SocketSessionImpl session = new SocketSessionImpl(SocketAcceptor.this,
(SocketSessionConfig) defaultConfig.getSessionConfig(),
handler,
channel,
address);
//signal start etc...
sessions.add(session);
//TODO
//need to set up filter chains somehow... (this is copied from connector...)
getFilterChainBuilder().buildFilterChain(session.getFilterChain());
config.getFilterChainBuilder().buildFilterChain(session.getFilterChain());
config.getThreadModel().buildFilterChain(session.getFilterChain());
((SocketFilterChain) session.getFilterChain()).sessionCreated(session);
session.start();
//not sure if this will work... socket is already opened before the created callback is called...
((SocketFilterChain) session.getFilterChain()).sessionOpened(session);
}
}
}