/* * Copyright 2009-2010 Brian S O'Neill * * 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. */ package org.cojen.dirmi.io; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.rmi.Remote; /** * * * @author Brian S O'Neill */ class PipedChannel implements Channel { private final IOExecutor mExecutor; private final Input mIn; private final PipedOutputStream mPout; private final BufferedChannelOutputStream mOut; //private CloseableGroup<? super Channel>[] mGroups; PipedChannel(IOExecutor executor, PipedInputStream in, PipedOutputStream out, int outputBufferSize) { mExecutor = executor; mIn = new Input(in); mPout = out; mOut = new BufferedChannelOutputStream(this, out, outputBufferSize); } @Override public Object getLocalAddress() { return mIn.toString(); } @Override public Object getRemoteAddress() { return mPout.toString(); } @Override public InputStream getInputStream() { return mIn; } @Override public OutputStream getOutputStream() { return mOut; } @Override public boolean isInputReady() throws IOException { return mIn.isReady(); } @Override public boolean isOutputReady() throws IOException { return mPout.isReady() || mOut.isReady(); } @Override public int setInputBufferSize(int size) { return 0; } @Override public int setOutputBufferSize(int size) { return mOut.setBufferSize(size); } @Override public void inputNotify(Channel.Listener listener) { mIn.inputNotify(mExecutor, listener); } @Override public void outputNotify(Channel.Listener listener) { try { if (isOutputReady()) { new PipeNotify(mExecutor, listener); } else { mOut.outputNotify(mExecutor, listener); } } catch (IOException e) { new PipeNotify(mExecutor, listener, e); } } @Override public boolean usesSelectNotification() { return false; } @Override public boolean inputResume() { return false; } @Override public boolean isResumeSupported() { return false; } @Override public boolean outputSuspend() throws IOException { flush(); return false; } @Override public String toString() { return "Channel {localAddress=" + getLocalAddress() + ", remoteAddress=" + getRemoteAddress() + '}'; } @Override public void register(CloseableGroup<? super Channel> group) { /* TODO: Can cause hang during close if (!group.add(this)) { return; } synchronized (this) { if (mGroups == null) { mGroups = new CloseableGroup[] {group}; } else { CloseableGroup<? super Channel>[] groups = new CloseableGroup[mGroups.length + 1]; System.arraycopy(mGroups, 0, groups, 0, mGroups.length); groups[groups.length - 1] = group; mGroups = groups; } } */ } @Override public boolean isClosed() { return mIn.isClosed() || mPout.isClosed(); } @Override public void flush() throws IOException { mOut.flush(); } @Override public void close() throws IOException { preClose(); mOut.outputClose(); mIn.inputClose(); } @Override public void disconnect() { preClose(); mOut.outputDisconnect(); mIn.inputClose(); } @Override public Remote installRecycler(Recycler recycler) { return null; } @Override public void setRecycleControl(Remote control) { } private synchronized void preClose() { /* TODO: Can cause hang during close if (mGroups != null) { for (CloseableGroup<? super Channel> group : mGroups) { group.remove(this); } mGroups = null; } */ } private class Input extends InputStream { private final PipedInputStream mIn; Input(PipedInputStream in) { mIn = in; } @Override public int read() throws IOException { return mIn.read(); } @Override public int read(byte[] b) throws IOException { return mIn.read(b); } @Override public int read(byte[] b, int offset, int length) throws IOException { return mIn.read(b, offset, length); } @Override public long skip(long n) throws IOException { return mIn.skip(n); } @Override public int available() throws IOException { return mIn.available(); } @Override public void close() throws IOException { PipedChannel.this.close(); } @Override public String toString() { return mIn.toString(); } void inputNotify(IOExecutor executor, Channel.Listener listener) { mIn.inputNotify(executor, listener); } boolean isReady() throws IOException { return mIn.isReady(); } boolean isClosed() { return mIn.isClosed(); } void inputClose() { mIn.close(); } } }