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.core.service; 021 022import java.net.SocketAddress; 023import java.util.concurrent.Executor; 024import java.util.concurrent.Executors; 025 026import org.apache.mina.core.future.ConnectFuture; 027import org.apache.mina.core.future.IoFuture; 028import org.apache.mina.core.future.IoFutureListener; 029import org.apache.mina.core.session.IdleStatus; 030import org.apache.mina.core.session.IoSession; 031import org.apache.mina.core.session.IoSessionConfig; 032import org.apache.mina.core.session.IoSessionInitializer; 033 034/** 035 * A base implementation of {@link IoConnector}. 036 * 037 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 038 */ 039public abstract class AbstractIoConnector extends AbstractIoService implements IoConnector { 040 /** 041 * The minimum timeout value that is supported (in milliseconds). 042 */ 043 private long connectTimeoutCheckInterval = 50L; 044 045 private long connectTimeoutInMillis = 60 * 1000L; // 1 minute by default 046 047 /** The remote address we are connected to */ 048 private SocketAddress defaultRemoteAddress; 049 050 /** The local address */ 051 private SocketAddress defaultLocalAddress; 052 053 /** 054 * Constructor for {@link AbstractIoConnector}. You need to provide a 055 * default session configuration and an {@link Executor} for handling I/O 056 * events. If null {@link Executor} is provided, a default one will be 057 * created using {@link Executors#newCachedThreadPool()}. 058 * 059 * @see AbstractIoService#AbstractIoService(IoSessionConfig, Executor) 060 * 061 * @param sessionConfig 062 * the default configuration for the managed {@link IoSession} 063 * @param executor 064 * the {@link Executor} used for handling execution of I/O 065 * events. Can be <code>null</code>. 066 */ 067 protected AbstractIoConnector(IoSessionConfig sessionConfig, Executor executor) { 068 super(sessionConfig, executor); 069 } 070 071 /** 072 * @return 073 * The minimum time that this connector can have for a connection 074 * timeout in milliseconds. 075 */ 076 public long getConnectTimeoutCheckInterval() { 077 return connectTimeoutCheckInterval; 078 } 079 080 public void setConnectTimeoutCheckInterval(long minimumConnectTimeout) { 081 if (getConnectTimeoutMillis() < minimumConnectTimeout) { 082 this.connectTimeoutInMillis = minimumConnectTimeout; 083 } 084 085 this.connectTimeoutCheckInterval = minimumConnectTimeout; 086 } 087 088 /** 089 * @deprecated 090 * Take a look at <tt>getConnectTimeoutMillis()</tt> 091 */ 092 public final int getConnectTimeout() { 093 return (int) connectTimeoutInMillis / 1000; 094 } 095 096 /** 097 * {@inheritDoc} 098 */ 099 public final long getConnectTimeoutMillis() { 100 return connectTimeoutInMillis; 101 } 102 103 /** 104 * @deprecated 105 * Take a look at <tt>setConnectTimeoutMillis(long)</tt> 106 */ 107 public final void setConnectTimeout(int connectTimeout) { 108 109 setConnectTimeoutMillis(connectTimeout * 1000L); 110 } 111 112 /** 113 * Sets the connect timeout value in milliseconds. 114 * 115 */ 116 public final void setConnectTimeoutMillis(long connectTimeoutInMillis) { 117 if (connectTimeoutInMillis <= connectTimeoutCheckInterval) { 118 this.connectTimeoutCheckInterval = connectTimeoutInMillis; 119 } 120 this.connectTimeoutInMillis = connectTimeoutInMillis; 121 } 122 123 /** 124 * {@inheritDoc} 125 */ 126 public SocketAddress getDefaultRemoteAddress() { 127 return defaultRemoteAddress; 128 } 129 130 /** 131 * {@inheritDoc} 132 */ 133 public final void setDefaultLocalAddress(SocketAddress localAddress) { 134 defaultLocalAddress = localAddress; 135 } 136 137 /** 138 * {@inheritDoc} 139 */ 140 public final SocketAddress getDefaultLocalAddress() { 141 return defaultLocalAddress; 142 } 143 144 /** 145 * {@inheritDoc} 146 */ 147 public final void setDefaultRemoteAddress(SocketAddress defaultRemoteAddress) { 148 if (defaultRemoteAddress == null) { 149 throw new IllegalArgumentException("defaultRemoteAddress"); 150 } 151 152 if (!getTransportMetadata().getAddressType().isAssignableFrom(defaultRemoteAddress.getClass())) { 153 throw new IllegalArgumentException("defaultRemoteAddress type: " + defaultRemoteAddress.getClass() 154 + " (expected: " + getTransportMetadata().getAddressType() + ")"); 155 } 156 this.defaultRemoteAddress = defaultRemoteAddress; 157 } 158 159 /** 160 * {@inheritDoc} 161 */ 162 public final ConnectFuture connect() { 163 SocketAddress defaultRemoteAddress = getDefaultRemoteAddress(); 164 if (defaultRemoteAddress == null) { 165 throw new IllegalStateException("defaultRemoteAddress is not set."); 166 } 167 168 return connect(defaultRemoteAddress, null, null); 169 } 170 171 /** 172 * {@inheritDoc} 173 */ 174 public ConnectFuture connect(IoSessionInitializer<? extends ConnectFuture> sessionInitializer) { 175 SocketAddress defaultRemoteAddress = getDefaultRemoteAddress(); 176 if (defaultRemoteAddress == null) { 177 throw new IllegalStateException("defaultRemoteAddress is not set."); 178 } 179 180 return connect(defaultRemoteAddress, null, sessionInitializer); 181 } 182 183 /** 184 * {@inheritDoc} 185 */ 186 public final ConnectFuture connect(SocketAddress remoteAddress) { 187 return connect(remoteAddress, null, null); 188 } 189 190 /** 191 * {@inheritDoc} 192 */ 193 public ConnectFuture connect(SocketAddress remoteAddress, 194 IoSessionInitializer<? extends ConnectFuture> sessionInitializer) { 195 return connect(remoteAddress, null, sessionInitializer); 196 } 197 198 /** 199 * {@inheritDoc} 200 */ 201 public ConnectFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) { 202 return connect(remoteAddress, localAddress, null); 203 } 204 205 /** 206 * {@inheritDoc} 207 */ 208 public final ConnectFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, 209 IoSessionInitializer<? extends ConnectFuture> sessionInitializer) { 210 if (isDisposing()) { 211 throw new IllegalStateException("The connector is being disposed."); 212 } 213 214 if (remoteAddress == null) { 215 throw new IllegalArgumentException("remoteAddress"); 216 } 217 218 if (!getTransportMetadata().getAddressType().isAssignableFrom(remoteAddress.getClass())) { 219 throw new IllegalArgumentException("remoteAddress type: " + remoteAddress.getClass() + " (expected: " 220 + getTransportMetadata().getAddressType() + ")"); 221 } 222 223 if (localAddress != null && !getTransportMetadata().getAddressType().isAssignableFrom(localAddress.getClass())) { 224 throw new IllegalArgumentException("localAddress type: " + localAddress.getClass() + " (expected: " 225 + getTransportMetadata().getAddressType() + ")"); 226 } 227 228 if (getHandler() == null) { 229 if (getSessionConfig().isUseReadOperation()) { 230 setHandler(new IoHandler() { 231 public void exceptionCaught(IoSession session, Throwable cause) throws Exception { 232 // Empty handler 233 } 234 235 public void messageReceived(IoSession session, Object message) throws Exception { 236 // Empty handler 237 } 238 239 public void messageSent(IoSession session, Object message) throws Exception { 240 // Empty handler 241 } 242 243 public void sessionClosed(IoSession session) throws Exception { 244 // Empty handler 245 } 246 247 public void sessionCreated(IoSession session) throws Exception { 248 // Empty handler 249 } 250 251 public void sessionIdle(IoSession session, IdleStatus status) throws Exception { 252 // Empty handler 253 } 254 255 public void sessionOpened(IoSession session) throws Exception { 256 // Empty handler 257 } 258 259 public void inputClosed(IoSession session) throws Exception { 260 // Empty handler 261 } 262 }); 263 } else { 264 throw new IllegalStateException("handler is not set."); 265 } 266 } 267 268 return connect0(remoteAddress, localAddress, sessionInitializer); 269 } 270 271 /** 272 * Implement this method to perform the actual connect operation. 273 * 274 * @param remoteAddress The remote address to connect from 275 * @param localAddress <tt>null</tt> if no local address is specified 276 * @param sessionInitializer The IoSessionInitializer to use when the connection s successful 277 * @return The ConnectFuture associated with this asynchronous operation 278 * 279 */ 280 protected abstract ConnectFuture connect0(SocketAddress remoteAddress, SocketAddress localAddress, 281 IoSessionInitializer<? extends ConnectFuture> sessionInitializer); 282 283 /** 284 * Adds required internal attributes and {@link IoFutureListener}s 285 * related with event notifications to the specified {@code session} 286 * and {@code future}. Do not call this method directly; 287 */ 288 @Override 289 protected final void finishSessionInitialization0(final IoSession session, IoFuture future) { 290 // In case that ConnectFuture.cancel() is invoked before 291 // setSession() is invoked, add a listener that closes the 292 // connection immediately on cancellation. 293 future.addListener(new IoFutureListener<ConnectFuture>() { 294 public void operationComplete(ConnectFuture future) { 295 if (future.isCanceled()) { 296 session.close(true); 297 } 298 } 299 }); 300 } 301 302 /** 303 * {@inheritDoc} 304 */ 305 @Override 306 public String toString() { 307 TransportMetadata m = getTransportMetadata(); 308 return '(' + m.getProviderName() + ' ' + m.getName() + " connector: " + "managedSessionCount: " 309 + getManagedSessionCount() + ')'; 310 } 311}