Java tutorial
/* Adobe Systems Incorporated(r) Source Code License Agreement Copyright(c) 2007 Adobe Systems Incorporated. All rights reserved. Please read this Source Code License Agreement carefully before using the source code. Use of the APIs is governed by the Share Beta Services Agreement: https://share.acrobat.com/adc/tou.do. Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license, to reproduce, prepare derivative works of, publicly display, publicly perform, and distribute this source code and such derivative works in source or object code form without any attribution requirements. The name "Adobe Systems Incorporated" must not be used to endorse or promote products derived from the source code without prior written permission. You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and against any loss, damage, claims or lawsuits, including attorney's fees that arise or result from your use or distribution of the source code. THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL ADOBE OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOURCE CODE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.adobe.share.api; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Date; import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.methods.DeleteMethod; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.HeadMethod; import org.apache.commons.httpclient.methods.InputStreamRequestEntity; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.PutMethod; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.commons.httpclient.methods.multipart.FilePart; import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity; import org.apache.commons.httpclient.methods.multipart.Part; import org.apache.commons.httpclient.methods.multipart.StringPart; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.json.XML; /** * ShareAPI is the helper class for accessing the Adobe <a * href="http://api.share.acrobat.com">Share API</a>. It provides much of the * client-side functionality for applications; for example, logging in and out, * uploading files, sharing files, and so on. While many of these functions call * functions in the ShareAPINode and ShareAPIUser classes, most of the work is * done here. These functions build the requests with the appropriate XML * payload and parse the responses. * * @author Patrick Rodriguez (parodrig@adobe.com) * @version 1.0, October 2007 * */ public class ShareAPI { /** The Constant logger. */ private static final Log LOGGER = LogFactory.getLog(ShareAPI.class); /** * DEFAULT_ENDPOINT is the base URL for Share requests. The path is extended * with dc, auth, or sessions, depending on the nature of the request. */ protected static final String DEFAULT_ENDPOINT = "https://api.share.acrobat.com/webservices/api/v1/"; /** * <code>auth</code> is appended to DEFAULT_ENDPOINT for authentication * requests during login. */ protected static final String ENDPOINT_AUTH = DEFAULT_ENDPOINT + "auth/"; /** * <code>sessions</code> is appended to DEFAULT_ENDPOINT after * authentication so that a session ID can be obtained and used to track the * current session. */ protected static final String ENDPOINT_SESSIONS = DEFAULT_ENDPOINT + "sessions/"; /** * <code>dc</code> is appended to DEFAULT_ENDPOINT to manipulate the * specified node and is usually accompanied by a node ID. <pre * class="codeexample"> DELETE * https://api.share.acrobat.com/webservices/api/v1/dc/[nodeid/] POST * https:/ * /api.share.acrobat.com/webservices/api/v1/dc/[nodeid/]?method=DELETE * </pre> */ protected static final String ENDPOINT_DC = DEFAULT_ENDPOINT + "dc/"; /** The Constant STATUS_OK. */ protected static final int STATUS_OK = 200; /** The Constant BAD_REQUEST. */ protected static final int STATUS_BAD_REQUEST = 400; /** The http client. */ private HttpClient httpClient = null; /** The apikey. */ private String apikey = null; /** The secret. */ private String secret = null; /** * Share API constructor. Note that the developer must have registered with * Share and will need to have an API key and secret to continue. * * @param apikeyVal The API key registered for this application * @param secretVal The shared secret assigned to this API key */ public ShareAPI(final String apikeyVal, final String secretVal) { this.apikey = apikeyVal; this.secret = secretVal; this.httpClient = new HttpClient(); final String proxyHost = System.getProperty("https.proxyHost"); int proxyPort = -1; try { proxyPort = Integer.parseInt(System.getProperty("https.proxyPort")); } catch (NumberFormatException nfe) { LOGGER.debug("No HTTPS proxy port defined", nfe); proxyPort = -1; } if (StringUtils.isNotBlank(proxyHost) && proxyPort > 0) { this.httpClient.getHostConfiguration().setProxy(proxyHost, proxyPort); } } /** * <code>login</code> constructs a log in request so the user can login to * Share. If successful, user object will have its session ID and secret * attributes set. * * @param user the user * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final void login(final ShareAPIUser user) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { String requestBody = null; JSONObject json = null; if (user.getUsername() == null || user.getPassword() == null) { throw new IllegalArgumentException("user object must have username and password set"); } try { requestBody = "<request><username>" + XML.escape(user.getUsername()) + "</username>" + "<password>" + XML.escape(user.getPassword()) + "</password></request>"; json = parseResponse(createRequest(user, "POST", ENDPOINT_AUTH, true, requestBody)); String authtoken = json.getJSONObject("response").getString("authtoken"); requestBody = "<request><authtoken>" + XML.escape(authtoken) + "</authtoken></request>"; json = parseResponse(createRequest(user, "POST", ENDPOINT_SESSIONS, true, requestBody)); String sessionid = json.getJSONObject("response").getString("sessionid"); String secretString = json.getJSONObject("response").optString("secret"); String name = json.getJSONObject("response").optString("name"); user.setSessionid(sessionid); user.setSecret(secretString); user.setName(name); } catch (JSONException e) { throw new ShareAPIException(-1, e.getMessage()); } } /** * <code>logout</code> logs a user out by ending the current session. In * this example, the session is ended by constructing an HTTP request using * the DELETE method and the current session ID. * * @param user the user * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final void logout(final ShareAPIUser user) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { assertUserLoggedIn(user); parseResponse(createRequest(user, "DELETE", ENDPOINT_SESSIONS + user.getSessionId() + "/", false, null)); user.setSessionid(null); user.setSecret(null); } /** * Gets a list of nodes (files and folders) in a user's account. Share * returns a substantial amount of detail about each node, and * <code>getNodes</code> calls functions in ShareAPINode to parse the data. * * @param user Execute method on behalf of this user * @param parentNode List nodes that are children of this node. If this is * null, use user's root node as parent. * * @return Node with details filled in, plus list of children nodes * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception */ public final ShareAPINode getNodes(final ShareAPIUser user, final ShareAPINode parentNode) throws HttpException, IOException, ShareAPIException { assertUserLoggedIn(user); JSONObject json = null; ShareAPINode thisNode = null; try { if (parentNode != null) { assertValidNode(parentNode); json = parseResponse( createRequest(user, "GET", ENDPOINT_DC + parentNode.getId() + "/", false, null)); } else { json = parseResponse(createRequest(user, "GET", ENDPOINT_DC, false, null)); } thisNode = new ShareAPINode(json.getJSONObject("response").getJSONObject("node")); // There might not be any children if (json.getJSONObject("response").optJSONObject("children") != null) { // If only one object is returned, it is represented // as a JSONObject. Otherwise, JSONArray. JSONObject respNode = json.getJSONObject("response").getJSONObject("children") .optJSONObject("node"); JSONArray respNodes = json.getJSONObject("response").getJSONObject("children").optJSONArray("node"); if (respNode == null && respNodes == null) { return thisNode; } if (respNodes == null && respNode != null) { respNodes = (new JSONArray()).put(respNode); } for (int i = 0; i < respNodes.length(); i++) { JSONObject curNode = respNodes.getJSONObject(i); thisNode.getChildren().add(new ShareAPINode(curNode)); } } return thisNode; } catch (JSONException e) { throw new ShareAPIException(-1, e.getMessage()); } } /** * Gets the details of the requested node from the Share response. * * @param user Execute method on behalf of this user * @param node Node to get details about * * @return Node with details filled in * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final ShareAPINode getNodeDetails(final ShareAPIUser user, final ShareAPINode node) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { assertUserLoggedIn(user); JSONObject json = null; try { assertValidNode(node); json = parseResponse(createRequest(user, "GET", ENDPOINT_DC + node.getId() + "/", false, null)); JSONObject respNode = json.getJSONObject("response").optJSONObject("node"); return new ShareAPINode(respNode); } catch (JSONException e) { throw new ShareAPIException(-1, e.getMessage()); } } /** * Returns the specified rendition (either a thumbnail or the actual file) * of a document as a stream. * * @param user Execute method on behalf of this user * @param node Node to get a rendition from * @param rendition Set to "source" for source document, "thumbnail" for * document's thumbnail * * @return Stream containing requested rendition * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final InputStream getNodeContent(final ShareAPIUser user, final ShareAPINode node, final String rendition) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { assertUserLoggedIn(user); assertFileNode(node); InputStream content = null; content = parseFileResponse( createRequest(user, "GET", ENDPOINT_DC + node.getId() + "/" + rendition + "/", false, null)); return content; } /** * Returns the thumbnail of a document as a stream. Note that folders do not * have thumbnails, and not all files will have thumbnails. * * @param user Execute method on behalf of this user * @param node Node to get thumbnail from * * @return Stream containing requested thumbnail * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final InputStream getThumbnail(final ShareAPIUser user, final ShareAPINode node) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { return getNodeContent(user, node, ShareAPINode.RENDITION_THUMBNAIL); } /** * Returns the actual file contents as a stream. * * @param user Execute method on behalf of this user * @param node Node to get source from * * @return Stream containing requested source * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final InputStream getSource(final ShareAPIUser user, final ShareAPINode node) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { return getNodeContent(user, node, ShareAPINode.RENDITION_SOURCE); } /** * Returns the PDF version as a stream. * * @param user Execute method on behalf of this user * @param node Node to get source from * * @return Stream containing requested source * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final InputStream getPDF(final ShareAPIUser user, final ShareAPINode node) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { return getNodeContent(user, node, ShareAPINode.RENDITION_PDF); } /** * Deletes a file or folder node node by constructing an HTTP request using * the DELETE method and the requisite node ID. * * @param user Execute method on behalf of this user * @param node Node to delete * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final void deleteNode(final ShareAPIUser user, final ShareAPINode node) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { assertUserLoggedIn(user); assertValidNode(node); parseResponse(createRequest(user, "DELETE", ENDPOINT_DC + node.getId() + "/", false, null)); } /** * Renames a node by constructing an HTTP request using the MOVE method and * the requisite node ID as well as the new name. * * @param user Execute method on behalf of this user * @param node Node to rename * @param newName the name for the node * * @throws HttpException httpException * @throws IOException ioException * @throws ShareAPIException shareAPIException * @throws IllegalArgumentException illegalArgumentException */ public final void renameNode(final ShareAPIUser user, final ShareAPINode node, final String newName) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { assertUserLoggedIn(user); assertValidNode(node); parseResponse(createRequest(user, "MOVE", ENDPOINT_DC + node.getId() + "/?newname=" + URLEncoder.encode(newName, "UTF-8"), false, null)); } /** * Moves a node by constructing an HTTP request using the MOVE method and * the requisite originating and destination node IDs. * * @param user Execute method on behalf of this user * @param node Node to move * @param destNode Move node to be under destNode. If null, move to under * root node. * @param newName Rename node to this. If null, keep name the same. * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final void moveNode(final ShareAPIUser user, final ShareAPINode node, final ShareAPINode destNode, final String newName) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { assertUserLoggedIn(user); assertValidNode(node); if (destNode == null || destNode.getId() == null) { throw new IllegalArgumentException("Destination node ID cannot be null"); } String name = ""; if (newName == null) { name = node.getName(); } else { name = newName; } parseResponse(createRequest(user, "MOVE", ENDPOINT_DC + node.getId() + "/?destnodeid=" + destNode.getId() + "&newname=" + URLEncoder.encode(name, "UTF-8"), false, null)); } /** * Uploads and adds a file to Share by building a request with an XML * payload containing the requisite details. * * @param user Execute method on behalf of this user * @param file File to upload * @param name Upload file using this name * @param description Description of file. Set to null for none. * @param parentNode Upload file under this folder node. If null, upload to * root node. * @param withRenditions Set to true to generate renditions (thumbnail, * flash preview) for this document * @return New file node * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final ShareAPINode addFile(final ShareAPIUser user, final File file, final String name, final String description, final ShareAPINode parentNode, final boolean withRenditions) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { return addFile(user, file, name, description, parentNode, withRenditions, false); } /** * Uploads and adds a file to Share by building a request with an XML * payload containing the requisite details. * * @param user Execute method on behalf of this user * @param file File to upload * @param name Upload file using this name * @param description Description of file. Set to null for none. * @param parentNode Upload file under this folder node. If null, upload to * root node. * @param withRenditions Set to true to generate renditions (thumbnail, * flash preview) * @param createPdf Make PDF available for download after it has been * converted * * @return New file node * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final ShareAPINode addFile(final ShareAPIUser user, final File file, final String name, final String description, final ShareAPINode parentNode, final boolean withRenditions, final boolean createPdf) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { assertUserLoggedIn(user); if (parentNode != null) { assertValidNode(parentNode); } JSONObject json = null; String nameXml = file.getName(); if (name != null) { nameXml = name; } String descriptionXml = ""; if (description != null) { descriptionXml = description; } try { String requestBody = "<request><file><name>" + XML.escape(nameXml) + "</name><description>" + XML.escape(descriptionXml) + "</description><renditions>" + withRenditions + "</renditions><createpdf>" + createPdf + "</createpdf></file></request>"; if (parentNode != null) { json = parseResponse(createFilePostRequest(user, ENDPOINT_DC + parentNode.getId() + "/", false, requestBody, file)); } else { json = parseResponse(createFilePostRequest(user, ENDPOINT_DC, false, requestBody, file)); } JSONObject node = json.getJSONObject("response").optJSONObject("node"); return new ShareAPINode(node); } catch (JSONException e) { throw new ShareAPIException(-1, e.getMessage()); } } /** * Replaces a file in an existing node with a new file. * * @param user Execute method on behalf of this user * @param newFile File to upload * @param name Upload file using this name * @param description Description of file. Set to null for none. * @param oldNode Node of file to replace * @param withRenditions Set to true to generate renditions (thumbnail, * flash preview) * * @return New file node * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final ShareAPINode replaceFile(final ShareAPIUser user, final File newFile, final String name, final String description, final ShareAPINode oldNode, final boolean withRenditions) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { assertFileNode(oldNode); return addFile(user, newFile, name, description, oldNode, withRenditions); } /** * Adds a new folder to Share by building a request with an XML payload * containing the requisite details. * * @param user Execute method on behalf of this user * @param name Name of new folder * @param description Description of new folder. Set to null for none. * @param parentNode Add folder under this folder node. If null, add to root * node. * * @return New folder node * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final ShareAPINode addFolder(final ShareAPIUser user, final String name, final String description, final ShareAPINode parentNode) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { assertUserLoggedIn(user); if (parentNode != null) { assertFolderNode(parentNode); } JSONObject json = null; String descriptionXml = ""; if (description != null) { descriptionXml = description; } try { String requestBody = "<request><folder><name>" + XML.escape(name) + "</name><description>" + XML.escape(descriptionXml) + "</description></folder></request>"; if (parentNode != null) { json = parseResponse( createRequest(user, "POST", ENDPOINT_DC + parentNode.getId() + "/", false, requestBody)); } else { json = parseResponse(createRequest(user, "POST", ENDPOINT_DC, false, requestBody)); } JSONObject node = json.getJSONObject("response").getJSONObject("node"); return new ShareAPINode(node); } catch (JSONException e) { throw new ShareAPIException(-1, e.getMessage()); } } /** * Adds a link by constructing an HTTP request using the PUT method and the * path to the requisite file. * * @param user Execute method on behalf of this user * @param node File node to share * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final void addLink(final ShareAPIUser user, final ShareAPINode node) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { assertUserLoggedIn(user); assertFileNode(node); String requestBody = ""; parseResponse(createRequest(user, "PUT", ENDPOINT_DC + node.getId() + "/", false, requestBody)); } /** * Shares an existing file or node with one or more specified users. The * share level is set by setting <code>level</code> to 0, 1, or 2. For * example, this code could take in a value set from a client's user * interface, builds an XML payload containing a list of specified users (if * any), a message (which can be null), and the share level. It then * constructs an HTTP request using the PUT method and the path to the * requisite file. * * @param user Execute method on behalf of this user * @param node File node to share * @param users List of users to share with * @param message Optional message to include in email * @param level the level * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final void shareFile(final ShareAPIUser user, final ShareAPINode node, final List<String> users, final String message, final int level) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { shareFile(user, node, users, null, message, level); } /** * Shares an existing file or node with one or more specified users. The * share level is set by setting <code>level</code> to 0, 1, or 2. For * example, this code could take in a value set from a client's user * interface, builds an XML payload containing a list of specified users (if * any), a message (which can be null), and the share level. It then * constructs an HTTP request using the PUT method and the path to the * requisite file. * * @param user Execute method on behalf of this user * @param node File node to share * @param users List of users to share with * @param subject Optional custom subject for email * @param message Optional message to include in email * @param level the level * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final void shareFile(final ShareAPIUser user, final ShareAPINode node, final List<String> users, final String subject, final String message, final int level) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { assertUserLoggedIn(user); assertFileNode(node); String usersString = ""; for (String userString : users) { usersString = usersString + "<user>" + XML.escape(userString) + "</user>"; } String subjectXml = ""; String messageXml = ""; if (subject != null) { subjectXml = subject; } if (message != null) { messageXml = message; } String requestBody = "<request><share>" + usersString + "</share><subject>" + XML.escape(subjectXml) + "</subject><message>" + XML.escape(messageXml) + "</message><level>" + level + "</level></request>"; parseResponse(createRequest(user, "PUT", ENDPOINT_DC + node.getId() + "/share/", false, requestBody)); } /** * Unshares a file that has already been shared. * * @param user Execute method on behalf of this user * @param node Shared file node to unshare * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final void unshareFile(final ShareAPIUser user, final ShareAPINode node) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { shareFile(user, node, null, null, 0); } /** * Updates a shared file by adding and removing users with permission to * access the file. It builds an XML payload containing a list of specified * users to add and remove and then constructs an HTTP request using the PUT * method and the path to the requisite file. * * @param user Execute method on behalf of this user * @param node Shared file node to update * @param usersToAdd Add these users to share * @param usersToRemove Remove these users from share * @param message Optional message to include in email * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception * @throws IllegalArgumentException the illegal argument exception */ public final void updateShare(final ShareAPIUser user, final ShareAPINode node, final List<String> usersToAdd, final List<String> usersToRemove, final String message) throws HttpException, IOException, ShareAPIException, IllegalArgumentException { assertUserLoggedIn(user); assertFileNode(node); String usersToAddString = ""; for (String userString : usersToAdd) { usersToAddString = usersToAddString + "<user>" + XML.escape(userString) + "</user>"; } String usersToRemoveString = ""; for (String userString : usersToRemove) { usersToRemoveString = usersToRemoveString + "<user>" + XML.escape(userString) + "</user>"; } String messageXml = ""; if (message != null) { messageXml = message; } String requestBody = "<request><share>" + usersToAddString + "</share><unshare>" + usersToRemoveString + "</unshare><message>" + XML.escape(messageXml) + "</message></request>"; parseResponse(createRequest(user, "PUT", ENDPOINT_DC + node.getId() + "/share/", false, requestBody)); } /** * Displays an error message if the current operation requires that the user * must be logged in. * * @param user the ShareAPIUser * * @throws IllegalArgumentException the illegal argument exception */ protected final void assertUserLoggedIn(final ShareAPIUser user) throws IllegalArgumentException { if (user.getSessionId() == null) { throw new IllegalArgumentException("The user must be logged in."); } } /** * Displays an error message if the specified node cannot be found. * * @param node the ShareAPINode * * @throws IllegalArgumentException the illegal argument exception */ protected final void assertValidNode(final ShareAPINode node) throws IllegalArgumentException { if (node.getId() == null) { throw new IllegalArgumentException("The specified node is not valid."); } } /** * Displays an error message if a folder node is specified but the node ID * refers to a file node. * * @param node the ShareAPINode * * @throws IllegalArgumentException the illegal argument exception */ protected final void assertFolderNode(final ShareAPINode node) throws IllegalArgumentException { assertValidNode(node); if (!node.isDirectory()) { throw new IllegalArgumentException("The specified node is " + "not a folder node."); } } /** * Displays an error message if a file node is specified but the node ID * refers to a folder node. * * @param node the ShareAPINode node * @throws IllegalArgumentException the illegal argument exception */ protected final void assertFileNode(final ShareAPINode node) throws IllegalArgumentException { assertValidNode(node); if (!node.isContent()) { throw new IllegalArgumentException("The specified node is " + "not a file node."); } } /** * Prepares a new HTTP request. * * @param user the user * @param method the method * @param url the url * @param anon the anon * @param requestBody the request body * * @return the http method */ protected final HttpMethod createRequest(final ShareAPIUser user, final String method, final String url, final boolean anon, final String requestBody) { HttpMethod httpMethod = null; if ("GET".equals(method)) { httpMethod = new GetMethod(url); } else if ("POST".equals(method)) { httpMethod = new PostMethod(url); httpMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); try { ((PostMethod) httpMethod).setRequestEntity(new StringRequestEntity(requestBody, null, null)); } catch (UnsupportedEncodingException uee) { // Catch the unsupported encoding exception LOGGER.error("Unsupported encoding exception: " + uee.getMessage()); } } else if ("PUT".equals(method)) { httpMethod = new PutMethod(url); httpMethod.setRequestHeader("Content-Type", "text/plain"); if (requestBody != null) { ((PutMethod) httpMethod).setRequestEntity( new InputStreamRequestEntity(new ByteArrayInputStream(requestBody.getBytes()))); } } else if ("DELETE".equals(method)) { httpMethod = new DeleteMethod(url); } else if ("HEAD".equals(method)) { httpMethod = new HeadMethod(url); } /** * MoveMethod not supported by HttpClient else if("MOVE".equals(method)) * { httpMethod = new MoveMethod(url); } **/ httpMethod.setRequestHeader("Authorization", generateAuthorization(user, anon, method, url)); return httpMethod; } /** * Prepares a new HTTP multi-part POST request for uploading files. The * format for these requests adheres to the standard RFC1867. For details, * see http://www.ietf.org/rfc/rfc1867.txt. * * @param user the user * @param url the url * @param anon the anon * @param requestBody the request body * @param file the file * * @return the post method * * @throws FileNotFoundException the file not found exception */ protected final PostMethod createFilePostRequest(final ShareAPIUser user, final String url, final boolean anon, final String requestBody, final File file) throws FileNotFoundException { PostMethod filePost = new PostMethod(url); StringPart p1 = new StringPart("request", requestBody); Part[] parts = { p1, new FilePart("file", file.getName(), file) }; filePost.setRequestEntity(new MultipartRequestEntity(parts, filePost.getParams())); filePost.setRequestHeader("Authorization", generateAuthorization(user, anon, "POST", url)); return filePost; } /** * Generates an authorization string for a request to start a session with * the Share service. For details, refer to the API specification at * http://labs.adobe.com/wiki/index.php/Share. * * @param user the user * @param anon the anon * @param method the method * @param url the url * * @return the string */ protected final String generateAuthorization(final ShareAPIUser user, final boolean anon, final String method, final String url) { StringBuilder sb = new StringBuilder(); sb.append("AdobeAuth "); if (!anon && user.getSessionId() != null) { sb.append("sessionid=\"").append(user.getSessionId()).append("\","); } sb.append("apikey=\"").append(apikey).append("\","); String calltime = Long.toString((new Date()).getTime()); StringBuilder data = new StringBuilder(); data.append(method).append(" ").append(url).append(" ").append(calltime); sb.append("data=\"").append(data).append("\","); sb.append("sig=\"").append(generateSig(user, data.toString())).append("\""); return sb.toString(); } /** * Generates a signature from an MD5 digest of the owner's secret. For * details, refer to the API specification at * http://labs.adobe.com/wiki/index.php/Share. * * @param user the user * @param data the data * * @return the string */ protected final String generateSig(final ShareAPIUser user, final String data) { try { MessageDigest md = MessageDigest.getInstance("MD5"); if (user.getSecret() == null) { return hex(md.digest((data + secret).getBytes())); } else { return hex(md.digest((data + user.getSecret()).getBytes())); } } catch (NoSuchAlgorithmException e) { LOGGER.warn("Could not get MD5 algorithm..."); } return null; } /** * Converts an ASCII string to its hex representation. * * @param array the array * * @return the string */ private String hex(final byte[] array) { StringBuffer sb = new StringBuffer(); final int hex1 = 0xFF; final int hex2 = 0x100; final int lastChar = 3; for (int i = 0; i < array.length; ++i) { sb.append(Integer.toHexString((array[i] & hex1) | hex2).substring(1, lastChar)); } return sb.toString(); } /** * Parses the response from the Share service. * * @param method the method * * @return the jSON object * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception */ protected final JSONObject parseResponse(final HttpMethod method) throws HttpException, IOException, ShareAPIException { JSONObject json = null; String content = null; if (LOGGER.isDebugEnabled()) { LOGGER.debug("URL: " + method.getURI().toString()); LOGGER.debug("Proxy: " + httpClient.getHostConfiguration().getProxyHost()); LOGGER.debug("Proxy Port: " + httpClient.getHostConfiguration().getProxyPort()); } try { int status = httpClient.executeMethod(method); final int bufferSize = 4096; byte[] buffer = new byte[bufferSize]; OutputStream outputStream = new ByteArrayOutputStream(); InputStream inputStream = method.getResponseBodyAsStream(); while (true) { int read = inputStream.read(buffer); if (read == -1) { break; } outputStream.write(buffer, 0, read); } outputStream.close(); inputStream.close(); content = outputStream.toString(); if (method.getResponseHeader("Content-Type") != null) { String contentType = method.getResponseHeader("Content-Type").getValue(); if (method.getStatusCode() == STATUS_OK) { if (contentType.contains("application/xml")) { json = XML.toJSONObject(content); } } } if (status >= STATUS_BAD_REQUEST) { if (json != null) { throw new ShareAPIException(method.getStatusCode(), json.getJSONObject("response").getString("message")); } else { throw new ShareAPIException(method.getStatusCode(), content); } } } catch (JSONException e) { e.printStackTrace(); throw new ShareAPIException(method.getStatusCode(), content); } return json; } /** * Parses the file response. * * @param method the method * * @return the input stream * * @throws HttpException the http exception * @throws IOException Signals that an I/O exception has occurred. * @throws ShareAPIException the share api exception */ protected final InputStream parseFileResponse(final HttpMethod method) throws HttpException, IOException, ShareAPIException { JSONObject json = null; String content = null; try { int status = httpClient.executeMethod(method); if (status >= STATUS_BAD_REQUEST) { if (json != null) { throw new ShareAPIException(method.getStatusCode(), json.getJSONObject("response").getString("message")); } else { throw new ShareAPIException(method.getStatusCode(), content); } } return method.getResponseBodyAsStream(); } catch (JSONException e) { throw new ShareAPIException(method.getStatusCode(), content); } } /** * Gets the HTTP client. * * @return HttpClient */ public final HttpClient getHttpClient() { return httpClient; } /** * Sets the HTTP client. * * @param httpClientRef the http client */ public final void setHttpClient(final HttpClient httpClientRef) { this.httpClient = httpClientRef; } /** * Gets the apikey. * * @return the apikey */ public final String getApikey() { return apikey; } /** * Sets the apikey. * * @param apikeyVal the new apikey */ public final void setApikey(final String apikeyVal) { this.apikey = apikeyVal; } /** * Gets the secret. * * @return the secret */ public final String getSecret() { return secret; } /** * Sets the secret. * * @param secretVal the new secret */ public final void setSecret(final String secretVal) { this.secret = secretVal; } }