/******************************************************************************* * Copyright (c) 2009 MATERNA Information & Communications. 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. For further * project-related information visit http://www.ws4d.org. The most recent * version of the JMEDS framework can be obtained from * http://sourceforge.net/projects/ws4d-javame. ******************************************************************************/ package org.ws4d.java.attachment; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.ws4d.java.attachment.interfaces.outgoing.OutgoingOutputStreamAttachment; import org.ws4d.java.constants.HTTPConstants; import org.ws4d.java.types.InternetMediaType; import org.ws4d.java.util.IDGenerator; import org.ws4d.java.util.Log; class OutputStreamAttachment extends AbstractAttachment implements IncomingAttachment, OutgoingOutputStreamAttachment { private StreamAttachmentOutputStream out = new StreamAttachmentOutputStream(); OutputStreamAttachment(InternetMediaType contentType) { this(IDGenerator.getUUID(), contentType); } OutputStreamAttachment(String contentType) { this(IDGenerator.getUUID(), maskContentType(contentType)); } OutputStreamAttachment(String contentId, InternetMediaType contentType) { this(contentId, contentType, HTTPConstants.HTTP_HEADERVALUE_TRANSFERENCODING_BINARY); } OutputStreamAttachment(String contentId, String contentType) { this(contentId, maskContentType(contentType), HTTPConstants.HTTP_HEADERVALUE_TRANSFERENCODING_BINARY); } OutputStreamAttachment(String contentId, InternetMediaType contentType, String transferEncoding) { super(contentId, contentType, transferEncoding); } /* * (non-Javadoc) * @see org.ws4d.java.attachment.Attachment#getType() */ public int getType() throws AttachmentException { return OUTPUTSTREAM_ATTACHMENT; } /* * (non-Javadoc) * @see org.ws4d.java.attachment.Attachment#getBytes() */ public byte[] getBytes() throws AttachmentException, IOException { throw new AttachmentException("This attachment does not allow to read bytes. No byte array available."); } /* * (non-Javadoc) * @see org.ws4d.java.attachment.Attachment#getInputStream() */ public InputStream getInputStream() throws AttachmentException, IOException { throw new AttachmentException("This attachment does not allow to read bytes. No input stream available."); } /* * (non-Javadoc) * @see org.ws4d.java.attachment.Attachment#size() */ public long size() throws AttachmentException { throw new AttachmentException("This attachment does not allow to read bytes. No size available."); } /* * (non-Javadoc) * @see org.ws4d.java.attachment.Attachment#isLocal() */ public boolean isLocal() { return true; } /* * (non-Javadoc) * @see org.ws4d.java.attachment.Attachment#getFilePath() */ public String getFilePath() throws AttachmentException { throw new AttachmentException("file system operations not supported for stream attachments"); } public void save(String targetFilePath) throws AttachmentException, IOException { throw new AttachmentException("file system operations not supported for stream attachments"); } public boolean move(String newFilePath) throws AttachmentException { throw new AttachmentException("file system operations not supported for stream attachments"); } /* * (non-Javadoc) * @see org.ws4d.java.attachment.Attachment#dispose() */ public void dispose() { if (out == null) { return; } try { out.close(); } catch (IOException e) { Log.warn("Unable to close attachment output stream on dispose: " + e); Log.printStackTrace(e); } out = null; } public void serialize(OutputStream out) throws IOException { if (this.out == null) { throw new IOException("Cannot serialize because this OutputStreamAttachment has already been disposed."); } this.out.setOutputStream(out); synchronized (this.out) { while (this.out != null && this.out.isWriteable()) { try { /* * Do not serialize the rest of the other stuff until the * returned output stream is closed. */ this.out.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * Returns an ouput stream which allows to write the stream data. * <p> * The stream MUST be closed if the streaming ends and the communication * should continue correctly! * </p> * * @return the output stream. */ public synchronized OutputStream getOutputStream() { return out; } public class StreamAttachmentOutputStream extends OutputStream { private volatile OutputStream out = null; private volatile boolean closed = false; /** * Checks if this StreamAttachmentOutputStream is already connected to * its underlying OutputStream. * * @return true if the underlying OutputStream is available */ public synchronized boolean isWriteable() { return out != null; } private void waitForOut() throws IOException { while (out == null && !closed) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } if (closed) throw new IOException("Cannot write because this StreamAttachmentOutputStream has already been closed."); } /* * (non-Javadoc) * @see java.io.OutputStream#write(int) */ public synchronized void write(int b) throws IOException { waitForOut(); out.write(b); } /* * (non-Javadoc) * @see java.io.OutputStream#write(byte[]) */ public synchronized void write(byte[] b) throws IOException { waitForOut(); out.write(b); } /* * (non-Javadoc) * @see java.io.OutputStream#write(byte[], int, int) */ public synchronized void write(byte[] b, int off, int len) throws IOException { waitForOut(); out.write(b, off, len); } /* * (non-Javadoc) * @see java.io.OutputStream#close() */ public synchronized void close() throws IOException { if (closed) { return; } closed = true; /* * Just flush. We cannot decide whether we should close this output * or not. */ if (out != null) { out.flush(); out = null; } notifyAll(); } public synchronized void flush() throws IOException { if (closed) throw new IOException("Cannot flush because this StreamAttachmentOutputStream has already been closed."); if (out == null) { return; } out.flush(); } /** * Set the real output stream and notify the waiting write thread. * * @param out */ synchronized void setOutputStream(OutputStream out) { if (!closed) { this.out = out; } /* * Notify the waiting thread about the stream serialization. */ notifyAll(); } } }