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.session; 021 022import java.io.IOException; 023import java.net.SocketAddress; 024import java.util.List; 025import java.util.Set; 026import java.util.concurrent.Executor; 027 028import org.apache.mina.core.file.FileRegion; 029import org.apache.mina.core.filterchain.DefaultIoFilterChain; 030import org.apache.mina.core.filterchain.IoFilter; 031import org.apache.mina.core.filterchain.IoFilterChain; 032import org.apache.mina.core.service.AbstractIoAcceptor; 033import org.apache.mina.core.service.DefaultTransportMetadata; 034import org.apache.mina.core.service.IoHandler; 035import org.apache.mina.core.service.IoHandlerAdapter; 036import org.apache.mina.core.service.IoProcessor; 037import org.apache.mina.core.service.IoService; 038import org.apache.mina.core.service.TransportMetadata; 039import org.apache.mina.core.write.WriteRequest; 040import org.apache.mina.core.write.WriteRequestQueue; 041 042/** 043 * A dummy {@link IoSession} for unit-testing or non-network-use of 044 * the classes that depends on {@link IoSession}. 045 * 046 * <h2>Overriding I/O request methods</h2> 047 * All I/O request methods (i.e. {@link #close()}, {@link #write(Object)} 048 * are final and therefore cannot be 049 * overridden, but you can always add your custom {@link IoFilter} to the 050 * {@link IoFilterChain} to intercept any I/O events and requests. 051 * 052 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 053 */ 054public class DummySession extends AbstractIoSession { 055 056 private static final TransportMetadata TRANSPORT_METADATA = new DefaultTransportMetadata("mina", "dummy", false, 057 false, SocketAddress.class, IoSessionConfig.class, Object.class); 058 059 private static final SocketAddress ANONYMOUS_ADDRESS = new SocketAddress() { 060 private static final long serialVersionUID = -496112902353454179L; 061 062 @Override 063 public String toString() { 064 return "?"; 065 } 066 }; 067 068 private volatile IoService service; 069 070 private volatile IoSessionConfig config = new AbstractIoSessionConfig() { 071 @Override 072 protected void doSetAll(IoSessionConfig config) { 073 // Do nothing 074 } 075 }; 076 077 private final IoFilterChain filterChain = new DefaultIoFilterChain(this); 078 079 private final IoProcessor<IoSession> processor; 080 081 private volatile IoHandler handler = new IoHandlerAdapter(); 082 083 private volatile SocketAddress localAddress = ANONYMOUS_ADDRESS; 084 085 private volatile SocketAddress remoteAddress = ANONYMOUS_ADDRESS; 086 087 private volatile TransportMetadata transportMetadata = TRANSPORT_METADATA; 088 089 /** 090 * Creates a new instance. 091 */ 092 public DummySession() { 093 super( 094 095 // Initialize dummy service. 096 new AbstractIoAcceptor(new AbstractIoSessionConfig() { 097 @Override 098 protected void doSetAll(IoSessionConfig config) { 099 // Do nothing 100 } 101 }, new Executor() { 102 public void execute(Runnable command) { 103 // Do nothing 104 } 105 }) { 106 107 @Override 108 protected Set<SocketAddress> bindInternal(List<? extends SocketAddress> localAddresses) 109 throws Exception { 110 throw new UnsupportedOperationException(); 111 } 112 113 @Override 114 protected void unbind0(List<? extends SocketAddress> localAddresses) throws Exception { 115 throw new UnsupportedOperationException(); 116 } 117 118 public IoSession newSession(SocketAddress remoteAddress, SocketAddress localAddress) { 119 throw new UnsupportedOperationException(); 120 } 121 122 public TransportMetadata getTransportMetadata() { 123 return TRANSPORT_METADATA; 124 } 125 126 @Override 127 protected void dispose0() throws Exception { 128 } 129 130 /** 131 * {@inheritDoc} 132 */ 133 public IoSessionConfig getSessionConfig() { 134 return sessionConfig; 135 } 136 }); 137 138 processor = new IoProcessor<IoSession>() { 139 public void add(IoSession session) { 140 // Do nothing 141 } 142 143 public void flush(IoSession session) { 144 DummySession s = (DummySession) session; 145 WriteRequest req = s.getWriteRequestQueue().poll(session); 146 147 // Chek that the request is not null. If the session has been closed, 148 // we may not have any pending requests. 149 if (req != null) { 150 Object m = req.getMessage(); 151 if (m instanceof FileRegion) { 152 FileRegion file = (FileRegion) m; 153 try { 154 file.getFileChannel().position(file.getPosition() + file.getRemainingBytes()); 155 file.update(file.getRemainingBytes()); 156 } catch (IOException e) { 157 s.getFilterChain().fireExceptionCaught(e); 158 } 159 } 160 getFilterChain().fireMessageSent(req); 161 } 162 } 163 164 /** 165 * {@inheritDoc} 166 */ 167 public void write(IoSession session, WriteRequest writeRequest) { 168 WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue(); 169 170 writeRequestQueue.offer(session, writeRequest); 171 172 if (!session.isWriteSuspended()) { 173 this.flush(session); 174 } 175 } 176 177 public void remove(IoSession session) { 178 if (!session.getCloseFuture().isClosed()) { 179 session.getFilterChain().fireSessionClosed(); 180 } 181 } 182 183 public void updateTrafficControl(IoSession session) { 184 // Do nothing 185 } 186 187 public void dispose() { 188 // Do nothing 189 } 190 191 public boolean isDisposed() { 192 return false; 193 } 194 195 public boolean isDisposing() { 196 return false; 197 } 198 199 }; 200 201 this.service = super.getService(); 202 203 try { 204 IoSessionDataStructureFactory factory = new DefaultIoSessionDataStructureFactory(); 205 setAttributeMap(factory.getAttributeMap(this)); 206 setWriteRequestQueue(factory.getWriteRequestQueue(this)); 207 } catch (Exception e) { 208 throw new InternalError(); 209 } 210 } 211 212 /** 213 * {@inheritDoc} 214 */ 215 public IoSessionConfig getConfig() { 216 return config; 217 } 218 219 /** 220 * Sets the configuration of this session. 221 * 222 * @param config the {@link IoSessionConfig} to set 223 */ 224 public void setConfig(IoSessionConfig config) { 225 if (config == null) { 226 throw new IllegalArgumentException("config"); 227 } 228 229 this.config = config; 230 } 231 232 /** 233 * {@inheritDoc} 234 */ 235 public IoFilterChain getFilterChain() { 236 return filterChain; 237 } 238 239 /** 240 * {@inheritDoc} 241 */ 242 public IoHandler getHandler() { 243 return handler; 244 } 245 246 /** 247 * Sets the {@link IoHandler} which handles this session. 248 * 249 * @param handler the {@link IoHandler} to set 250 */ 251 public void setHandler(IoHandler handler) { 252 if (handler == null) { 253 throw new IllegalArgumentException("handler"); 254 } 255 256 this.handler = handler; 257 } 258 259 /** 260 * {@inheritDoc} 261 */ 262 public SocketAddress getLocalAddress() { 263 return localAddress; 264 } 265 266 /** 267 * {@inheritDoc} 268 */ 269 public SocketAddress getRemoteAddress() { 270 return remoteAddress; 271 } 272 273 /** 274 * Sets the socket address of local machine which is associated with 275 * this session. 276 * 277 * @param localAddress The socket address to set 278 */ 279 public void setLocalAddress(SocketAddress localAddress) { 280 if (localAddress == null) { 281 throw new IllegalArgumentException("localAddress"); 282 } 283 284 this.localAddress = localAddress; 285 } 286 287 /** 288 * Sets the socket address of remote peer. 289 * 290 * @param remoteAddress The socket address to set 291 */ 292 public void setRemoteAddress(SocketAddress remoteAddress) { 293 if (remoteAddress == null) { 294 throw new IllegalArgumentException("remoteAddress"); 295 } 296 297 this.remoteAddress = remoteAddress; 298 } 299 300 /** 301 * {@inheritDoc} 302 */ 303 public IoService getService() { 304 return service; 305 } 306 307 /** 308 * Sets the {@link IoService} which provides I/O service to this session. 309 * 310 * @param service The {@link IoService} to set 311 */ 312 public void setService(IoService service) { 313 if (service == null) { 314 throw new IllegalArgumentException("service"); 315 } 316 317 this.service = service; 318 } 319 320 /** 321 * {@inheritDoc} 322 */ 323 @Override 324 public final IoProcessor<IoSession> getProcessor() { 325 return processor; 326 } 327 328 /** 329 * {@inheritDoc} 330 */ 331 public TransportMetadata getTransportMetadata() { 332 return transportMetadata; 333 } 334 335 /** 336 * Sets the {@link TransportMetadata} that this session runs on. 337 * 338 * @param transportMetadata The {@link TransportMetadata} to set 339 */ 340 public void setTransportMetadata(TransportMetadata transportMetadata) { 341 if (transportMetadata == null) { 342 throw new IllegalArgumentException("transportMetadata"); 343 } 344 345 this.transportMetadata = transportMetadata; 346 } 347 348 /** 349 * {@inheritDoc} 350 */ 351 @Override 352 public void setScheduledWriteBytes(int byteCount) { 353 super.setScheduledWriteBytes(byteCount); 354 } 355 356 /** 357 * {@inheritDoc} 358 */ 359 @Override 360 public void setScheduledWriteMessages(int messages) { 361 super.setScheduledWriteMessages(messages); 362 } 363 364 /** 365 * Update all statistical properties related with throughput. By default 366 * this method returns silently without updating the throughput properties 367 * if they were calculated already within last 368 * {@link IoSessionConfig#getThroughputCalculationInterval() calculation interval}. 369 * If, however, <tt>force</tt> is specified as <tt>true</tt>, this method 370 * updates the throughput properties immediately. 371 * 372 * @param force the flag that forces the update of properties immediately if <tt>true</tt> 373 */ 374 public void updateThroughput(boolean force) { 375 super.updateThroughput(System.currentTimeMillis(), force); 376 } 377}