Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'incubation/projects/org.eclipse.ecf.ipc/bundles/org.eclipse.ecf.ipc/src/org/eclipse/ecf/ipc/fifo/FIFO.java')
-rw-r--r--incubation/projects/org.eclipse.ecf.ipc/bundles/org.eclipse.ecf.ipc/src/org/eclipse/ecf/ipc/fifo/FIFO.java489
1 files changed, 489 insertions, 0 deletions
diff --git a/incubation/projects/org.eclipse.ecf.ipc/bundles/org.eclipse.ecf.ipc/src/org/eclipse/ecf/ipc/fifo/FIFO.java b/incubation/projects/org.eclipse.ecf.ipc/bundles/org.eclipse.ecf.ipc/src/org/eclipse/ecf/ipc/fifo/FIFO.java
new file mode 100644
index 000000000..8d612ee86
--- /dev/null
+++ b/incubation/projects/org.eclipse.ecf.ipc/bundles/org.eclipse.ecf.ipc/src/org/eclipse/ecf/ipc/fifo/FIFO.java
@@ -0,0 +1,489 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Clark N. Hobbie
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Clark N. Hobbie - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ecf.ipc.fifo;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.ecf.ipc.IPCException;
+import org.eclipse.ecf.ipc.Utils;
+import org.eclipse.ecf.ipc.fifo.FIFOImpl.PipeDirection;
+
+
+/**
+ * A one-way interprocess communications channel.
+ * <UL>
+ * <LI><A href="#note">Note</A></LI>
+ * <LI><A href="#quickstart">Quickstart</A></LI>
+ * <LI><A href="#description">Description</A></LI>
+ * </UL>
+ * <A name="note"><H2>NOTE</H2></A> Because of the way that Windows implements named
+ * pipes, one process must call the {@link #create()} method before the named pipe can be
+ * used. This is in addition to whether that process will be exclusively reading from or
+ * writing to the pipe.
+ * <P>
+ * On Linux, a named pipe (FIFO) will persist after the process that created it
+ * terminates, allowing subsequent clients to use it without taking on a client or role.
+ * </P>
+ * <P>
+ * Unfortunately, using this information will introduce a platform dependency in the
+ * system.
+ * </P>
+ * <A name="quickstart"><H2>Quickstart</H2></A> <H3>Creator/Writer Process</H3>
+ * <P>
+ * <CODE>
+ * <PRE>
+ * byte[] buffer;
+ * String name;
+ * // create an instance for buffer and fill it with data
+ * // set name to a valid file name
+ * ...
+ * NamedPipe pipe = new NamedPipe(name);
+ * pipe.create();
+ * pipe.openWriter();
+ * pipe.write(buffer);
+ * </PRE>
+ * </CODE>
+ * <P>
+ * <H3>Reader Process</H3>
+ * <P>
+ * <CODE>
+ * <PRE>
+ * byte[] buffer;
+ * String name;
+ * // create an instance for buffer
+ * // set name to the same name as the writer is using
+ * ...
+ * NamedPipe pipe = new NamedPipe(name);
+ * pipe.openReader();
+ * int count = pipe.read(buffer);
+ * // process the data received
+ * ...
+ * </PRE>
+ * </CODE>
+ * </P>
+ * <A name="description"><H2>Description</H2></A> <A href="#note">Please see the note on
+ * client/server roles before using this class</A>
+ * <P>
+ * This class provides a named pipe IPC primitive. These are sometimes referred to as
+ * FIFOs or message queues.
+ * </P>
+ * <P>
+ * In this context a named pipe provides a one-way, synchronous communications channel
+ * between two processes. Note that this class only allows one-way communications even if
+ * the underlying mechanism the operating system supports is two-way.
+ * </P>
+ * <P>
+ * The class uses file naming <A href="../package-summary.html#file_naming"> as explained
+ * in the package description.</A> In the case of this class, the file name will refer to
+ * an operating system file. On Linux, this will be the name of the FIFO that is a file in
+ * the file system that implements the pipe. On windows, this will be a file that contains
+ * the actual name of the pipe.
+ * </P>
+ * <P>
+ * Before using an instance of the class, the {@link #openReader()} or
+ * {@link #openWriter()} method should be used to establish the role it will be taking
+ * when communicating. Attempting to use an instance without properly initializing may
+ * result in unpredictable behavior.
+ * </P>
+ * <P>
+ * Once the role has been established the {@link #read(byte[])},
+ * {@link #read(byte[], int, int)}, {@link #write(byte[])} and
+ * {@link #write(byte[], int, int)} methods, as appropriate, can be used to send or
+ * receive information.
+ * </P>
+ * <A name="virtual_file"><H2>Virtual File Name</H3></A>
+ * <P>
+ * This class uses virtual and
+ * actual files to implement the underlying primitive. On platforms such as Linux, the
+ * virtual and actual file names are the same and both refer to a special file on the
+ * system that represents the communications channel.
+ * </P>
+ * <P>
+ * In the case of Windows, named pipes must use a special file name of the form: {@code
+ * \\.\pipe\<name>}. Such file names cannot be used on Linux.
+ * </P>
+ * <P>
+ * In order to provide a reasonably platform independent solution, the Windows
+ * implementation of named pipes uses two file names for the pipe: a virtual name name and
+ * an actual name.
+ * </P>
+ * <P>
+ * The virtual name is basically any valid file name on the system. The file identified in
+ * the virtual name corresponds to an actual file in the system, which contains the actual
+ * name of the named pipe.
+ * </P>
+ * <P>
+ * The actual name is then used to identify the named pipe to Windows.
+ * </P>
+ *
+ * @author Clark N. Hobbie
+ */
+public class FIFO
+{
+ public enum BlockingMode
+ {
+ Blocking,
+ NonBlocking;
+
+ public static BlockingMode toValueIgnoreCase(String s)
+ {
+ return (BlockingMode) Utils.toValueIgnoreCase(values(), s);
+ }
+ }
+
+
+ public static final int DEFAULT_BUFFER_SIZE = 1024;
+
+ private BlockingMode myBlockingMode;
+
+ public BlockingMode getBlockingMode()
+ {
+ return myBlockingMode;
+ }
+
+ public void setBlockingMode(BlockingMode blockingMode)
+ {
+ myBlockingMode = blockingMode;
+ }
+
+ private PipeDirection myDirection;
+
+
+ private int myTimeoutMsec;
+
+ private FIFOImpl myImpl;
+
+
+ public int getTimeoutMsec()
+ {
+ return myTimeoutMsec;
+ }
+
+ public void setTimeoutMsec(int timeout)
+ {
+ myTimeoutMsec = timeout;
+ }
+
+
+ protected FIFOImpl getImpl()
+ {
+ return myImpl;
+ }
+
+ protected void setImpl(FIFOImpl impl)
+ {
+ myImpl = impl;
+ }
+
+ /**
+ * Create a named pipe with the specified virtual name.
+ *
+ * @param virtualName
+ * The file to be used for the named pipe. See the class description for
+ * details about <A href="#virtual_file"> how and why the virtual file name is
+ * used. </A>
+ */
+ public FIFO(String virtualName)
+ {
+ initialize(virtualName, DEFAULT_BUFFER_SIZE, -1);
+ }
+
+ public FIFO(String virtualName, int timeoutMsec)
+ {
+ initialize(virtualName, DEFAULT_BUFFER_SIZE, timeoutMsec);
+ }
+
+ protected void initialize(String virtualName)
+ {
+ initialize(virtualName, DEFAULT_BUFFER_SIZE, -1);
+ }
+
+ private void initialize(String virtualName, int bufferSize, int timeoutMsec)
+ {
+ FIFOImpl impl = new FIFOImpl(virtualName);
+ setTimeoutMsec(timeoutMsec);
+ impl.setBufferSize(bufferSize);
+ setImpl(impl);
+ }
+
+ public void create() throws IPCException
+ {
+ FIFOResult result = getImpl().create();
+ processCreateResult(result);
+ }
+
+ private void processCreateResult(FIFOResult result) throws IPCException
+ {
+ if (FIFOResult.SUCCESS != result.resultCode)
+ {
+ String msg = null;
+
+ switch (result.resultCode)
+ {
+ case FIFOResult.ERROR_ACCESS_DENIED:
+ msg = "access to the FIFO was denied";
+ break;
+
+ case FIFOResult.ERROR_UNKNOWN:
+ default:
+ msg = "An unknown error was encountered while trying to create the named pipe";
+ break;
+ }
+
+ throw new IPCException(msg);
+ }
+ }
+
+ protected String readActualName(String virtualName) throws IPCException
+ {
+ try
+ {
+ String s = Utils.readFile(virtualName);
+ return s;
+ }
+ catch (IOException e)
+ {
+ String msg = "An error was encountered while trying to read the actual "
+ + "name of a named pipe from its virtual name.";
+
+ throw new IPCException(msg, e);
+ }
+ }
+
+ /**
+ * Open the named pipe, taking on the role of reader or writer.
+ * <P>
+ * On Linux, users of named pipes need to either read or write to a named pipe, they
+ * should not do both. Therefore this method establishes how the client is going to
+ * use the pipe and then connects to it.
+ * </P>
+ *
+ * @param direction
+ * The role the client wants to take on: reader or writer.
+ * @throws IPCException
+ * If a problem is encountered while trying to connect to the pipe.
+ */
+ public void open(PipeDirection direction) throws IPCException
+ {
+ FIFOResult result = getImpl().open(direction);
+ processOpenResult(result);
+ }
+
+ private void processOpenResult(FIFOResult result) throws IPCException
+ {
+ if (FIFOResult.SUCCESS != result.resultCode)
+ {
+ switch (result.resultCode)
+ {
+ case FIFOResult.ERROR_INVALID_HANDLE:
+ {
+ String msg = "Attempt to listen to an unconnected named pipe. "
+ + "Error code = " + result.errorCode;
+
+ throw new IPCException(msg);
+ }
+
+ default:
+ {
+ String msg = "An unknown error was encountered while trying to listen "
+ + "for a client. " + "Error code = " + result.errorCode;
+
+ throw new IPCException(msg);
+ }
+ }
+ }
+ }
+
+ /**
+ * Connect to the named pipe, taking on the role of writer.
+ * <P>
+ * This method is equivalent to calling {@link #open(PipeDirection)} with an argument
+ * of {@link PipeDirection#Writer}.
+ * </P>
+ *
+ * @throws IPCException
+ * If a problem is encountered while trying to connect to the pipe.
+ * @see #open(PipeDirection)
+ */
+ public void openWriter() throws IPCException
+ {
+ setDirection(PipeDirection.Writer);
+ open(PipeDirection.Writer);
+ }
+
+ /**
+ * Connect to the named pipe, taking on the role of reader.
+ * <P>
+ * This method is equivalent to calling {@link #open(PipeDirection)} with an argument
+ * of {@link PipeDirection#Reader}.
+ * </P>
+ *
+ * @throws IPCException
+ * If a problem is encountered while trying to connect to the pipe.
+ * @see #open(PipeDirection)
+ */
+ public void openReader() throws IPCException
+ {
+ setDirection(PipeDirection.Reader);
+ open(PipeDirection.Reader);
+ }
+
+ protected PipeDirection getDirection()
+ {
+ return myDirection;
+ }
+
+ protected void setDirection(PipeDirection direction)
+ {
+ myDirection = direction;
+ }
+
+ protected String getActualName()
+ {
+ return getImpl().getActualName();
+ }
+
+ /**
+ * Read from the named pipe, blocking until data becomes available.
+ * <P>
+ * This method is equivalent to calling {@linkplain #read(byte[], int, int)
+ * read(buffer, 0, buff.length)}. See that class for additional details.
+ * </P>
+ *
+ * @param buffer
+ * The buffer where the data read from the pipe should be placed.
+ * @return The number of bytes actually read.
+ * @throws IPCException
+ * If a problem is encountered while reading from the named pipe.
+ * @see #read(byte[], int, int)
+ */
+ public int read(byte[] buffer) throws IPCException
+ {
+ return read(buffer, 0, buffer.length);
+ }
+
+ /**
+ * Read some data from the named pipe, blocking if the data is not available.
+ * <P>
+ * This method will block if data is not available when it is called. The method may
+ * read less than the specified amount of data.
+ * </P>
+ * <P>
+ * It is an error on some platforms to read from a pipe that has been opened for
+ * writing.
+ * </P>
+ *
+ * @param buffer
+ * The buffer where the data read should be placed.
+ * @param offset
+ * Where the data should start in the buffer.
+ * @param length
+ * The size of the buffer, in bytes.
+ * @return The number of bytes read.
+ * @throws IPCException
+ * If a problem is encountered while trying to read from the pipe.
+ */
+ public int read(byte[] buffer, int offset, int length) throws IPCException
+ {
+ return getImpl().read(buffer, offset, length);
+ }
+
+ /**
+ * Write some data to the named pipe.
+ * <P>
+ * This method is equivalent to calling {@linkplain #write(byte[], int, int)
+ * write(buffer, 0, buffer.length)}. Please see that method for additional details.
+ * </P>
+ *
+ * @param buffer
+ * The buffer that contains the data.
+ * @return The number of bytes actually written.
+ * @throws IPCException
+ * If a problem is encountered while writing the data.
+ */
+ public int write(byte[] buffer) throws IPCException
+ {
+ return write(buffer, 0, buffer.length);
+ }
+
+ /**
+ * Write some data to the named pipe.
+ * <P>
+ * This method writes data to the named pipe and will block if the pipe is currently
+ * full.
+ * </P>
+ * <P>
+ * It is an error to write to a named pipe that the client opened as a reader.
+ * </P>
+ * <P>
+ * The method may perform a partial write, in which case only some of the data in
+ * the buffer was actually written. In that case, the return value will be
+ * less than the length parameter.
+ * </P>
+ *
+ * @param buffer
+ * The buffer that contains the data to write.
+ * @param offset
+ * Where in the buffer that the data starts.
+ * @param length
+ * The number of bytes of data to write.
+ * @return The number of bytes of data that were actually written.
+ * @throws IPCException
+ * If a problem is encountered while trying to write the data.
+ */
+ public int write(byte[] buffer, int offset, int length) throws IPCException
+ {
+ return getImpl().write(buffer, offset, length, getTimeoutMsec());
+ }
+
+
+
+ public InputStream getInputStream(int timeoutMsec) throws IOException
+ {
+ try
+ {
+ open(PipeDirection.Reader);
+ return new FIFOInputStream(this, timeoutMsec);
+ }
+ catch (IPCException e)
+ {
+ throw new IOException("Error opening pipe", e);
+ }
+ }
+
+
+ public InputStream getInputStream() throws IOException
+ {
+ return getInputStream(-1);
+ }
+
+ public OutputStream getOutputStream() throws IOException
+ {
+ try
+ {
+ open(PipeDirection.Writer);
+ return new FIFOOutputStream(this);
+ }
+ catch (IPCException e)
+ {
+ throw new IOException("Error opening pipe", e);
+ }
+ }
+
+ public String getVirtualName()
+ {
+ return getImpl().getVirtualName();
+ }
+
+ public native void selectForWriting(int timeoutMsec);
+}

Back to the top