001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.transport.vmpipe;
021
022import java.io.IOException;
023import java.net.SocketAddress;
024import java.util.HashMap;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Map;
028import java.util.Set;
029import java.util.concurrent.Executor;
030
031import org.apache.mina.core.future.IoFuture;
032import org.apache.mina.core.service.AbstractIoAcceptor;
033import org.apache.mina.core.service.IoHandler;
034import org.apache.mina.core.service.TransportMetadata;
035import org.apache.mina.core.session.IdleStatusChecker;
036import org.apache.mina.core.session.IoSession;
037
038/**
039 * Binds the specified {@link IoHandler} to the specified
040 * {@link VmPipeAddress}.
041 *
042 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
043 */
044public final class VmPipeAcceptor extends AbstractIoAcceptor {
045
046    // object used for checking session idle
047    private IdleStatusChecker idleChecker;
048
049    static final Map<VmPipeAddress, VmPipe> boundHandlers = new HashMap<VmPipeAddress, VmPipe>();
050
051    /**
052     * Creates a new instance.
053     */
054    public VmPipeAcceptor() {
055        this(null);
056    }
057
058    /**
059     * Creates a new instance.
060     * 
061     * @param executor The executor to use
062     */
063    public VmPipeAcceptor(Executor executor) {
064        super(new DefaultVmPipeSessionConfig(), executor);
065        idleChecker = new IdleStatusChecker();
066        // we schedule the idle status checking task in this service exceutor
067        // it will be woke up every seconds
068        executeWorker(idleChecker.getNotifyingTask(), "idleStatusChecker");
069    }
070
071    /**
072     * {@inheritDoc}
073     */
074    public TransportMetadata getTransportMetadata() {
075        return VmPipeSession.METADATA;
076    }
077
078    /**
079     * {@inheritDoc}
080     */
081    public VmPipeSessionConfig getSessionConfig() {
082        return (VmPipeSessionConfig) sessionConfig;
083    }
084
085    /**
086     * {@inheritDoc}
087     */
088    @Override
089    public VmPipeAddress getLocalAddress() {
090        return (VmPipeAddress) super.getLocalAddress();
091    }
092
093    /**
094     * {@inheritDoc}
095     */
096    @Override
097    public VmPipeAddress getDefaultLocalAddress() {
098        return (VmPipeAddress) super.getDefaultLocalAddress();
099    }
100
101    // This method is overriden to work around a problem with
102    // bean property access mechanism.
103    /**
104     * Sets the local Address for this acceptor
105     * 
106     * @param localAddress The local address to use
107     */
108    public void setDefaultLocalAddress(VmPipeAddress localAddress) {
109        super.setDefaultLocalAddress(localAddress);
110    }
111
112    /**
113     * {@inheritDoc}
114     */
115    @Override
116    protected void dispose0() throws Exception {
117        // stop the idle checking task
118        idleChecker.getNotifyingTask().cancel();
119        unbind();
120    }
121
122    /**
123     * {@inheritDoc}
124     */
125    @Override
126    protected Set<SocketAddress> bindInternal(List<? extends SocketAddress> localAddresses) throws IOException {
127        Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();
128
129        synchronized (boundHandlers) {
130            for (SocketAddress a : localAddresses) {
131                VmPipeAddress localAddress = (VmPipeAddress) a;
132                if (localAddress == null || localAddress.getPort() == 0) {
133                    localAddress = null;
134                    for (int i = 10000; i < Integer.MAX_VALUE; i++) {
135                        VmPipeAddress newLocalAddress = new VmPipeAddress(i);
136                        if (!boundHandlers.containsKey(newLocalAddress) && !newLocalAddresses.contains(newLocalAddress)) {
137                            localAddress = newLocalAddress;
138                            break;
139                        }
140                    }
141
142                    if (localAddress == null) {
143                        throw new IOException("No port available.");
144                    }
145                } else if (localAddress.getPort() < 0) {
146                    throw new IOException("Bind port number must be 0 or above.");
147                } else if (boundHandlers.containsKey(localAddress)) {
148                    throw new IOException("Address already bound: " + localAddress);
149                }
150
151                newLocalAddresses.add(localAddress);
152            }
153
154            for (SocketAddress a : newLocalAddresses) {
155                VmPipeAddress localAddress = (VmPipeAddress) a;
156                if (!boundHandlers.containsKey(localAddress)) {
157                    boundHandlers.put(localAddress, new VmPipe(this, localAddress, getHandler(), getListeners()));
158                } else {
159                    for (SocketAddress a2 : newLocalAddresses) {
160                        boundHandlers.remove(a2);
161                    }
162                    throw new IOException("Duplicate local address: " + a);
163                }
164            }
165        }
166
167        return newLocalAddresses;
168    }
169
170    @Override
171    protected void unbind0(List<? extends SocketAddress> localAddresses) {
172        synchronized (boundHandlers) {
173            for (SocketAddress a : localAddresses) {
174                boundHandlers.remove(a);
175            }
176        }
177    }
178
179    /**
180     * {@inheritDoc}
181     */
182    public IoSession newSession(SocketAddress remoteAddress, SocketAddress localAddress) {
183        throw new UnsupportedOperationException();
184    }
185
186    void doFinishSessionInitialization(IoSession session, IoFuture future) {
187        initSession(session, future, null);
188    }
189}