Java tutorial
/* * @(#)$Id: codetemplate_xbird.xml 943 2006-09-13 07:03:37Z yui $ * * Copyright 2006-2008 Makoto YUI * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Makoto YUI - initial implementation */ package xbird.server.services; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import xbird.config.Settings; import xbird.server.ServiceException; import xbird.storage.io.RemoteVarSegments; import xbird.storage.io.VarSegments.IDescriptor; import xbird.util.concurrent.reference.FinalizableSoftValueReferenceMap; import xbird.util.concurrent.reference.ReferenceMap; import xbird.util.concurrent.reference.ReferenceType; import xbird.util.concurrent.reference.ReferentFinalizer; import xbird.util.io.IOUtils; import xbird.util.lang.SystemUtils; import xbird.util.nio.NIOUtils; import xbird.util.nio.RemoteMemoryMappedFile; /** * * <DIV lang="en"></DIV> * <DIV lang="ja"></DIV> * * @author Makoto YUI (yuin405+xbird@gmail.com) */ public final class RemotePagingService extends ServiceBase { private static final Log LOG = LogFactory.getLog(RemotePagingService.class); public static final int MAX_COMMAND_BUFLEN = 1024; public static final int COMMAND_READ = 1; public static final int COMMAND_TRACK_READ = 2; public static final String SRV_NAME = "RemotePaging"; public static final int PORT = Integer.parseInt(Settings.get("xbird.remote.paging_serv.port", "8900")); private static final int SND_BUFSIZE = 16 * 1024; // 16k private final FinalizableSoftValueReferenceMap<String, FileChannel> _fdCacheMap; private final ReferenceMap<String, IDescriptor> _directoryCache; private final ByteBuffer _sndBufSegm; private final ByteBuffer _sndBufDTM; public RemotePagingService() { super(SRV_NAME); this._fdCacheMap = new FinalizableSoftValueReferenceMap<String, FileChannel>( new ReferentFinalizer<FileChannel>() { public void finalize(FileChannel reclaimed) { IOUtils.closeQuietly(reclaimed); } }); this._directoryCache = new ReferenceMap<String, IDescriptor>(ReferenceType.STRONG, ReferenceType.SOFT); if (SystemUtils.isSendfileSupported()) { this._sndBufSegm = null; this._sndBufDTM = null; } else { this._sndBufSegm = ByteBuffer.allocateDirect(SND_BUFSIZE); _sndBufSegm.order(ByteOrder.BIG_ENDIAN); this._sndBufDTM = ByteBuffer.allocateDirect(SND_BUFSIZE); _sndBufDTM.order(ByteOrder.LITTLE_ENDIAN); } } public void start() throws ServiceException { final ServerSocketChannel channel = createServerSocketChannel(); final Thread pagerThread = new Thread(SRV_NAME) { public void run() { try { acceptConnections(channel); } catch (IOException e) { throw new IllegalStateException(e); } finally { IOUtils.closeQuietly(channel); } } }; pagerThread.setDaemon(true); pagerThread.start(); } private static ServerSocketChannel createServerSocketChannel() { try { return ServerSocketChannel.open(); } catch (IOException e) { throw new IllegalStateException("failed to create a server socket", e); } } private void acceptConnections(final ServerSocketChannel channel) throws IOException { // set to non blocking mode channel.configureBlocking(false); // Bind the server socket to the local host and port InetAddress host = InetAddress.getLocalHost(); InetSocketAddress sockAddr = new InetSocketAddress(host, PORT); ServerSocket socket = channel.socket(); //socket.setReuseAddress(true); socket.bind(sockAddr); // Register accepts on the server socket with the selector. This // step tells the selector that the socket wants to be put on the // ready list when accept operations occur. Selector selector = Selector.open(); ByteBuffer cmdBuffer = ByteBuffer.allocateDirect(MAX_COMMAND_BUFLEN); IOHandler ioHandler = new IOHandler(cmdBuffer); AcceptHandler acceptHandler = new AcceptHandler(ioHandler); channel.register(selector, SelectionKey.OP_ACCEPT, acceptHandler); int n; while ((n = selector.select()) > 0) { // Someone is ready for I/O, get the ready keys Set<SelectionKey> selectedKeys = selector.selectedKeys(); final Iterator<SelectionKey> keyItor = selectedKeys.iterator(); while (keyItor.hasNext()) { SelectionKey key = keyItor.next(); keyItor.remove(); // The key indexes into the selector so you // can retrieve the socket that's ready for I/O Handler handler = (Handler) key.attachment(); try { handler.handle(key); } catch (CancelledKeyException cke) { ; } catch (IOException ioe) { LOG.fatal(ioe); NIOUtils.cancelKey(key); } catch (Throwable e) { LOG.fatal(e); NIOUtils.cancelKey(key); } } } } private interface Handler { public void handle(SelectionKey key) throws ClosedChannelException, IOException; } private static final class AcceptHandler implements Handler { final Handler nextHandler; AcceptHandler(Handler nextHandler) { this.nextHandler = nextHandler; } public void handle(SelectionKey key) throws ClosedChannelException, IOException { if (!key.isValid()) { return; } ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); SocketChannel socketChannel = serverChannel.accept(); if (LOG.isDebugEnabled()) { LOG.debug("accepted a connection from " + socketChannel.socket().getInetAddress()); } socketChannel.configureBlocking(false); socketChannel.register(key.selector(), SelectionKey.OP_READ, nextHandler); } } private final class IOHandler implements Handler { final ByteBuffer cmdBuffer; IOHandler(ByteBuffer cmdBuffer) { this.cmdBuffer = cmdBuffer; } public void handle(SelectionKey key) throws ClosedChannelException, IOException { if (!key.isValid()) { return; } SocketChannel channel = (SocketChannel) key.channel(); if (key.isReadable()) { if (doRead(channel, cmdBuffer)) { key.interestOps(SelectionKey.OP_WRITE); } else { key.selector().wakeup(); } } else if (key.isWritable()) { doWrite(channel, cmdBuffer); key.interestOps(SelectionKey.OP_READ); } else { if (LOG.isWarnEnabled()) { LOG.warn("Illegal state was detected for key: " + key); } } } } private static boolean doRead(final SocketChannel channel, final ByteBuffer cmdBuffer) throws IOException { cmdBuffer.clear(); int n, count = 0; while (channel.isOpen() && (n = channel.read(cmdBuffer)) > 0) { count += n; } return count > 0; } /** * @return is closeable? */ private void doWrite(final SocketChannel socketChannel, final ByteBuffer cmdBuffer) { // receive a command cmdBuffer.flip(); final int cmd = cmdBuffer.getInt(); switch (cmd) { case COMMAND_READ: RemoteMemoryMappedFile.sendback(socketChannel, cmdBuffer, _fdCacheMap, _sndBufDTM); break; case COMMAND_TRACK_READ: RemoteVarSegments.sendback(socketChannel, cmdBuffer, _fdCacheMap, _directoryCache, _sndBufSegm); break; default: throw new IllegalStateException("Unknown command: " + cmd); } } public void stop() throws ServiceException { _fdCacheMap.clear(); } }