/******************************************************************************* * 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.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.ws4d.java.DPWSFramework; import org.ws4d.java.attachment.interfaces.incoming.IncomingFileAttachment; import org.ws4d.java.configuration.AttachmentProperties; import org.ws4d.java.io.fs.FileSystem; import org.ws4d.java.service.parameter.ParameterValue; import org.ws4d.java.types.InternetMediaType; import org.ws4d.java.util.IDGenerator; import org.ws4d.java.util.Log; /** * This class is used when sending a file as an attachment given its path within * the file system. The raw attachment data can be always accessed by means of * {@link #getInputStream()}. On each call, this method will return a newly * created stream to the file in the local file system. Accordingly, the method * {@link #size()} will return the exact file size as reported by the file * system. Finally, {@link #getBytes()} will attempt to read up to * {@link AttachmentProperties#getMaxMemBufferSize()} bytes from the file and * return a new byte array from that. If the file size is however greater than * the configured limit, this method will throw an {@link AttachmentException}. * <p> * Beware when using the {@link #move(String)} method - it could be that this * attachment was received locally (i.e. from a client or service residing * within the same Java virtual machine) which would then result in moving the * original file! To prevent this, it is strongly recommended to check whether * the file referred to was initially local by means of the {@link #isLocal()} * method. * </p> * <p> * In order to support easy and fast type checking, method {@link #getType()} * will always return {@link #FILE_ATTACHMENT} for instances of this class. * </p> * <p> * This class may be used only on platforms including file system support (e.g. * Java Standard Edition). * </p> */ class FileAttachment extends AbstractAttachment implements IncomingFileAttachment, OutgoingAttachment { private static final FileSystem FS; private static final OutputStream NIRVANA = new OutputStream() { /* * (non-Javadoc) * @seejava.io. OutputStream * #write(int) */ public void write(int b) throws IOException { // void } /* * (non-Javadoc) * @seejava.io. OutputStream * #write (byte[]) */ public void write(byte[] b) throws IOException { // void } /* * (non-Javadoc) * @seejava.io. OutputStream * #write (byte[], int, int) */ public void write(byte[] b, int off, int len) throws IOException { // void } }; static { FileSystem fs; try { fs = DPWSFramework.getLocalFileSystem(); } catch (IOException e) { Log.error("No local file system available, file attachments cannot be used: " + e); Log.printStackTrace(e); fs = new FileSystem() { /* * (non-Javadoc) * @see * org.ws4d.java.io.fs.FileSystem#writeFile(java.lang.String) */ public OutputStream writeFile(String filePath) throws IOException { return NIRVANA; } /* * (non-Javadoc) * @see * org.ws4d.java.io.fs.FileSystem#renameFile(java.lang.String, * java.lang.String) */ public boolean renameFile(String filePath, String newFilePath) { return true; } /* * (non-Javadoc) * @see * org.ws4d.java.io.fs.FileSystem#readFile(java.lang.String) */ public InputStream readFile(String filePath) throws IOException { return EMPTY_STREAM; } /* * (non-Javadoc) * @see * org.ws4d.java.io.fs.FileSystem#listFiles(java.lang.String) */ public String[] listFiles(String dirPath) { return EMPTY_STRING_ARRAY; } /* * (non-Javadoc) * @see org.ws4d.java.io.fs.FileSystem#fileSeparator() */ public String fileSeparator() { return ""; } /* * (non-Javadoc) * @see * org.ws4d.java.io.fs.FileSystem#escapeFileName(java.lang.String * ) */ public String escapeFileName(String rawFileName) { return rawFileName; } /* * (non-Javadoc) * @see * org.ws4d.java.io.fs.FileSystem#deleteFile(java.lang.String) */ public boolean deleteFile(String filePath) { return true; } /* * (non-Javadoc) * @see * org.ws4d.java.io.fs.FileSystem#fileSize(java.lang.String) */ public long fileSize(String filePath) { return 0; } /* * (non-Javadoc) * @see * org.ws4d.java.io.fs.FileSystem#fileExists(java.lang.String) */ public boolean fileExists(String filePath) { return true; } public long lastModified(String filePath) { return 0; } }; } FS = fs; } private String filePath; private boolean local; /** * Creates an attachment from the file with the given <code>filePath</code> * within the local file system. A unique {@link #getContentId() content ID} * for this attachment is automatically generated. Its * {@link #getContentType() content type} will be unspecified. * * @param filePath the path to the file to create an attachment from */ FileAttachment(String filePath) { this(filePath, (InternetMediaType) null); } /** * Creates an attachment from the file with the given <code>filePath</code> * within the local file system. A unique {@link #getContentId() content ID} * for this attachment is automatically generated. * * @param filePath the path to the file to create an attachment from * @param contentType the MIME content type of the file */ FileAttachment(String filePath, InternetMediaType contentType) { this(filePath, IDGenerator.getUUID(), contentType); } /** * Creates an attachment from the file with the given <code>filePath</code> * within the local file system. A unique {@link #getContentId() content ID} * for this attachment is automatically generated. * * @param filePath the path to the file to create an attachment from * @param contentType the MIME content type of the file */ FileAttachment(String filePath, String contentType) { this(filePath, IDGenerator.getUUID(), contentType); } /** * Creates an attachment from the file with the given <code>filePath</code> * within the local file system and assigns the specified * <code>contentId</code> to it. * * @param filePath the path to the file from which to create an attachment * @param contentId the MIME content ID of the attachment, which should be * unique within the scope of the MIME package in which the * attachment is contained; in the case of DPWS this scope * corresponds to a single invocation message, i.e. the content * ID must be unique within the {@link ParameterValue} hierarchy * of an operations input or output parameters * @param contentType the MIME content type of the file */ FileAttachment(String filePath, String contentId, InternetMediaType contentType) { super(contentId, contentType); this.filePath = filePath; this.local = true; } /** * Creates an attachment from the file with the given <code>filePath</code> * within the local file system and assigns the specified * <code>contentId</code> to it. * * @param filePath the path to the file from which to create an attachment * @param contentId the MIME content ID of the attachment, which should be * unique within the scope of the MIME package the attachment is * contained in; in the case of DPWS this scope corresponds to a * single invocation message, i.e. the content ID must be unique * within the {@link ParameterValue} hierarchy of an operations * input or output parameters * @param contentType the MIME content type of the file */ FileAttachment(String filePath, String contentId, String contentType) { this(filePath, contentId, maskContentType(contentType)); } /* * Creates an attachment from the file with the given <code>filePath</code> * within the local file system. If the argument <code>local</code> is * <code>false</code>, it is assumed that this file attachment was created * by class DefaultAttachmentStore, so that it is safe to delete the file it * points to when method dispose() is invoked. */ FileAttachment(String filePath, boolean local) { super((InternetMediaType) null); this.filePath = filePath; this.local = local; } /** * Always returns {@link #FILE_ATTACHMENT}. */ public final int getType() throws AttachmentException { return FILE_ATTACHMENT; } /** * Creates and returns a new input stream to the file to which this * attachment refers to on each call. */ public InputStream getInputStream() throws AttachmentException, IOException { if (readInException != null) { throw readInException; } return FS.readFile(filePath); } /** * Returns the size of the file constituting this attachment's raw data. */ public long size() throws AttachmentException { if (readInException != null) { throw readInException; } return FS.fileSize(filePath); } /** * Returns the raw data of this attachment as a byte array, as long as its * {@link #size() size} is less than or equal to the currently configured * {@link AttachmentProperties#getMaxMemBufferSize() maximum in-memory * attachment size}. Throws an {@link AttachmentException} otherwise. */ public byte[] getBytes() throws AttachmentException, IOException { if (readInException != null) { throw readInException; } // support this method only up to a limited amount of bytes ByteArrayOutputStream out = new ByteArrayOutputStream(); DefaultAttachmentStore.readOut(FS.readFile(filePath), AttachmentProperties.getInstance().getMaxMemBufferSize(), out); out.close(); return out.toByteArray(); } /* * (non-Javadoc) * @see org.ws4d.java.attachment.StreamAttachment#dispose() */ public void dispose() { if (local) { return; } try { FS.deleteFile(filePath); } catch (Exception e) { Log.warn("Unable to delete attachment file \"" + filePath + "\" on dispose: " + e); Log.printStackTrace(e); } } /* * (non-Javadoc) * @see org.ws4d.java.attachment.Attachment#serialize(java.io.OutputStream) */ public void serialize(OutputStream out) throws IOException { DefaultAttachmentStore.readOut(FS.readFile(filePath), out); } /* * (non-Javadoc) * @see org.ws4d.java.attachment.Attachment#isLocal() */ public boolean isLocal() { return local; } /* * (non-Javadoc) * @see org.ws4d.java.attachment.Attachment#getFilePath() */ public String getFilePath() throws AttachmentException { if (readInException != null) { throw readInException; } return filePath; } /* * (non-Javadoc) * @see org.ws4d.java.attachment.Attachment#move(java.lang.String) */ public boolean move(String newFilePath) throws AttachmentException { if (readInException != null) { throw readInException; } boolean result = FS.renameFile(filePath, newFilePath); // store new path this.filePath = newFilePath; this.local = true; return result; } /* * (non-Javadoc) * @see org.ws4d.java.attachment.Attachment#save(java.lang.String) */ public void save(String targetFilePath) throws AttachmentException, IOException { if (readInException != null) { throw readInException; } InputStream in = FS.readFile(filePath); OutputStream out = FS.writeFile(targetFilePath); DefaultAttachmentStore.readOut(in, out); out.flush(); out.close(); } }