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