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: */
020: package org.apache.mina.common;
021:
022: import java.io.IOException;
023: import java.net.SocketAddress;
024: import java.util.ArrayList;
025: import java.util.Collection;
026: import java.util.Collections;
027: import java.util.HashSet;
028: import java.util.List;
029: import java.util.Set;
030:
031: /**
032: * A base implementation of {@link IoAcceptor}.
033: *
034: * @author The Apache MINA Project (dev@mina.apache.org)
035: * @version $Rev: 601236 $, $Date: 2007-12-05 00:53:03 -0700 (Wed, 05 Dec 2007) $
036: */
037: public abstract class AbstractIoAcceptor extends AbstractIoService
038: implements IoAcceptor {
039:
040: private final List<SocketAddress> defaultLocalAddresses = new ArrayList<SocketAddress>();
041: private final List<SocketAddress> unmodifiableDefaultLocalAddresses = Collections
042: .unmodifiableList(defaultLocalAddresses);
043: private final Set<SocketAddress> boundAddresses = new HashSet<SocketAddress>();
044:
045: private boolean disconnectOnUnbind = true;
046:
047: /**
048: * The lock object which is acquired while bind or unbind operation is performed.
049: * Acquire this lock in your property setters which shouldn't be changed while
050: * the service is bound.
051: */
052: protected final Object bindLock = new Object();
053:
054: protected AbstractIoAcceptor(IoSessionConfig sessionConfig) {
055: super (sessionConfig);
056: defaultLocalAddresses.add(null);
057: }
058:
059: public SocketAddress getLocalAddress() {
060: Set<SocketAddress> localAddresses = getLocalAddresses();
061: if (localAddresses.isEmpty()) {
062: return null;
063: } else {
064: return localAddresses.iterator().next();
065: }
066: }
067:
068: public final Set<SocketAddress> getLocalAddresses() {
069: Set<SocketAddress> localAddresses = new HashSet<SocketAddress>();
070: synchronized (bindLock) {
071: localAddresses.addAll(boundAddresses);
072: }
073: return localAddresses;
074: }
075:
076: public SocketAddress getDefaultLocalAddress() {
077: if (defaultLocalAddresses.isEmpty()) {
078: return null;
079: }
080: return defaultLocalAddresses.iterator().next();
081: }
082:
083: public final void setDefaultLocalAddress(SocketAddress localAddress) {
084: setDefaultLocalAddresses(localAddress);
085: }
086:
087: public final List<SocketAddress> getDefaultLocalAddresses() {
088: return unmodifiableDefaultLocalAddresses;
089: }
090:
091: public final void setDefaultLocalAddresses(
092: List<? extends SocketAddress> localAddresses) {
093: if (localAddresses == null) {
094: throw new NullPointerException("localAddresses");
095: }
096: setDefaultLocalAddresses((Iterable<? extends SocketAddress>) localAddresses);
097: }
098:
099: public final void setDefaultLocalAddresses(
100: Iterable<? extends SocketAddress> localAddresses) {
101: if (localAddresses == null) {
102: throw new NullPointerException("localAddresses");
103: }
104:
105: synchronized (bindLock) {
106: if (!boundAddresses.isEmpty()) {
107: throw new IllegalStateException(
108: "localAddress can't be set while the acceptor is bound.");
109: }
110:
111: Collection<SocketAddress> newLocalAddresses = new ArrayList<SocketAddress>();
112: for (SocketAddress a : localAddresses) {
113: checkAddressType(a);
114: newLocalAddresses.add(a);
115: }
116:
117: if (newLocalAddresses.isEmpty()) {
118: throw new IllegalArgumentException(
119: "empty localAddresses");
120: }
121:
122: this .defaultLocalAddresses.clear();
123: this .defaultLocalAddresses.addAll(newLocalAddresses);
124: }
125: }
126:
127: public final void setDefaultLocalAddresses(
128: SocketAddress firstLocalAddress,
129: SocketAddress... otherLocalAddresses) {
130: if (otherLocalAddresses == null) {
131: otherLocalAddresses = new SocketAddress[0];
132: }
133:
134: Collection<SocketAddress> newLocalAddresses = new ArrayList<SocketAddress>(
135: otherLocalAddresses.length + 1);
136:
137: newLocalAddresses.add(firstLocalAddress);
138: for (SocketAddress a : otherLocalAddresses) {
139: newLocalAddresses.add(a);
140: }
141:
142: setDefaultLocalAddresses(newLocalAddresses);
143: }
144:
145: public final boolean isCloseOnDeactivation() {
146: return disconnectOnUnbind;
147: }
148:
149: public final void setCloseOnDeactivation(
150: boolean disconnectClientsOnUnbind) {
151: this .disconnectOnUnbind = disconnectClientsOnUnbind;
152: }
153:
154: public final void bind() throws IOException {
155: bind(getDefaultLocalAddresses());
156: }
157:
158: public final void bind(SocketAddress localAddress)
159: throws IOException {
160: if (localAddress == null) {
161: throw new NullPointerException("localAddress");
162: }
163:
164: List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(
165: 1);
166: localAddresses.add(localAddress);
167: bind(localAddresses);
168: }
169:
170: public final void bind(SocketAddress firstLocalAddress,
171: SocketAddress... otherLocalAddresses) throws IOException {
172: if (firstLocalAddress == null) {
173: throw new NullPointerException("firstLocalAddress");
174: }
175: if (otherLocalAddresses == null) {
176: throw new NullPointerException("otherLocalAddresses");
177: }
178:
179: List<SocketAddress> localAddresses = new ArrayList<SocketAddress>();
180: localAddresses.add(firstLocalAddress);
181: Collections.addAll(localAddresses, otherLocalAddresses);
182: bind(localAddresses);
183: }
184:
185: public final void bind(
186: Iterable<? extends SocketAddress> localAddresses)
187: throws IOException {
188: if (isDisposing()) {
189: throw new IllegalStateException("Already disposed.");
190: }
191: if (localAddresses == null) {
192: throw new NullPointerException("localAddresses");
193: }
194:
195: List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
196: for (SocketAddress a : localAddresses) {
197: checkAddressType(a);
198: localAddressesCopy.add(a);
199: }
200: if (localAddressesCopy.isEmpty()) {
201: throw new IllegalArgumentException(
202: "localAddresses is empty.");
203: }
204:
205: boolean activate = false;
206: synchronized (bindLock) {
207: if (boundAddresses.isEmpty()) {
208: activate = true;
209: }
210:
211: if (getHandler() == null) {
212: throw new IllegalStateException("handler is not set.");
213: }
214:
215: try {
216: boundAddresses.addAll(bind0(localAddressesCopy));
217: } catch (IOException e) {
218: throw e;
219: } catch (RuntimeException e) {
220: throw e;
221: } catch (Throwable e) {
222: throw new RuntimeIoException("Failed to bind to: "
223: + getLocalAddresses(), e);
224: }
225: }
226:
227: if (activate) {
228: getListeners().fireServiceActivated();
229: }
230: }
231:
232: public final void unbind() {
233: unbind(getLocalAddresses());
234: }
235:
236: public final void unbind(SocketAddress localAddress) {
237: if (localAddress == null) {
238: throw new NullPointerException("localAddress");
239: }
240:
241: List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(
242: 1);
243: localAddresses.add(localAddress);
244: unbind(localAddresses);
245: }
246:
247: public final void unbind(SocketAddress firstLocalAddress,
248: SocketAddress... otherLocalAddresses) {
249: if (firstLocalAddress == null) {
250: throw new NullPointerException("firstLocalAddress");
251: }
252: if (otherLocalAddresses == null) {
253: throw new NullPointerException("otherLocalAddresses");
254: }
255:
256: List<SocketAddress> localAddresses = new ArrayList<SocketAddress>();
257: localAddresses.add(firstLocalAddress);
258: Collections.addAll(localAddresses, otherLocalAddresses);
259: unbind(localAddresses);
260: }
261:
262: public final void unbind(
263: Iterable<? extends SocketAddress> localAddresses) {
264: if (localAddresses == null) {
265: throw new NullPointerException("localAddresses");
266: }
267:
268: boolean deactivate = false;
269: synchronized (bindLock) {
270: if (boundAddresses.isEmpty()) {
271: return;
272: }
273:
274: List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
275: int specifiedAddressCount = 0;
276: for (SocketAddress a : localAddresses) {
277: specifiedAddressCount++;
278: if (a != null && boundAddresses.contains(a)) {
279: localAddressesCopy.add(a);
280: }
281: }
282: if (specifiedAddressCount == 0) {
283: throw new IllegalArgumentException(
284: "localAddresses is empty.");
285: }
286:
287: if (!localAddressesCopy.isEmpty()) {
288: try {
289: unbind0(localAddressesCopy);
290: } catch (RuntimeException e) {
291: throw e;
292: } catch (Throwable e) {
293: throw new RuntimeIoException(
294: "Failed to unbind from: "
295: + getLocalAddresses(), e);
296: }
297:
298: boundAddresses.removeAll(localAddressesCopy);
299: if (boundAddresses.isEmpty()) {
300: deactivate = true;
301: }
302: }
303: }
304:
305: if (deactivate) {
306: getListeners().fireServiceDeactivated();
307: }
308: }
309:
310: /**
311: * Implement this method to perform the actual bind operation.
312: * @return the {@link Set} of the local addresses which is bound actually
313: */
314: protected abstract Set<SocketAddress> bind0(
315: List<? extends SocketAddress> localAddresses)
316: throws Exception;
317:
318: /**
319: * Implement this method to perform the actual unbind operation.
320: */
321: protected abstract void unbind0(
322: List<? extends SocketAddress> localAddresses)
323: throws Exception;
324:
325: @Override
326: public String toString() {
327: TransportMetadata m = getTransportMetadata();
328: return '('
329: + m.getProviderName()
330: + ' '
331: + m.getName()
332: + " acceptor: "
333: + (isActive() ? "localAddress(es): "
334: + getLocalAddresses()
335: + ", managedSessionCount: "
336: + getManagedSessionCount() : "not bound") + ')';
337: }
338:
339: private void checkAddressType(SocketAddress a) {
340: if (a != null
341: && !getTransportMetadata().getAddressType()
342: .isAssignableFrom(a.getClass())) {
343: throw new IllegalArgumentException("localAddress type: "
344: + a.getClass().getSimpleName()
345: + " (expected: "
346: + getTransportMetadata().getAddressType()
347: .getSimpleName() + ")");
348: }
349: }
350:
351: protected static class AcceptorOperationFuture extends
352: ServiceOperationFuture {
353: private final List<SocketAddress> localAddresses;
354:
355: public AcceptorOperationFuture(
356: List<? extends SocketAddress> localAddresses) {
357: this .localAddresses = new ArrayList<SocketAddress>(
358: localAddresses);
359: }
360:
361: public final List<SocketAddress> getLocalAddresses() {
362: return Collections.unmodifiableList(localAddresses);
363: }
364: }
365: }
|