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}