Java tutorial
/* * Copyright (C) 2003-2013 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.clouddrive.box; import com.box.boxjavalibv2.BoxClient; import com.box.boxjavalibv2.BoxConfigBuilder; import com.box.boxjavalibv2.BoxRESTClient; import com.box.boxjavalibv2.authorization.IAuthSecureStorage; import com.box.boxjavalibv2.authorization.OAuthDataController.OAuthTokenState; import com.box.boxjavalibv2.authorization.OAuthRefreshListener; import com.box.boxjavalibv2.dao.BoxCollection; import com.box.boxjavalibv2.dao.BoxEnterprise; import com.box.boxjavalibv2.dao.BoxEvent; import com.box.boxjavalibv2.dao.BoxEventCollection; import com.box.boxjavalibv2.dao.BoxFile; import com.box.boxjavalibv2.dao.BoxFolder; import com.box.boxjavalibv2.dao.BoxItem; import com.box.boxjavalibv2.dao.BoxOAuthToken; import com.box.boxjavalibv2.dao.BoxServerError; import com.box.boxjavalibv2.dao.BoxSharedLink; import com.box.boxjavalibv2.dao.BoxTypedObject; import com.box.boxjavalibv2.dao.IAuthData; import com.box.boxjavalibv2.exceptions.AuthFatalFailureException; import com.box.boxjavalibv2.exceptions.BoxJSONException; import com.box.boxjavalibv2.exceptions.BoxServerException; import com.box.boxjavalibv2.jsonparsing.BoxJSONParser; import com.box.boxjavalibv2.jsonparsing.BoxResourceHub; import com.box.boxjavalibv2.requests.requestobjects.BoxEventRequestObject; import com.box.boxjavalibv2.requests.requestobjects.BoxFileRequestObject; import com.box.boxjavalibv2.requests.requestobjects.BoxFolderDeleteRequestObject; import com.box.boxjavalibv2.requests.requestobjects.BoxFolderRequestObject; import com.box.boxjavalibv2.requests.requestobjects.BoxItemCopyRequestObject; import com.box.boxjavalibv2.requests.requestobjects.BoxItemRestoreRequestObject; import com.box.boxjavalibv2.requests.requestobjects.BoxRequestExtras; import com.box.boxjavalibv2.utils.ISO8601DateParser; import com.box.boxjavalibv2.utils.Utils; import com.box.restclientv2.exceptions.BoxRestException; import com.box.restclientv2.requestsbase.BoxDefaultRequestObject; import com.box.restclientv2.requestsbase.BoxFileUploadRequestObject; import org.apache.http.client.HttpClient; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.exoplatform.clouddrive.CloudDriveException; import org.exoplatform.clouddrive.ConflictException; import org.exoplatform.clouddrive.FileTrashRemovedException; import org.exoplatform.clouddrive.NotFoundException; import org.exoplatform.clouddrive.RefreshAccessException; import org.exoplatform.clouddrive.oauth2.UserToken; import org.exoplatform.clouddrive.utils.ChunkIterator; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.security.KeyStore; import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; /** * All calls to Box API here. * * Created by The eXo Platform SAS. * * @author <a href="mailto:pnedonosko@exoplatform.com">Peter Nedonosko</a> * @version $Id: BoxAPI.java 00000 Aug 30, 2013 pnedonosko $ * */ public class BoxAPI { protected static final Log LOG = ExoLogger.getLogger(BoxAPI.class); /** * Pagination size used within Box API. */ public static final int BOX_PAGE_SIZE = 100; public static final String NO_STATE = "__no_state_set__"; /** * Id of root folder on Box. */ public static final String BOX_ROOT_ID = "0"; /** * Id of Trash folder on Box. */ public static final String BOX_TRASH_ID = "1"; /** * Box item_status for active items. */ public static final String BOX_ITEM_STATE_ACTIVE = "active"; /** * Box item_status for trashed items. */ public static final String BOX_ITEM_STATE_TRASHED = "trashed"; /** * URL of Box app. */ public static final String BOX_APP_URL = "https://app.box.com/"; /** * Not official part of the path used in file services with Box API. */ protected static final String BOX_FILES_PATH = "files/0/f/"; /** * URL prefix for Box files' UI. */ public static final String BOX_FILE_URL = BOX_APP_URL + BOX_FILES_PATH; /** * Extension for Box's webdoc files. */ public static final String BOX_WEBDOCUMENT_EXT = "webdoc"; /** * Custom mimetype for Box's webdoc files. */ public static final String BOX_WEBDOCUMENT_MIMETYPE = "application/x-exo.box.webdoc"; /** * Extension for Box's webdoc files. */ public static final String BOX_NOTE_EXT = "boxnote"; /** * Custom mimetype for Box's note files. */ public static final String BOX_NOTE_MIMETYPE = "application/x-exo.box.note"; /** * URL patter for Embedded UI of Box file. Based on:<br> * http://stackoverflow.com/questions/12816239/box-com-embedded-file-folder-viewer-code-via-api * http://developers.box.com/box-embed/<br> */ public static final String BOX_EMBED_URL = "https://%sapp.box.com/embed_widget/000000000000/%s?" + "view=list&sort=date&theme=gray&show_parent_path=no&show_item_feed_actions=no&session_expired=true"; public static final String BOX_EMBED_URL_SSO = "https://app.box.com/login/auto_initiate_sso?enterprise_id=%s&redirect_url=%s"; public static final Pattern BOX_URL_CUSTOM_PATTERN = Pattern .compile("^https://([\\p{ASCII}]*){1}?\\.app\\.box\\.com/.*\\z"); public static final Pattern BOX_URL_MAKE_CUSTOM_PATTERN = Pattern.compile("^(https)://(app\\.box\\.com/.*)\\z"); public static final Set<String> BOX_EVENTS = new HashSet<String>(); static { BOX_EVENTS.add(BoxEvent.EVENT_TYPE_ITEM_CREATE); BOX_EVENTS.add(BoxEvent.EVENT_TYPE_ITEM_UPLOAD); BOX_EVENTS.add(BoxEvent.EVENT_TYPE_ITEM_MOVE); BOX_EVENTS.add(BoxEvent.EVENT_TYPE_ITEM_COPY); BOX_EVENTS.add(BoxEvent.EVENT_TYPE_ITEM_TRASH); BOX_EVENTS.add(BoxEvent.EVENT_TYPE_ITEM_UNDELETE_VIA_TRASH); BOX_EVENTS.add(BoxEvent.EVENT_TYPE_ITEM_RENAME); } class StoredToken extends UserToken implements OAuthRefreshListener, IAuthSecureStorage { void store(BoxOAuthToken btoken) throws CloudDriveException { this.store(btoken.getAccessToken(), btoken.getRefreshToken(), btoken.getExpiresIn()); } /** * @param newAuthData */ public void onRefresh(IAuthData newAuthData) { // save the auth data. BoxOAuthToken newToken = (BoxOAuthToken) newAuthData; try { store(newToken); } catch (CloudDriveException e) { LOG.error("Error storing refreshed access token", e); } } /** * {@inheritDoc} */ @Override public void saveAuth(IAuthData auth) { try { store((BoxOAuthToken) auth); } catch (CloudDriveException e) { LOG.error("Error saving access token", e); } } /** * {@inheritDoc} */ @Override public IAuthData getAuth() { Map<String, Object> data = new HashMap<String, Object>(); data.put(BoxOAuthToken.FIELD_ACCESS_TOKEN, getAccessToken()); data.put(BoxOAuthToken.FIELD_REFRESH_TOKEN, getRefreshToken()); data.put(BoxOAuthToken.FIELD_EXPIRES_IN, getExpirationTime()); data.put(BoxOAuthToken.FIELD_TOKEN_TYPE, "bearer"); return new BoxOAuthToken(data); } } /** * Iterator over whole set of items from Box service. This iterator hides next-chunk logic on * request to the service. <br> * Iterator methods can throw {@link BoxException} in case of remote or communication errors. */ class ItemsIterator extends ChunkIterator<BoxItem> { final String folderId; int offset = 0, total = 0; /** * Parent folder. */ BoxFolder parent; ItemsIterator(String folderId) throws CloudDriveException { this.folderId = folderId; // fetch first this.iter = nextChunk(); } protected Iterator<BoxItem> nextChunk() throws CloudDriveException { BoxDefaultRequestObject obj = new BoxDefaultRequestObject(); obj.setPage(BOX_PAGE_SIZE, offset); BoxRequestExtras ext = obj.getRequestExtras(); ext.addField(BoxItem.FIELD_ID); ext.addField(BoxItem.FIELD_PARENT); ext.addField(BoxItem.FIELD_NAME); ext.addField(BoxItem.FIELD_TYPE); ext.addField(BoxItem.FIELD_ETAG); ext.addField(BoxItem.FIELD_SEQUENCE_ID); ext.addField(BoxItem.FIELD_CREATED_AT); ext.addField(BoxItem.FIELD_MODIFIED_AT); ext.addField(BoxItem.FIELD_DESCRIPTION); ext.addField(BoxItem.FIELD_SIZE); ext.addField(BoxItem.FIELD_CREATED_BY); ext.addField(BoxItem.FIELD_MODIFIED_BY); ext.addField(BoxItem.FIELD_OWNED_BY); ext.addField(BoxItem.FIELD_SHARED_LINK); ext.addField(BoxItem.FIELD_ITEM_STATUS); ext.addField(BoxItem.FIELD_PATH_COLLECTION); ext.addField(BoxFolder.FIELD_ITEM_COLLECTION); try { parent = client.getFoldersManager().getFolder(folderId, obj); BoxCollection items = parent.getItemCollection(); // total number of files in the folder total = items.getTotalCount(); if (offset == 0) { available(total); } offset += items.getEntries().size(); ArrayList<BoxItem> oitems = new ArrayList<BoxItem>(); // put folders first, then files oitems.addAll(Utils.getTypedObjects(items, BoxFolder.class)); oitems.addAll(Utils.getTypedObjects(items, BoxFile.class)); return oitems.iterator(); } catch (BoxRestException e) { throw new BoxException("Error getting folder items: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then folder not found throw new NotFoundException("Folder not found " + folderId, e); } throw new BoxException("Error reading folder items: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error on folder items: " + e.getMessage(), e); } } protected boolean hasNextChunk() { return total > offset; } } /** * Iterator over set of events from Box service. This iterator hides next-chunk logic on * request to the service. <br> * Iterator methods can throw {@link BoxException} in case of remote or communication errors. */ class EventsIterator extends ChunkIterator<BoxEvent> { /** * Set of already fetched event Ids. Used to ignore duplicates from different requests. */ final Set<String> eventIds = new HashSet<String>(); Long streamPosition; Integer offset = 0, chunkSize = 0; EventsIterator(long streamPosition) throws BoxException, RefreshAccessException { this.streamPosition = streamPosition <= -1 ? BoxEventRequestObject.STREAM_POSITION_NOW : streamPosition; // fetch first this.iter = nextChunk(); } protected Iterator<BoxEvent> nextChunk() throws BoxException, RefreshAccessException { try { BoxEventRequestObject request = BoxEventRequestObject.getEventsRequestObject(streamPosition); // interest to tree changes only request.setStreamType(BoxEventRequestObject.STREAM_TYPE_CHANGES); request.setLimit(BOX_PAGE_SIZE); BoxEventCollection ec = client.getEventsManager().getEvents(request); // for next chunk and next iterators streamPosition = ec.getNextStreamPosition(); ArrayList<BoxEvent> events = new ArrayList<BoxEvent>(); for (BoxTypedObject eobj : ec.getEntries()) { BoxEvent event = (BoxEvent) eobj; if (BOX_EVENTS.contains(event.getEventType())) { String id = event.getId(); if (!eventIds.contains(id)) { eventIds.add(id); events.add(event); } } } this.chunkSize = events.size(); return events.iterator(); } catch (BoxRestException e) { throw new BoxException("Error requesting Events service: " + e.getMessage(), e); } catch (BoxServerException e) { throw new BoxException("Error reading Events service: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error for Events service: " + e.getMessage(), e); } } /** * {@inheritDoc} */ protected boolean hasNextChunk() { // if something was read in previous chunk, then we may have a next chunk return chunkSize > 0; } long getNextStreamPosition() { return streamPosition; } } public static class ChangesLink { final String type; final String url; final long maxRetries, retryTimeout, ttl, outdatedTimeout, created; ChangesLink(String type, String url, long ttl, long maxRetries, long retryTimeout) { this.type = type; this.url = url; this.ttl = ttl; this.maxRetries = maxRetries; this.retryTimeout = retryTimeout; // link will be outdated in 95% of retry timeout this.outdatedTimeout = retryTimeout - Math.round(retryTimeout * 0.05f); this.created = System.currentTimeMillis(); } /** * @return the type */ public String getType() { return type; } /** * @return the url */ public String getUrl() { return url; } /** * @return the ttl */ public long getTtl() { return ttl; } /** * @return the maxRetries */ public long getMaxRetries() { return maxRetries; } /** * @return the retryTimeout */ public long getRetryTimeout() { return retryTimeout; } /** * @return the outdatedTimeout */ public long getOutdatedTimeout() { return outdatedTimeout; } /** * @return the created */ public long getCreated() { return created; } public boolean isOutdated() { return (System.currentTimeMillis() - created) > outdatedTimeout; } } /** * Box REST client adopted to Apache HTTP 4.1 (from Platform 4.0) and using allow-all hostname validator. * Purpose of this client is to workaround <a href= * "http://stackoverflow.com/questions/23529852/multiple-files-upload-to-box-fails-with-http-client-error-connection-still-allo" * >multiple files upload problem</a>. */ class RESTClient extends BoxRESTClient { final HttpClient httpClient; RESTClient() { super(); SchemeRegistry schemeReg = new SchemeRegistry(); schemeReg.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); SSLSocketFactory socketFactory; try { SSLContext sslContext = SSLContext.getInstance(SSLSocketFactory.TLS); KeyManagerFactory kmfactory = KeyManagerFactory .getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmfactory.init(null, null); KeyManager[] keymanagers = kmfactory.getKeyManagers(); TrustManagerFactory tmfactory = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmfactory.init((KeyStore) null); TrustManager[] trustmanagers = tmfactory.getTrustManagers(); sslContext.init(keymanagers, trustmanagers, null); socketFactory = new SSLSocketFactory(sslContext, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); } catch (Exception ex) { throw new IllegalStateException("Failure initializing default SSL context for Box REST client", ex); } schemeReg.register(new Scheme("https", 443, socketFactory)); ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager(schemeReg); // XXX 2 recommended by RFC 2616 sec 8.1.4, we make it bigger for quicker // upload connectionManager.setDefaultMaxPerRoute(4); // 20 by default, we twice it also connectionManager.setMaxTotal(40); this.httpClient = new DefaultHttpClient(connectionManager); } /** * {@inheritDoc} */ @Override public HttpClient getRawHttpClient() { return httpClient; } } private StoredToken token; private BoxClient client; private ChangesLink changesLink; private String enterpriseId, enterpriseName, customDomain; /** * Create Box API from OAuth2 authentication code. * * @param key {@link String} Box API key the same also as OAuth2 client_id * @param clientSecret {@link String} * @param authCode {@link String} * @throws BoxException if authentication failed for any reason. * @throws CloudDriveException if credentials store exception happen */ BoxAPI(String key, String clientSecret, String authCode, String redirectUri) throws BoxException, CloudDriveException { BoxResourceHub hub = new BoxResourceHub(); BoxJSONParser parser = new BoxJSONParser(hub); this.client = new BoxClient(key, clientSecret, hub, parser, new RESTClient(), new BoxConfigBuilder().build()); this.token = new StoredToken(); this.client.addOAuthRefreshListener(token); try { BoxOAuthToken bt = this.client.getOAuthManager().createOAuth(authCode, key, clientSecret, redirectUri); this.token.store(bt); this.client.authenticate(bt); } catch (BoxRestException e) { throw new BoxException("Error submiting authentication code: " + e.getMessage(), e); } catch (BoxServerException e) { throw new BoxException("Error authenticating user code: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { throw new BoxException("Authentication code error: " + e.getMessage(), e); } // finally init changes link updateChangesLink(); // init user (enterprise etc.) initUser(); } /** * Create Box API from existing user credentials. * * @param key {@link String} Box API key the same also as OAuth2 client_id * @param clientSecret {@link String} * @param accessToken {@link String} * @param refreshToken {@link String} * @param expirationTime long, token expiration time on milliseconds * @throws CloudDriveException if credentials store exception happen */ BoxAPI(String key, String clientSecret, String accessToken, String refreshToken, long expirationTime) throws CloudDriveException { BoxResourceHub hub = new BoxResourceHub(); BoxJSONParser parser = new BoxJSONParser(hub); this.client = new BoxClient(key, clientSecret, hub, parser, new RESTClient(), new BoxConfigBuilder().build()); this.token = new StoredToken(); this.token.load(accessToken, refreshToken, expirationTime); this.client.addOAuthRefreshListener(token); this.client.authenticateFromSecureStorage(token); // init user (enterprise etc.) initUser(); } /** * Update OAuth2 token to a new one. * * @param newToken {@link StoredToken} * @throws CloudDriveException */ void updateToken(UserToken newToken) throws CloudDriveException { this.token.merge(newToken); } /** * Current OAuth2 token associated with this API instance. * * @return {@link StoredToken} */ StoredToken getToken() { return token; } /** * Currently connected Box user. * * @return * @throws BoxException * @throws RefreshAccessException */ com.box.boxjavalibv2.dao.BoxUser getCurrentUser() throws BoxException, RefreshAccessException { BoxDefaultRequestObject obj = new BoxDefaultRequestObject(); obj.getRequestExtras().addField("id"); obj.getRequestExtras().addField("type"); obj.getRequestExtras().addField("name"); obj.getRequestExtras().addField("login"); obj.getRequestExtras().addField("created_at"); obj.getRequestExtras().addField("modified_at"); obj.getRequestExtras().addField("status"); obj.getRequestExtras().addField("job_title"); obj.getRequestExtras().addField("phone"); obj.getRequestExtras().addField("address"); obj.getRequestExtras().addField("avatar_url"); obj.getRequestExtras().addField("role"); obj.getRequestExtras().addField("enterprise"); try { return client.getUsersManager().getCurrentUser(obj); } catch (BoxRestException e) { throw new BoxException("Error requesting current user: " + e.getMessage(), e); } catch (BoxServerException e) { throw new BoxException("Error getting current user: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error for current user: " + e.getMessage(), e); } } /** * The Box root folder. * * @return {@link BoxFolder} * @throws BoxException */ BoxFolder getRootFolder() throws BoxException { try { BoxFolder root = client.getFoldersManager().getFolder(BOX_ROOT_ID, null); return root; } catch (BoxRestException e) { throw new BoxException("Error getting root folder: " + e.getMessage(), e); } catch (BoxServerException e) { throw new BoxException("Error reading root folder: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { throw new BoxException("Authentication error for root folder: " + e.getMessage(), e); } } ItemsIterator getFolderItems(String folderId) throws CloudDriveException { return new ItemsIterator(folderId); } Calendar parseDate(String dateString) throws ParseException { Calendar calendar = Calendar.getInstance(); Date d = ISO8601DateParser.parse(dateString); calendar.setTime(d); return calendar; } String formatDate(Calendar date) { return ISO8601DateParser.toString(date.getTime()); } /** * Link (URl) to the Box file for opening on Box site (UI). * * @param item {@link BoxItem} * @return String with the file URL. */ String getLink(BoxItem item) { BoxSharedLink shared = item.getSharedLink(); if (shared != null) { String link = shared.getUrl(); if (link != null) { return link(link); } } // XXX This link build not on official documentation, but from observed URLs from Box app site. StringBuilder link = new StringBuilder(); link.append(link(BOX_FILE_URL)); String id = item.getId(); if (BOX_ROOT_ID.equals(id)) { link.append(id); } else if (item instanceof BoxFile) { String parentId = item.getParent().getId(); link.append(parentId); link.append("/1/f_"); link.append(id); } else if (item instanceof BoxFolder) { link.append(id); link.append('/'); link.append(item.getName()); } else { // for unknown open root folder link.append(BOX_ROOT_ID); } link.append("/"); return link.toString(); } /** * Link (URL) to embed a file onto external app (in PLF). * * @param item {@link BoxItem} * @return String with the file embed URL. */ String getEmbedLink(BoxItem item) { StringBuilder linkValue = new StringBuilder(); BoxSharedLink shared = item.getSharedLink(); if (shared != null) { String link = shared.getUrl(); String[] lparts = link.split("/"); if (lparts.length > 3 && lparts[lparts.length - 2].equals("s")) { // XXX unofficial way of linkValue extracting from shared link linkValue.append("s/"); linkValue.append(lparts[lparts.length - 1]); } } if (linkValue.length() == 0) { linkValue.append(BOX_FILES_PATH); // XXX This link build not on official documentation, but from observed URLs from Box app site. String id = item.getId(); if (BOX_ROOT_ID.equals(id)) { linkValue.append(id); } else if (item instanceof BoxFile) { String parentId = item.getParent().getId(); linkValue.append(parentId); linkValue.append("/1/f_"); linkValue.append(id); } else if (item instanceof BoxFolder) { linkValue.append(id); } else { // for unknown open root folder linkValue.append(BOX_ROOT_ID); } } // Take in account custom domain for Enterprise on Box if (customDomain != null) { return String.format(BOX_EMBED_URL, customDomain + ".", linkValue.toString()); } else { return String.format(BOX_EMBED_URL, "", linkValue.toString()); } } /** * A link (URL) to the Box file thumbnail image. * * @param item {@link BoxItem} * @return String with the file URL. */ String getThumbnailLink(BoxItem item) { // TODO use real thumbnails from Box return getLink(item); } ChangesLink getChangesLink() throws BoxException, RefreshAccessException { if (changesLink == null || changesLink.isOutdated()) { updateChangesLink(); } return changesLink; } /** * Update link to the drive's long-polling changes notification service. This kind of service optional and * may not be supported. If long-polling changes notification not supported then this method will do * nothing. * */ void updateChangesLink() throws BoxException, RefreshAccessException { BoxDefaultRequestObject obj = new BoxDefaultRequestObject(); try { BoxCollection changesPoll = client.getEventsManager().getEventOptions(obj); ArrayList<BoxTypedObject> ce = changesPoll.getEntries(); if (ce.size() > 0) { BoxTypedObject c = ce.get(0); Object urlObj = c.getValue("url"); String url = urlObj != null ? urlObj.toString() : null; Object typeObj = c.getValue("type"); String type = typeObj != null ? typeObj.toString() : null; Object ttlObj = c.getValue("ttl"); if (ttlObj == null) { ttlObj = c.getExtraData("ttl"); } long ttl; try { ttl = ttlObj != null ? Long.parseLong(ttlObj.toString()) : 10; } catch (NumberFormatException e) { LOG.warn("Error parsing ttl value in Events response [" + ttlObj + "]: " + e); ttl = 10; // 10? What is it ttl? The number from Box docs. } Object maxRetriesObj = c.getValue("max_retries"); if (maxRetriesObj == null) { maxRetriesObj = c.getExtraData("max_retries"); } long maxRetries; try { maxRetries = maxRetriesObj != null ? Long.parseLong(maxRetriesObj.toString()) : 0; } catch (NumberFormatException e) { LOG.warn("Error parsing max_retries value in Events response [" + maxRetriesObj + "]: " + e); maxRetries = 2; // assume two possible attempts to try use the link } Object retryTimeoutObj = c.getValue("retry_timeout"); if (retryTimeoutObj == null) { retryTimeoutObj = c.getExtraData("retry_timeout"); } long retryTimeout; // it is in milliseconds try { retryTimeout = retryTimeoutObj != null ? Long.parseLong(retryTimeoutObj.toString()) * 1000 : 600000; } catch (NumberFormatException e) { LOG.warn( "Error parsing retry_timeout value in Events response [" + retryTimeoutObj + "]: " + e); retryTimeout = 600000; // 600 sec = 10min (as mentioned in Box docs) } this.changesLink = new ChangesLink(type, url, ttl, maxRetries, retryTimeout); } else { throw new BoxException("Empty entries from Events service."); } } catch (BoxRestException e) { throw new BoxException("Error requesting changes long poll URL: " + e.getMessage(), e); } catch (BoxServerException e) { throw new BoxException("Error reading changes long poll URL: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error for changes long poll URL: " + e.getMessage(), e); } } EventsIterator getEvents(long streamPosition) throws BoxException, RefreshAccessException { return new EventsIterator(streamPosition); } BoxFile createFile(String parentId, String name, Calendar created, InputStream data) throws BoxException, NotFoundException, RefreshAccessException, ConflictException { try { // To speedup the process we check if parent exists first. // How this speedups: if parent not found we will not wait for the content upload to the Box side. try { readFolder(parentId); } catch (NotFoundException e) { // parent not found throw new NotFoundException( "Parent not found " + parentId + ". Cannot start file uploading " + name, e); } // TODO You can optionally specify a Content-MD5 header with the SHA1 hash of the file to ensure that // the file is not corrupted in transit. BoxFileUploadRequestObject obj = BoxFileUploadRequestObject.uploadFileRequestObject(parentId, name, data); obj.setLocalFileCreatedAt(created.getTime()); obj.put("created_at", formatDate(created)); return client.getFilesManager().uploadFile(obj); } catch (BoxJSONException e) { throw new BoxException("Error uploading file: " + e.getMessage(), e); } catch (UnsupportedEncodingException e) { throw new BoxException("Error uploading file: " + e.getMessage(), e); } catch (BoxRestException e) { throw new BoxException("Error uploading file: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then parent not found throw new NotFoundException( "Parent not found " + parentId + ". File uploading canceled for " + name, e); } else if (status == 403) { throw new NotFoundException("The user doesn't have access to upload a file " + name, e); } else if (status == 409) { // conflict - the same name file exists throw new ConflictException("File with the same name as creating already exists " + name, e); } throw new BoxException("Error uploading file: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when uploading file: " + e.getMessage(), e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new BoxException("File " + name + " uploading interrupted.", e); } } BoxFolder createFolder(String parentId, String name, Calendar created) throws BoxException, NotFoundException, RefreshAccessException, ConflictException { try { BoxFolderRequestObject obj = BoxFolderRequestObject.createFolderRequestObject(name, parentId); obj.put("created_at", formatDate(created)); return client.getFoldersManager().createFolder(obj); } catch (BoxRestException e) { throw new BoxException("Error creating folder: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then parent not found throw new NotFoundException("Parent not found " + parentId, e); } else if (status == 403) { throw new NotFoundException("The user doesn't have access to create a folder " + name, e); } else if (status == 409) { // conflict - the same name file exists throw new ConflictException("File with the same name as creating already exists " + name, e); } throw new BoxException("Error creating folder: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when creating folder: " + e.getMessage(), e); } } BoxFolder createSharedFolder(String parentId, String name, Calendar created) throws BoxException, NotFoundException, RefreshAccessException, ConflictException { try { BoxFolderRequestObject obj = BoxFolderRequestObject.createSharedLinkRequestObject(null); obj.put("created_at", formatDate(created)); return client.getSharedFoldersManager("sharedLink", "password").createFolder(obj); } catch (BoxRestException e) { throw new BoxException("Error creating folder: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then parent not found throw new NotFoundException("Parent not found " + parentId, e); } else if (status == 403) { throw new NotFoundException("The user doesn't have access to create a folder " + name, e); } else if (status == 409) { // conflict - the same name file exists throw new ConflictException("File with the same name as creating already exists " + name, e); } throw new BoxException("Error creating folder: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when creating folder: " + e.getMessage(), e); } } /** * Delete a cloud file by given fileId. Depending on Box enterprise settings for this user, the file will * either be actually deleted from Box or moved to the Trash. * * @param id {@link String} * @throws BoxException * @throws NotFoundException * @throws RefreshAccessException */ void deleteFile(String id) throws BoxException, NotFoundException, RefreshAccessException { try { BoxDefaultRequestObject obj = new BoxDefaultRequestObject(); // obj.setIfMatch(etag); // // TODO use it! client.getFilesManager().deleteFile(id, obj); } catch (BoxRestException e) { throw new BoxException("Error deleting file: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then item not found throw new NotFoundException("File not found " + id, e); } else if (status == 403) { throw new NotFoundException("The user doesn't have access to the file " + id, e); } throw new BoxException("Error deleting file: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when deleting file: " + e.getMessage(), e); } } /** * Delete a cloud folder by given folderId. Depending on Box enterprise settings for this user, the folder * will * either be actually deleted from Box or moved to the Trash. * * @param id {@link String} * @throws BoxException * @throws NotFoundException * @throws RefreshAccessException */ void deleteFolder(String id) throws BoxException, NotFoundException, RefreshAccessException { try { BoxFolderDeleteRequestObject obj = BoxFolderDeleteRequestObject.deleteFolderRequestObject(true); // obj.setIfMatch(etag); // TODO use it! client.getFoldersManager().deleteFolder(id, obj); } catch (BoxRestException e) { throw new BoxException("Error deleting folder: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then item not found throw new NotFoundException("File not found " + id, e); } else if (status == 403) { throw new NotFoundException("The user doesn't have access to the folder " + id, e); } throw new BoxException("Error deleting folder: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when deleting folder: " + e.getMessage(), e); } } /** * Trash a cloud file by given fileId. Depending on Box enterprise settings for this user, the file will * either be actually deleted from Box or moved to the Trash. If the file was actually deleted on Box, this * method will throw {@link FileTrashRemovedException}, and the caller code should delete the file locally * also. * * @param id {@link String} * @return {@link BoxFile} of the file successfully moved to Box Trash * @throws BoxException * @throws FileTrashRemovedException if file was permanently removed. * @throws NotFoundException * @throws RefreshAccessException */ BoxFile trashFile(String id) throws BoxException, FileTrashRemovedException, NotFoundException, RefreshAccessException { try { BoxDefaultRequestObject deleteObj = new BoxDefaultRequestObject(); // deleteObj.setIfMatch(etag); // TODO use it! client.getFilesManager().deleteFile(id, deleteObj); // check if file actually removed or in the trash try { BoxDefaultRequestObject trashObj = new BoxDefaultRequestObject(); return client.getTrashManager().getTrashFile(id, trashObj); } catch (BoxRestException e) { throw new BoxException("Error reading trashed file: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then file not found in the Trash // XXX throwing an exception not a best solution, but returning a boolean also can have double // meaning: not trashed at all or deleted instead of trashed throw new FileTrashRemovedException("Trashed file deleted permanently " + id); } throw new BoxException("Error reading trashed file: " + e.getMessage(), e); } } catch (BoxRestException e) { throw new BoxException("Error trashing file: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then item not found throw new NotFoundException("File not found " + id, e); } else if (status == 403) { throw new NotFoundException("The user doesn't have access to the file " + id, e); } throw new BoxException("Error trashing file: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when trashing file: " + e.getMessage(), e); } } /** * Trash a cloud folder by given folderId. Depending on Box enterprise settings for this user, the folder * will either be actually deleted from Box or moved to the Trash. If the folder was actually deleted in * Box, this method will return {@link FileTrashRemovedException}, and the caller code should delete the * folder locally also. * * @param id {@link String} * @return {@link BoxFolder} of the folder successfully moved to Box Trash * @throws BoxException * @throws FileTrashRemovedException if folder was permanently removed. * @throws NotFoundException * @throws RefreshAccessException */ BoxFolder trashFolder(String id) throws BoxException, FileTrashRemovedException, NotFoundException, RefreshAccessException { try { BoxFolderDeleteRequestObject deleteObj = BoxFolderDeleteRequestObject.deleteFolderRequestObject(true); // deleteObj.setIfMatch(etag); // TODO use it! client.getFoldersManager().deleteFolder(id, deleteObj); // check if file actually removed or in the trash try { BoxDefaultRequestObject trashObj = new BoxDefaultRequestObject(); return client.getTrashManager().getTrashFolder(id, trashObj); } catch (BoxRestException e) { throw new BoxException("Error reading trashed foler: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then foler not found in the Trash // XXX throwing an exception not a best solution, but returning a boolean also can have double // meaning: not trashed at all or deleted instead of trashed throw new FileTrashRemovedException("Trashed folder deleted permanently " + id); } throw new BoxException("Error reading trashed foler: " + e.getMessage(), e); } } catch (BoxRestException e) { throw new BoxException("Error trashing foler: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then item not found throw new NotFoundException("File not found " + id, e); } else if (status == 403) { throw new NotFoundException("The user doesn't have access to the folder " + id, e); } throw new BoxException("Error trashing foler: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when trashing foler: " + e.getMessage(), e); } } BoxFile untrashFile(String id, String name) throws BoxException, NotFoundException, RefreshAccessException, ConflictException { try { BoxItemRestoreRequestObject obj = BoxItemRestoreRequestObject.restoreItemRequestObject(); if (name != null) { obj.setNewName(name); } return client.getTrashManager().restoreTrashFile(id, obj); } catch (BoxRestException e) { throw new BoxException("Error untrashing file: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then item not found throw new NotFoundException("Trashed file not found " + id, e); } else if (status == 405) { // method_not_allowed throw new NotFoundException("File not in the trash " + id, e); } else if (status == 409) { // conflict throw new ConflictException("File with the same name as untrashed already exists " + id, e); } throw new BoxException("Error untrashing file: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when untrashing file: " + e.getMessage(), e); } } BoxFolder untrashFolder(String id, String name) throws BoxException, NotFoundException, RefreshAccessException, ConflictException { try { BoxItemRestoreRequestObject obj = BoxItemRestoreRequestObject.restoreItemRequestObject(); if (name != null) { obj.setNewName(name); } return client.getTrashManager().restoreTrashFolder(id, obj); } catch (BoxRestException e) { throw new BoxException("Error untrashing folder: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then item not found throw new NotFoundException("Trashed folder not found " + id, e); } else if (status == 405) { // method_not_allowed throw new NotFoundException("Folder not in the trash " + id, e); } else if (status == 409) { // conflict throw new ConflictException("Folder with the same name as untrashed already exists " + id, e); } throw new BoxException("Error untrashing folder: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when untrashing folder: " + e.getMessage(), e); } } /** * Update file name or/and parent and set given modified date. If file was actually updated (name or/and * parent changed) this method return updated file object or <code>null</code> if file already exists * with such name and parent. * * * @param parentId {@link String} * @param id {@link String} * @param name {@link String} * @param modified {@link Calendar} * @return {@link BoxFile} of actually changed file or <code>null</code> if file already exists with * such name and parent. * @throws BoxException * @throws NotFoundException * @throws RefreshAccessException * @throws ConflictException */ BoxFile updateFile(String parentId, String id, String name, Calendar modified) throws BoxException, NotFoundException, RefreshAccessException, ConflictException { BoxFile existing = readFile(id); int attemts = 0; boolean nameChanged = !existing.getName().equals(name); boolean parentChanged = !existing.getParent().getId().equals(parentId); while ((nameChanged || parentChanged) && attemts < 3) { attemts++; try { // if name or parent changed - we do actual update, we ignore modified date changes // otherwise, if name the same, Box service will respond with error 409 (conflict) BoxFileRequestObject obj = BoxFileRequestObject.getRequestObject(); // obj.setIfMatch(etag); // TODO use it if (nameChanged) { obj.setName(name); } if (parentChanged) { obj.setParent(parentId); } obj.put("modified_at", formatDate(modified)); return client.getFilesManager().updateFileInfo(id, obj); } catch (BoxRestException e) { throw new BoxException("Error updating file: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then item not found throw new NotFoundException("File not found " + id, e); } else if (status == 409) { // conflict, try again if (attemts < 3) { if (LOG.isDebugEnabled()) { LOG.debug( "File with the same name as updated already exists " + id + ". Trying again."); } existing = readFile(id); nameChanged = !existing.getName().equalsIgnoreCase(name); parentChanged = !existing.getParent().getId().equals(parentId); } else { throw new ConflictException("File with the same name as updated already exists " + id); } } else { throw new BoxException("Error updating file: " + e.getMessage(), e); } } catch (UnsupportedEncodingException e) { throw new BoxException("Error updating file: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when updating file: " + e.getMessage(), e); } } return existing; } BoxFile updateFileContent(String parentId, String id, String name, Calendar modified, InputStream data) throws BoxException, NotFoundException, RefreshAccessException { try { BoxFileUploadRequestObject obj = BoxFileUploadRequestObject.uploadFileRequestObject(parentId, name, data); obj.setLocalFileLastModifiedAt(modified.getTime()); obj.put("modified_at", formatDate(modified)); return client.getFilesManager().uploadNewVersion(id, obj); } catch (BoxJSONException e) { throw new BoxException("Error uploading new version of file: " + e.getMessage(), e); } catch (BoxRestException e) { throw new BoxException("Error uploading new version of file: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then item not found throw new NotFoundException("File not found " + id, e); } throw new BoxException("Error uploading new version of file: " + e.getMessage(), e); } catch (UnsupportedEncodingException e) { throw new BoxException("Error uploading new version of file: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when uploading new version of file: " + e.getMessage(), e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new BoxException("File " + name + ", new version uploading interrupted.", e); } } /** * Update folder name or/and parent and set given modified date. If folder was actually updated (name or/and * parent changed) this method return updated folder object or <code>null</code> if folder already exists * with such name and parent. * * @param parentId {@link String} * @param id {@link String} * @param name {@link String} * @param modified {@link Calendar} * @return {@link BoxFolder} of actually changed folder or <code>null</code> if folder already exists with * such name and parent. * @throws BoxException * @throws NotFoundException * @throws RefreshAccessException * @throws ConflictException */ BoxFolder updateFolder(String parentId, String id, String name, Calendar modified) throws BoxException, NotFoundException, RefreshAccessException, ConflictException { BoxFolder existing = readFolder(id); int attemts = 0; boolean nameChanged = !existing.getName().equals(name); boolean parentChanged = !existing.getParent().getId().equals(parentId); while ((nameChanged || parentChanged) && attemts < 3) { attemts++; // if name or parent changed - we do actual update, we ignore modified date changes // otherwise, if name the same, Box service will respond with error 409 (conflict) try { BoxFolderRequestObject obj = BoxFolderRequestObject.createFolderRequestObject(name, parentId); obj.put("modified_at", formatDate(modified)); return client.getFoldersManager().updateFolderInfo(id, obj); } catch (BoxRestException e) { throw new BoxException("Error updating folder: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then item not found throw new NotFoundException("Folder not found " + id, e); } else if (status == 409) { // conflict, try again if (attemts < 3) { if (LOG.isDebugEnabled()) { LOG.debug("Folder with the same name as updated already exists " + id + ". Trying again."); } existing = readFolder(id); nameChanged = !existing.getName().equalsIgnoreCase(name); parentChanged = !existing.getParent().getId().equals(parentId); } else { throw new ConflictException("Folder with the same name as updated already exists " + id); } } else { throw new BoxException("Error updating folder: " + e.getMessage(), e); } } catch (UnsupportedEncodingException e) { throw new BoxException("Error updating folder: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when updating folder: " + e.getMessage(), e); } } return existing; } /** * Copy file to a new one. If file was successfully copied this method return new file object. * * * @param id {@link String} * @param parentId {@link String} * @param name {@link String} * @param modified {@link Calendar} * @return {@link BoxFile} of actually copied file. * @throws BoxException * @throws NotFoundException * @throws RefreshAccessException * @throws ConflictException */ BoxFile copyFile(String id, String parentId, String name) throws BoxException, NotFoundException, RefreshAccessException, ConflictException { try { BoxItemCopyRequestObject obj = BoxItemCopyRequestObject.copyItemRequestObject(parentId); // obj.setIfMatch(etag); // TODO use it obj.setName(name); return client.getFilesManager().copyFile(id, obj); } catch (BoxRestException e) { throw new BoxException("Error copying file: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then item not found throw new NotFoundException("File not found " + id, e); } else if (status == 409) { // conflict, try again if (LOG.isDebugEnabled()) { LOG.debug("File with the same name as copying already exists " + id + ". Trying again."); } throw new ConflictException("File with the same name as copying already exists " + id); } throw new BoxException("Error copying file: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when copying file: " + e.getMessage(), e); } } /** * Copy folder to a new one. If folder was successfully copied this method return new folder object. * * @param id {@link String} * @param parentId {@link String} * @param name {@link String} * @return {@link BoxFile} of actually copied folder. * @throws BoxException * @throws NotFoundException * @throws RefreshAccessException * @throws ConflictException */ BoxFolder copyFolder(String id, String parentId, String name) throws BoxException, NotFoundException, RefreshAccessException, ConflictException { try { BoxItemCopyRequestObject obj = BoxItemCopyRequestObject.copyItemRequestObject(parentId); // obj.setIfMatch(etag); // TODO use it obj.setName(name); return client.getFoldersManager().copyFolder(id, obj); } catch (BoxRestException e) { throw new BoxException("Error copying folder: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then item not found throw new NotFoundException("Folder not found " + id, e); } else if (status == 409) { // conflict, try again if (LOG.isDebugEnabled()) { LOG.debug("Folder with the same name as copying already exists " + id + ". Trying again."); } throw new ConflictException("Folder with the same name as copying already exists " + id); } throw new BoxException("Error copying folder: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when copying folder: " + e.getMessage(), e); } } BoxFile readFile(String id) throws BoxException, NotFoundException, RefreshAccessException { try { return client.getFilesManager().getFile(id, new BoxDefaultRequestObject()); } catch (BoxRestException e) { throw new BoxException("Error reading file: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then item not found throw new NotFoundException("File not found " + id, e); } throw new BoxException("Error reading file: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when reading file: " + e.getMessage(), e); } } BoxFolder readFolder(String id) throws BoxException, NotFoundException, RefreshAccessException { try { return client.getFoldersManager().getFolder(id, new BoxDefaultRequestObject()); } catch (BoxRestException e) { throw new BoxException("Error reading folder: " + e.getMessage(), e); } catch (BoxServerException e) { int status = getErrorStatus(e); if (status == 404 || status == 412) { // not_found or precondition_failed - then item not found throw new NotFoundException("Folder not found " + id, e); } throw new BoxException("Error reading folder: " + e.getMessage(), e); } catch (AuthFatalFailureException e) { checkTokenState(); throw new BoxException("Authentication error when reading folder: " + e.getMessage(), e); } } /** * Current user's enterprise name. Can be <code>null</code> if user doesn't belong to any enterprise. * * @return {@link String} user's enterprise name or <code>null</code> */ String getEnterpriseName() { return enterpriseName; } /** * Current user's enterprise ID. Can be <code>null</code> if user doesn't belong to any enterprise. * * @return {@link String} user's enterprise ID or <code>null</code> */ String getEnterpriseId() { return enterpriseId; } /** * Current user's custom domain (actual for enterprise users). Can be <code>null</code> if user doesn't have * a custom domain. * * @return {@link String} user's custom domain or <code>null</code> */ String getCustomDomain() { return customDomain; } // ********* internal ********* /** * Find server error status. * * @param e {@link BoxServerException} * @return int */ private int getErrorStatus(BoxServerException e) { BoxServerError se = e.getError(); if (se != null) { int status = se.getStatus(); if (status != 0) { return status; } } return e.getStatusCode(); } /** * Check if need new access token from user (refresh token already expired). * * @throws RefreshAccessException if client failed to refresh the access token and need new new token */ private void checkTokenState() throws RefreshAccessException { if (OAuthTokenState.FAIL.equals(client.getAuthState())) { // we need new access token (refresh token already expired here) throw new RefreshAccessException("Authentication failure. Reauthenticate."); } } private void initUser() throws BoxException, RefreshAccessException, NotFoundException { com.box.boxjavalibv2.dao.BoxUser user = getCurrentUser(); String avatarUrl = user.getAvatarUrl(); Matcher m = BOX_URL_CUSTOM_PATTERN.matcher(avatarUrl); if (m.matches()) { // we have custom domain (actual for Enterprise users) customDomain = m.group(1); } BoxEnterprise enterprise = user.getEnterprise(); if (enterprise != null) { enterpriseName = enterprise.getName(); enterpriseId = enterprise.getExtraData("id").toString(); } } /** * Correct file link for enterprise users with the enterprise custom domain (if it present). * * @param fileLink {@link String} * @return file link optionally with added custom domain. */ private String link(String fileLink) { if (customDomain != null) { Matcher m = BOX_URL_MAKE_CUSTOM_PATTERN.matcher(fileLink); if (m.matches()) { // we need add custom domain to the link host name return m.replaceFirst("$1://" + customDomain + ".$2"); } // else, link already starts with custom domain (actual for Enterprise users) } // else, custom domain not available return fileLink; } }