/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.api.admin;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.Iterator;
import java.util.Properties;
/**
* Interface for admin command payloads--data carried
* in the http request and response which flow between the admin client and the server.
* This API also allows the requester to ask that the receiver remove a file,
* presumably one that was transferred via an earlier request payload.
* <h2>Motivation and Overview</h2>
* The API is intended to be a simple abstraction of the
* input and output streams in HTTP requests and responses,
* inspired by the concepts from the mail API for
* MIME body parts and multiparts.
* A payload can contain zero or more parts.
* If a payload contains only one part then only that part is sent or received
* in the payload. If, on the other hand, the payload contains multiple parts
* then the payload as a whole is a "multipart" payload which in turn contains
* the individual parts.
* <h2>Usage</h2>
* <h3>Outbound</h3>
* Code (on the client or the server) that needs to place data in the payload or
* request that earlier-sent data be removed
* would instantiate an implementation of {@link Payload.Outbound}. (The
* {@link org.glassfish.admin.payload.PayloadImpl.Outbound} class is such an implementation.)
* To this Payload.Outbound object the code can add parts using any of the {@link Outbound#addPart}
* methods or the {@link Outbound#attachFile} or {@link Outbound#requestFileRemoval} convenience methods.
* After the code has added all
* the relevant parts to the payload it invokes the {@link Outbound#writeTo} method to write
* the outbound payload to an OutputStream. Typically the caller will pass the
* output stream associated with the request (in the client case) or the response (in the
* server case).
* <h3>Inbound</h3>
* Code that needs to read the payload from a request (as the server does) or
* from a response (as the client does) will instantiate an implementation of
* {@link Payload.Inbound}. (The {@link org.glassfish.admin.payload.PayloadImpl.Inbound}
* is such an implementation.
* <p>
* Payload.Inbound exposes the {@link Payload.Inbound#parts()} method
* which returns an Iterator&lt;Part&gt;. With each {@link Part} available through the
* iterator the caller can use {@link Part#getContentType()},
* {@link Part#getName()}, and {@link Part#getInputStream()}.
* Note that the caller should close the InputStream returned by getInputStream()
* once it has read the data from the stream.
* <h2>Part Properties</h2>
* Each Part can carry with it Properties. The conventions as to what property
* names are used and what they mean can vary from one admin command to the next.
* For a file transfer these properties should be set:
* <ul>
* <li>data-request-type=file-xfer
* <li>data-request-name=(anything - typically the option name from the command to
* help identify which part goes with which command argument)
* <li>file-xfer-root=root path expression in the file system syntax of the system
* where tranferred files will reside.
* <li>last-modified=String representation of the long value recording the last-modified time of the original file
* </ul>
* As an example as the server processes the "--retrieve xxx" option on the deploy and redeploy commands
* it will specify file-xfer-root as xxx (in this example) to indicate where the
* downloaded app client files should be stored on the
* local system. Placing such information in the Part's properties helps to
* decouple the admin client from the specifics of the command which triggered
* the file transfer. That is, for example, the admin client does not need to
* fetch the "--retrieve" option value to know where to store the transferred files
* because the server placed this information with each part. The admin client
* can therefore process this type of admin data request generically, without
* regard to what command triggered the transfer.
* <p>
* The receiver of the file transfer request should return the
* file-xfer-root value back to the requester without change so the local file path
* syntax can be used correctly on the system where the file will exist.
* <h2>Notes and Restrictions</h2>
* In some implementations the caller must consume the contents of a
* Part's input stream before advancing to the next Part. All callers should
* follow this practice regardless of the underlying implementation used, in case
* that implementation were to change in the future.
*
* @author tjquinn
*/
public interface Payload {
public static final String PAYLOAD_HEADER_NAME = "org.glassfish.payload.admin.data";
/**
* Public API for outbound Payloads.
*/
public static interface Outbound {
/**
* Adds a part of the specified content type, name, and String content
* to the payload.
* @param contentType content type of the part
* @param name name to be assigned to the part
* @param props Properties to be included with the part
* @param content String containing the content for the part
* @throws java.io.IOException
*/
public void addPart(
final String contentType,
final String name,
final Properties props,
final String content) throws IOException;
/**
* Adds a part of the specified content type, name, and content to
* the payload.
* @param contentType content type of the part
* @param name name to be assigned to the part
* @param props Properties to be included with the part
* @param content InputStream furnishing the content for this part
* @throws java.io.IOException
*/
public void addPart(
final String contentType,
final String name,
final Properties props,
final InputStream content) throws IOException;
/**
* Adds a part of the specified content type, name, and content at a
* specified position in the parts of the payload.
* @param index position (zero-based) where the part should be added
* @param contentType content type of the part
* @param name name to be assigned to thepart
* @param props Properties to be included with the part
* @param content InputStream furnishing the content for this part
* @throws java.io.IOException
*/
public void addPart(
final int index,
final String contentType,
final String name,
final Properties props,
final InputStream content
) throws IOException;
/**
* Adds a part to the payload of the given content type from the
* specified file. The name assigned to the new part is the URI
* for the file relativized according to the specified file URI. The
* properties which indicate that this is file transfer data request
* are set automatically.
* <p>
* If the <code>file</code> argument specifies a directory, only the
* directory - not its contents - are attached to the payload. To
* include the directory and its contents use {@link #attachFile(java.lang.String, java.net.URI, java.lang.String, java.io.File, boolean) }
* and specify the <code>recursive</code> argument as <code>true</code>.
* @param contentType content type of the part
* @param fileURI URI relative to which the part's name should be computed
* @param dataRequestName name identifying which part of a request this file answers
* @param file File containing the content for the part
* @throws java.io.IOException
*/
public void attachFile(
final String contentType,
final URI fileURI,
final String dataRequestName,
final File file) throws IOException;
/**
* Adds a part to the payload of the given content type from the
* specified file. The name assigned to the new part is the URI
* for the file relativized according to the specified file URI. The
* properties which indicate that this is file transfer data request
* are set automatically.
* @param contentType content type of the part
* @param fileURI URI relative to which the part's name should be computed
* @param dataRequestName name identifying which part of a request this file answers
* @param file File containing the content for the part
* @param isRecursive if file is a directory, whether to add its contents as well
* @throws java.io.IOException
*/
public void attachFile(
final String contentType,
final URI fileURI,
final String dataRequestName,
final File file,
final boolean isRecursive) throws IOException;
/**
* Adds a part to the payload of the given content type from the
* specified file. The name assigned to the new part is the URI
* for the file relativized according to the specified file URI. The
* properties which indicate that this is a file transfer data request
* are set automatically and added to the properties passed by the caller.
* @param contentType content type of the part
* @param fileURI URI relative to which the part's name should be computed
* @param dataRequestName name identifying which part of a request this file answers
* @param props Properties to be included with the part
* @param file File containing the content for the part
* @throws java.io.IOException
*/
public void attachFile(
final String contentType,
final URI fileURI,
final String dataRequestName,
final Properties props,
final File file) throws IOException;
/**
* Adds a part to the payload of the given content type from the
* specified file. The name assigned to the new part is the URI
* for the file relativized according to the specified file URI. The
* properties which indicate that this is a file transfer data request
* are set automatically and added to the properties passed by the caller.
* @param contentType content type of the part
* @param fileURI URI relative to which the part's name should be computed
* @param dataRequestName name identifying which part of a request this file answers
* @param props Properties to be included with the part
* @param file File containing the content for the part
* @param isRecursive if file is a directory, whether to add its contents as well
* @throws java.io.IOException
*/
public void attachFile(
final String contentType,
final URI fileURI,
final String dataRequestName,
final Properties props,
final File file,
final boolean isRecursive) throws IOException;
/**
* Adds a part to the payload that represents a request to remove the
* specified file, presumably previously transferred in a payload
* during an earlier request.
* @param fileURI relative URI of the file for deletion
* @param dataRequestName name identifying which part of a request triggered the file removal
* @param props Properties to be included with the part
* @throws IOException
*/
public void requestFileRemoval(
final URI fileURI,
final String dataRequestName,
final Properties props) throws IOException;
/**
* Adds a part to the payload that represents a request to remove the
* specified file, presumably previously transferred in a payload
* during an earlier request.
* @param fileURI relative URI of the file for deletion
* @param dataRequestName name identifying which part of a request triggered the file removal
* @param props Properties to be included with the part
* @param isRecursive if fileURI is a directory, whether to remove its contents as well
* @throws IOException
*/
public void requestFileRemoval(
final URI fileURI,
final String dataRequestName,
final Properties props,
final boolean isRecursive) throws IOException;
/**
* Adds a part to the payload to request that the specified file be
* replaced.
* <p>
* If the fileURI translates to a non-directory file on the receiving
* system then calling this method will replace the file's contents
* on the target with the contents of the <code>file</code> argument.
* <p>
* If the fileURI is for a directory, then if isRecursive is also
* specified the payload will contain one Part to replace the
* directory (which will have the result of removing the directory
* and its contents and then recreating the directory) plus a Part for
* each file, including subdirectories, below the directory. The
* intent is to replace the entire directory with new contents.
* @param fileURI
* @param dataRequestName
* @param props
* @param isRecursive
* @throws IOException
*/
public void requestFileReplacement(
final String contentType,
final URI fileURI,
final String dataRequestName,
final Properties props,
final File file,
final boolean isRecursive) throws IOException;
/**
* Writes the parts already added to the payload to the specified
* OutputStream.
* @param os OutputStream to receive the formatted payload
* @throws java.io.IOException
*/
public void writeTo(final OutputStream os) throws IOException;
/**
* Returns the content type of the payload, determined by whether there
* are multiple parts and, if not, if the content type of the single
* part is of type "text."
* @return the content type of the pauload
*/
public String getContentType();
/**
* Returns the name of the header that should be set in the outgoing and
* incoming http request or response.
* @return the header name
*/
public String getHeaderName();
}
/**
* Public API for inbound payloads.
*/
public static interface Inbound {
/**
* Returns the parts from the inbound payload.
* @return Iterator over the inbound Parts
*/
public Iterator<Part> parts();
/**
* Returns the name of the header that should be set in the outgoing and
* incoming http request or response.
* @return the header name
*/
public String getHeaderName();
}
/**
* Public API for the payload Part.
*/
public static interface Part {
/**
* Returns the name assigned to the part when it was created.
* @return name
*/
public String getName();
/**
* Returns the content type of the part.
* @return content type
*/
public String getContentType();
/**
* Returns the Properties associated with the Part.
* @return Properties for the Part
*/
public Properties getProperties();
/**
* Returns an InputStream suitable for reading the content of the Part.
* @return
*/
public InputStream getInputStream();
/**
* Copies the contents of the Part to the specified OutputStream.
* @param os target OutputStream to receive the content of the Part
* @throws java.io.IOException
*/
public void copy(final OutputStream os) throws IOException;
/**
* Indicates if the Part represents a recursive action or not.
* @return
*/
public boolean isRecursive();
}
}