All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.emc.atmos.api.jersey.AtmosApiClient Maven / Gradle / Ivy

Go to download

EMC Atmos Client for Java - provides REST access to object data on EMC platforms using the Atmos API.

There is a newer version: 3.2.1
Show newest version
/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2013-2018, Dell EMC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package com.emc.atmos.api.jersey;

import com.emc.atmos.AtmosException;
import com.emc.atmos.api.*;
import com.emc.atmos.api.bean.*;
import com.emc.atmos.api.multipart.MultipartEntity;
import com.emc.atmos.api.request.*;
import com.emc.util.HttpUtil;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.ClientFilter;
import org.apache.log4j.Logger;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

/**
 * Reference implementation of AtmosApi.
 * 

* This implementation uses the JAX-RS reference implementation (Jersey) as it's REST client. When sending or * receiving data, the following content handlers are supported by default. You may add your own * MessageBodyReader/MessageBodyWriter implementations by using the optional constructor, however be sure to return a * positive value from MessageBodyWriter.getSize() as this sets the content-length of the request, which is required by * Atmos. Be sure to use the appropriate content-type associated with each object type or the handlers will not * understand the request. *

*

* * * * * * * * *
Object Type (class)Expected Content-Type(s)
byte[]*any*
java.lang.String*any*
java.io.File (send-only)*any*
java.io.InputStream (send-only)*any*
com.emc.atmos.api.BufferSegment (send-only)*any*
any annotated JAXB root element beantext/xml, application/xml
com.emc.atmos.api.multipart.MultipartEntity (receive-only)multipart/*
*

* Also keep in mind that you can always send/receive byte[] and do your own conversion as that has always been * supported. *

* Note: this implementation is not supported with Atmos versions below 1.4.2. *

* To use, simply pass a new AtmosConfig object to the constructor like so: *

 *     URI atmosEndpoint = new URI( "http://api.atmosonline.com" ); // or use your private cloud endpoint/load balancer
 *     AtmosApi atmos = new AtmosApiClient( new AtmosConfig( "my_full_token_id", "my_secret_key", atmosEndpoint ) );
 * 
*

* You can also specify multiple endpoints and have each request round-robin between them like so: *

 *     URI endpoint1 = new URI( "https://10.0.0.101" ); // 1st Atmos node
 *     URI endpoint2 = new URI( "https://10.0.0.102" ); // 2nd Atmos node
 *     URI endpoint3 = new URI( "https://10.0.0.103" ); // 3rd Atmos node
 *     URI endpoint4 = new URI( "https://10.0.0.104" ); // 4th Atmos node
 *     AtmosApi atmos = new AtmosApiClient( new AtmosConfig( "my_full_token_id", "my_secret_key",
 *                                          endpoint1, endpoint2, endpoint3, endpoint4 ) );
 * 
*

* To create an object, simply pass the object in to one of the createObject methods. The object type must be one of * the supported types above. *

 *     String stringContent = "Hello World!";
 *     ObjectId oid1 = atmos.createObject( stringContent, "text/plain" );
 *
 *     File fileContent = new File( "spreadsheet.xls" );
 *     ObjectId oid2 = atmos.createObject( fileContent, "application/vnd.ms-excel" );
 *
 *     byte[] binaryContent;
 *     ... // load binary content to store as an object
 *     ObjectId oid3 = atmos.createObject( binaryContent, null ); // default content-type is application/octet-stream
 * 
*

* To read an object, specify the type of object you want to receive from a readObject method. The same rules apply to * this type. *

 *     String stringContent = atmos.readObject( oid1, String.class );
 *
 *     byte[] fileContent = atmos.readObject( oid2, byte[].class );
 *     // do something with file content (stream to client? save in local filesystem?)
 *
 *     byte[] binaryContent = atmos.readObject( oid3, byte[].class );
 * 
*/ public class AtmosApiClient extends AbstractAtmosApi { private static final Logger l4j = Logger.getLogger( AtmosApiClient.class ); protected Client client; protected Client client100; public AtmosApiClient( AtmosConfig config ) { this( config, (List>>) null, null ); } public AtmosApiClient( AtmosConfig config, List>> readers, List>> writers ) { this( config, JerseyApacheUtil.createApacheClient( config, false, readers, writers ), JerseyApacheUtil.createApacheClient( config, true, readers, writers ) ); } protected AtmosApiClient( AtmosConfig config, Client client, Client client100 ) { super( config ); this.client = client; // without writing our own client implementation, the only way to discriminate requests that enable // Expect: 100-continue behavior is to have two clients; one with the feature enabled and one without. this.client100 = client100; } /** * Adds a ClientFilter to the Jersey client used to make REST requests. Useful to provide your own in-line content * or header manipulation. */ public void addClientFilter( ClientFilter filter ) { client.addFilter( filter ); client100.addFilter( filter ); } @Override public ServiceInformation getServiceInformation() { ClientResponse response = client.resource( config.resolvePath( "service", null ) ).get( ClientResponse.class ); ServiceInformation serviceInformation = response.getEntity( ServiceInformation.class ); String featureString = response.getHeaders().getFirst( RestUtil.XHEADER_FEATURES ); if ( featureString != null ) { for ( String feature : featureString.split( "," ) ) serviceInformation.addFeatureFromHeaderName( feature.trim() ); } // legacy String utf8String = response.getHeaders().getFirst( RestUtil.XHEADER_SUPPORT_UTF8 ); if ( utf8String != null && Boolean.valueOf( utf8String ) ) serviceInformation.addFeature( ServiceInformation.Feature.Utf8 ); response.close(); return serviceInformation; } @Override public long calculateServerClockSkew() { WebResource resource = client.resource( config.resolvePath( "", null ) ); resource.setProperty( ErrorFilter.NO_EXCEPTIONS, true ); ClientResponse response = resource.get( ClientResponse.class ); if ( response.getResponseDate() == null ) throw new AtmosException( "Response date is null", response.getStatus() ); config.setServerClockSkew( System.currentTimeMillis() - response.getResponseDate().getTime() ); response.close(); return config.getServerClockSkew(); } @Override public CreateObjectResponse createObject( CreateObjectRequest request ) { ClientResponse response = build( request ).post( ClientResponse.class, getContent( request ) ); response.close(); return fillResponse( new CreateObjectResponse(), response ); } @Override public ReadObjectResponse readObject( ReadObjectRequest request, Class objectType ) throws IOException { if ( request.getRanges() != null && request.getRanges().size() > 1 && !MultipartEntity.class.isAssignableFrom( objectType ) ) l4j.warn( "multiple ranges imply a multi-part response. you should ask for MultipartEntity instead of " + objectType.getSimpleName() ); ClientResponse response = build( request ).get( ClientResponse.class ); ReadObjectResponse ret = new ReadObjectResponse( response.getEntity( objectType ) ); response.close(); return fillResponse( ret, response ); } @Override public ReadObjectResponse readObjectStream( ObjectIdentifier identifier, Range range ) { ClientResponse response = build( new ReadObjectRequest().identifier( identifier ).ranges( range ) ) .get( ClientResponse.class ); return fillResponse( new ReadObjectResponse( response.getEntityInputStream() ), response ); } @Override public BasicResponse updateObject( UpdateObjectRequest request ) { ClientResponse response = build( request ).put( ClientResponse.class, getContent( request ) ); response.close(); return fillResponse( new BasicResponse(), response ); } @Override public void delete( ObjectIdentifier identifier ) { URI uri = config.resolvePath( identifier.getRelativeResourcePath(), null ); WebResource.Builder builder = client.resource( uri ).getRequestBuilder(); if ( identifier instanceof ObjectKey ) builder.header( RestUtil.XHEADER_POOL, ((ObjectKey) identifier).getBucket() ); builder.delete(); } @Override public ObjectId createDirectory( ObjectPath path ) { if ( !path.isDirectory() ) throw new AtmosException( "Path must be a directory" ); CreateObjectRequest request = new CreateObjectRequest().identifier( path ); ClientResponse response = build( request ).post( ClientResponse.class ); response.close(); return RestUtil.parseObjectId( response.getLocation().getPath() ); } @Override public ObjectId createDirectory( ObjectPath path, Acl acl, Metadata... metadata ) { if ( !path.isDirectory() ) throw new AtmosException( "Path must be a directory" ); CreateObjectRequest request = new CreateObjectRequest().identifier( path ).acl( acl ); request.userMetadata( metadata ); ClientResponse response = build( request ).post( ClientResponse.class ); response.close(); return RestUtil.parseObjectId( response.getLocation().getPath() ); } @Override public ListDirectoryResponse listDirectory( ListDirectoryRequest request ) { if ( !request.getPath().isDirectory() ) throw new AtmosException( "Path must be a directory" ); ClientResponse response = build( request ).get( ClientResponse.class ); request.setToken( response.getHeaders().getFirst( RestUtil.XHEADER_TOKEN ) ); if ( request.getToken() != null ) l4j.info( "Results truncated. Call listDirectory again for next page of results." ); ListDirectoryResponse ret = response.getEntity( ListDirectoryResponse.class ); response.close(); return fillResponse( ret, response ); } @Override public void move( ObjectPath oldPath, ObjectPath newPath, boolean overwrite ) { WebResource resource = client.resource( config.resolvePath( oldPath.getRelativeResourcePath(), "rename" ) ); WebResource.Builder builder = resource.getRequestBuilder(); builder.header( RestUtil.XHEADER_PATH, config.isEncodeUtf8() ? HttpUtil.encodeUtf8( newPath.getPath() ) : newPath.getPath() ); if ( config.isEncodeUtf8() ) builder.header( RestUtil.XHEADER_UTF8, "true" ); if ( overwrite ) builder.header( RestUtil.XHEADER_FORCE, "true" ); // workaround for clients that set a default content-type for POSTs builder.type( RestUtil.TYPE_DEFAULT ); builder.post(); } @Override public Map getUserMetadataNames( ObjectIdentifier identifier ) { URI uri = config.resolvePath( identifier.getRelativeResourcePath(), "metadata/tags" ); WebResource.Builder builder = client.resource( uri ).getRequestBuilder(); if ( identifier instanceof ObjectKey ) builder.header( RestUtil.XHEADER_POOL, ((ObjectKey) identifier).getBucket() ); if ( config.isEncodeUtf8() ) builder.header( RestUtil.XHEADER_UTF8, "true" ); ClientResponse response = builder.get( ClientResponse.class ); Map metaNames = new TreeMap(); String nameString = response.getHeaders().getFirst( RestUtil.XHEADER_TAGS ); if ( nameString != null ) { for ( String name : nameString.split( "," ) ) metaNames.put( config.isEncodeUtf8() ? HttpUtil.decodeUtf8( name.trim() ) : name.trim(), false ); } nameString = response.getHeaders().getFirst( RestUtil.XHEADER_LISTABLE_TAGS ); if ( nameString != null ) { for ( String name : nameString.split( "," ) ) metaNames.put( config.isEncodeUtf8() ? HttpUtil.decodeUtf8( name.trim() ) : name.trim(), true ); } response.close(); return metaNames; } @Override public Map getUserMetadata( ObjectIdentifier identifier, String... metadataNames ) { URI uri = config.resolvePath( identifier.getRelativeResourcePath(), "metadata/user" ); WebResource.Builder builder = client.resource( uri ).getRequestBuilder(); if ( identifier instanceof ObjectKey ) builder.header( RestUtil.XHEADER_POOL, ((ObjectKey) identifier).getBucket() ); if ( metadataNames != null ) { for ( String name : metadataNames ) { if ( config.isEncodeUtf8() ) name = HttpUtil.encodeUtf8( name ); builder.header( RestUtil.XHEADER_TAGS, name ); } } if ( config.isEncodeUtf8() ) builder.header( RestUtil.XHEADER_UTF8, "true" ); ClientResponse response = builder.get( ClientResponse.class ); Map metaMap = new TreeMap(); metaMap.putAll( RestUtil.parseMetadataHeader( response.getHeaders().getFirst( RestUtil.XHEADER_META ), false, config.isEncodeUtf8() ) ); metaMap.putAll( RestUtil.parseMetadataHeader( response.getHeaders().getFirst( RestUtil.XHEADER_LISTABLE_META ), true, config.isEncodeUtf8() ) ); response.close(); return metaMap; } @Override public Map getSystemMetadata( ObjectIdentifier identifier, String... metadataNames ) { URI uri = config.resolvePath( identifier.getRelativeResourcePath(), "metadata/system" ); WebResource.Builder builder = client.resource( uri ).getRequestBuilder(); if ( identifier instanceof ObjectKey ) builder.header( RestUtil.XHEADER_POOL, ((ObjectKey) identifier).getBucket() ); if ( metadataNames != null ) { for ( String name : metadataNames ) { if ( config.isEncodeUtf8() ) name = HttpUtil.encodeUtf8( name ); builder.header( RestUtil.XHEADER_TAGS, name ); } } if ( config.isEncodeUtf8() ) builder.header( RestUtil.XHEADER_UTF8, "true" ); ClientResponse response = builder.get( ClientResponse.class ); response.close(); return RestUtil.parseMetadataHeader( response.getHeaders().getFirst( RestUtil.XHEADER_META ), false, config.isEncodeUtf8() ); } @Override public boolean objectExists( ObjectIdentifier identifier ) { try { getSystemMetadata( identifier ); return true; } catch ( AtmosException e ) { if ( e.getErrorCode() == 1003 ) return false; throw e; } } @Override public ObjectMetadata getObjectMetadata( ObjectIdentifier identifier ) { URI uri = config.resolvePath( identifier.getRelativeResourcePath(), null ); WebResource.Builder builder = client.resource( uri ).getRequestBuilder(); if ( identifier instanceof ObjectKey ) builder.header( RestUtil.XHEADER_POOL, ((ObjectKey) identifier).getBucket() ); if ( config.isEncodeUtf8() ) builder.header( RestUtil.XHEADER_UTF8, "true" ); ClientResponse response = builder.head(); Acl acl = new Acl( RestUtil.parseAclHeader( response.getHeaders().getFirst( RestUtil.XHEADER_USER_ACL ) ), RestUtil.parseAclHeader( response.getHeaders() .getFirst( RestUtil.XHEADER_GROUP_ACL ) ) ); Map metaMap = new TreeMap(); metaMap.putAll( RestUtil.parseMetadataHeader( response.getHeaders().getFirst( RestUtil.XHEADER_META ), false, config.isEncodeUtf8() ) ); metaMap.putAll( RestUtil.parseMetadataHeader( response.getHeaders() .getFirst( RestUtil.XHEADER_LISTABLE_META ), true, config.isEncodeUtf8() ) ); String wsChecksumHeader = response.getHeaders().getFirst( RestUtil.XHEADER_WSCHECKSUM ); ChecksumValue wsChecksum = wsChecksumHeader == null ? null : new ChecksumValueImpl( wsChecksumHeader ); String serverChecksumHeader = response.getHeaders().getFirst( RestUtil.XHEADER_CONTENT_CHECKSUM ); ChecksumValue serverChecksum = serverChecksumHeader == null ? null : new ChecksumValueImpl( serverChecksumHeader ); String retentionPeriod = response.getHeaders().getFirst( RestUtil.XHEADER_RETENTION_PERIOD ); response.close(); ObjectMetadata metadata = new ObjectMetadata( metaMap, acl, response.getType().toString(), wsChecksum, serverChecksum ); if ( retentionPeriod != null ) metadata.setRetentionPeriod( Long.parseLong( retentionPeriod ) ); metadata.setRetentionPolicy( response.getHeaders().getFirst( RestUtil.XHEADER_RETENTION_POLICY ) ); metadata.setETag( response.getHeaders().getFirst( RestUtil.HEADER_ETAG ) ); return metadata; } @Override public void setUserMetadata( ObjectIdentifier identifier, Metadata... metadata ) { URI uri = config.resolvePath( identifier.getRelativeResourcePath(), "metadata/user" ); WebResource.Builder builder = client.resource( uri ).getRequestBuilder(); if ( identifier instanceof ObjectKey ) builder.header( RestUtil.XHEADER_POOL, ((ObjectKey) identifier).getBucket() ); for ( Metadata oneMetadata : metadata ) { if ( oneMetadata.isListable() ) { builder.header( RestUtil.XHEADER_LISTABLE_META, config.isEncodeUtf8() ? oneMetadata.toASCIIString() : oneMetadata.toString() ); } else { builder.header( RestUtil.XHEADER_META, config.isEncodeUtf8() ? oneMetadata.toASCIIString() : oneMetadata.toString() ); } } // workaround for clients that set a default content-type for POSTs builder.type( RestUtil.TYPE_DEFAULT ); if ( config.isEncodeUtf8() ) builder.header( RestUtil.XHEADER_UTF8, "true" ); builder.post(); } @Override public void deleteUserMetadata( ObjectIdentifier identifier, String... names ) { URI uri = config.resolvePath( identifier.getRelativeResourcePath(), "metadata/user" ); WebResource.Builder builder = client.resource( uri ).getRequestBuilder(); if ( identifier instanceof ObjectKey ) builder.header( RestUtil.XHEADER_POOL, ((ObjectKey) identifier).getBucket() ); for ( String name : names ) { if ( config.isEncodeUtf8() ) name = HttpUtil.encodeUtf8( name ); builder.header( RestUtil.XHEADER_TAGS, name ); } if ( config.isEncodeUtf8() ) builder.header( RestUtil.XHEADER_UTF8, "true" ); builder.delete(); } @Override public Set listMetadata( String metadataName ) { URI uri = config.resolvePath( "objects", "listabletags" ); WebResource.Builder builder = client.resource( uri ).getRequestBuilder(); if ( metadataName != null ) builder.header( RestUtil.XHEADER_TAGS, config.isEncodeUtf8() ? HttpUtil.encodeUtf8( metadataName ) : metadataName ); if ( config.isEncodeUtf8() ) builder.header( RestUtil.XHEADER_UTF8, "true" ); ClientResponse response = builder.get( ClientResponse.class ); String headerValue = response.getHeaders().getFirst( RestUtil.XHEADER_LISTABLE_TAGS ); Set names = new TreeSet(); if ( headerValue == null ) return names; for ( String name : headerValue.split( "," ) ) names.add( config.isEncodeUtf8() ? HttpUtil.decodeUtf8( name.trim() ) : name.trim() ); response.close(); return names; } @Override public ListObjectsResponse listObjects( ListObjectsRequest request ) { if ( request.getMetadataName() == null ) throw new AtmosException( "You must specify the name of a listable piece of metadata" ); ClientResponse response; try { response = build( request ).get( ClientResponse.class ); } catch ( AtmosException e ) { // if the name doesn't exist, return an empty result instead of throwing an exception (requested by users) if ( e.getErrorCode() != 1003 ) throw e; ListObjectsResponse lor = new ListObjectsResponse(); lor.setEntries( new ArrayList() ); return lor; } request.setToken( response.getHeaders().getFirst( RestUtil.XHEADER_TOKEN ) ); if ( request.getToken() != null ) l4j.info( "Results truncated. Call listObjects again for next page of results." ); ListObjectsResponse ret = response.getEntity( ListObjectsResponse.class ); response.close(); return fillResponse( ret, response ); } @Override public Acl getAcl( ObjectIdentifier identifier ) { URI uri = config.resolvePath( identifier.getRelativeResourcePath(), "acl" ); WebResource.Builder builder = client.resource( uri ).getRequestBuilder(); if ( identifier instanceof ObjectKey ) builder.header( RestUtil.XHEADER_POOL, ((ObjectKey) identifier).getBucket() ); ClientResponse response = builder.get( ClientResponse.class ); Acl acl = new Acl(); acl.setUserAcl( RestUtil.parseAclHeader( response.getHeaders().getFirst( RestUtil.XHEADER_USER_ACL ) ) ); acl.setGroupAcl( RestUtil.parseAclHeader( response.getHeaders().getFirst( RestUtil.XHEADER_GROUP_ACL ) ) ); response.close(); return acl; } @Override public void setAcl( ObjectIdentifier identifier, Acl acl ) { URI uri = config.resolvePath( identifier.getRelativeResourcePath(), "acl" ); WebResource.Builder builder = client.resource( uri ).getRequestBuilder(); if ( identifier instanceof ObjectKey ) builder.header( RestUtil.XHEADER_POOL, ((ObjectKey) identifier).getBucket() ); if ( acl != null ) { for ( Object value : acl.getUserAclHeader() ) builder.header( RestUtil.XHEADER_USER_ACL, value ); for ( Object value : acl.getGroupAclHeader() ) builder.header( RestUtil.XHEADER_GROUP_ACL, value ); } // workaround for clients that set a default content-type for POSTs builder.type( RestUtil.TYPE_DEFAULT ); builder.post(); } @Override public ObjectInfo getObjectInfo( ObjectIdentifier identifier ) { URI uri = config.resolvePath( identifier.getRelativeResourcePath(), "info" ); WebResource.Builder builder = client.resource( uri ).getRequestBuilder(); if ( identifier instanceof ObjectKey ) builder.header( RestUtil.XHEADER_POOL, ((ObjectKey) identifier).getBucket() ); return builder.get( ObjectInfo.class ); } @Override public ObjectId createVersion( ObjectIdentifier identifier ) { URI uri = config.resolvePath( identifier.getRelativeResourcePath(), "versions" ); WebResource.Builder builder = client.resource( uri ).getRequestBuilder(); if ( identifier instanceof ObjectKey ) builder.header( RestUtil.XHEADER_POOL, ((ObjectKey) identifier).getBucket() ); // workaround for clients that set a default content-type for POSTs builder.type( RestUtil.TYPE_DEFAULT ); ClientResponse response = builder.post( ClientResponse.class ); response.close(); return RestUtil.parseObjectId( response.getLocation().getPath() ); } @Override public ListVersionsResponse listVersions( ListVersionsRequest request ) { ClientResponse response = build( request ).get( ClientResponse.class ); request.setToken( response.getHeaders().getFirst( RestUtil.XHEADER_TOKEN ) ); if ( request.getToken() != null ) l4j.info( "Results truncated. Call listVersions again for next page of results." ); ListVersionsResponse ret = response.getEntity( ListVersionsResponse.class ); response.close(); return fillResponse( ret, response ); } @Override public void restoreVersion( ObjectId objectId, ObjectId versionId ) { URI uri = config.resolvePath( objectId.getRelativeResourcePath(), "versions" ); WebResource.Builder builder = client.resource( uri ).getRequestBuilder(); builder.header( RestUtil.XHEADER_VERSION_OID, versionId ).put(); } @Override public void deleteVersion( ObjectId versionId ) { client.resource( config.resolvePath( versionId.getRelativeResourcePath(), "versions" ) ).delete(); } @Override public CreateAccessTokenResponse createAccessToken( CreateAccessTokenRequest request ) throws MalformedURLException { ClientResponse response = build( request ).post( ClientResponse.class, request.getPolicy() ); URI tokenUri = config.resolvePath( response.getLocation().getPath(), response.getLocation().getQuery() ); response.close(); return fillResponse( new CreateAccessTokenResponse( tokenUri.toURL() ), response ); } @Override public GetAccessTokenResponse getAccessToken( String accessTokenId ) { URI uri = config.resolvePath( "accesstokens/" + accessTokenId, "info" ); ClientResponse response = client.resource( uri ).get( ClientResponse.class ); GetAccessTokenResponse ret = new GetAccessTokenResponse( response.getEntity( AccessToken.class ) ); response.close(); return fillResponse( ret, response ); } @Override public void deleteAccessToken( String accessTokenId ) { client.resource( config.resolvePath( "accesstokens/" + accessTokenId, null ) ).delete(); } @Override public ListAccessTokensResponse listAccessTokens( ListAccessTokensRequest request ) { ClientResponse response = build( request ).get( ClientResponse.class ); request.setToken( response.getHeaders().getFirst( RestUtil.XHEADER_TOKEN ) ); if ( request.getToken() != null ) l4j.info( "Results truncated. Call listAccessTokens again for next page of results." ); ListAccessTokensResponse ret = response.getEntity( ListAccessTokensResponse.class ); response.close(); return fillResponse( ret, response ); } @SuppressWarnings("unchecked") @Override public GenericResponse execute( PreSignedRequest request, Class resultType, Object content ) throws URISyntaxException { WebResource.Builder builder = client.resource( request.getUrl().toURI() ).getRequestBuilder(); addHeaders( builder, request.getHeaders() ).type( request.getContentType() ); ClientResponse response = builder.method( request.getMethod(), ClientResponse.class, content ); GenericResponse ret; if ( InputStream.class.equals( resultType ) ) { ret = (GenericResponse) new GenericResponse( response.getEntityInputStream() ); } else { ret = new GenericResponse( response.getEntity( resultType ) ); response.close(); } return fillResponse( ret, response ); } protected WebResource.Builder build( Request request ) { WebResource resource; if ( request.supports100Continue() && config.isEnableExpect100Continue() && client100 != null ) { // use client with Expect: 100-continue l4j.debug( "Expect: 100-continue is enabled for this request" ); resource = client100.resource( config.resolvePath( request.getServiceRelativePath(), request.getQuery() ) ); } else { resource = client.resource( config.resolvePath( request.getServiceRelativePath(), request.getQuery() ) ); } WebResource.Builder builder = resource.getRequestBuilder(); if ( request instanceof ContentRequest ) { ContentRequest contentRequest = (ContentRequest) request; if ( contentRequest.getContentType() == null ) builder.type( AbstractAtmosApi.DEFAULT_CONTENT_TYPE ); else builder.type( contentRequest.getContentType() ); } else if ( "POST".equals( request.getMethod() ) ) { // workaround for clients that set a default content-type for POSTs builder.type( RestUtil.TYPE_DEFAULT ); } return addHeaders( builder, request.generateHeaders( config.isEncodeUtf8() ) ); } protected WebResource.Builder addHeaders( WebResource.Builder builder, Map> headers ) { for ( String name : headers.keySet() ) { for ( Object value : headers.get( name ) ) { builder.header( name, value ); } } return builder; } /** * Populates a response object with data from the ClientResponse. */ protected T fillResponse( T response, ClientResponse clientResponse ) { Response.StatusType statusType = clientResponse.getStatusInfo(); MediaType type = clientResponse.getType(); URI location = clientResponse.getLocation(); response.setHttpStatus( clientResponse.getStatus() ); response.setHttpMessage( statusType == null ? null : statusType.getReasonPhrase() ); response.setHeaders( clientResponse.getHeaders() ); response.setContentType( type == null ? null : type.toString() ); response.setContentLength( clientResponse.getLength() ); response.setLocation( location == null ? null : location.toString() ); if ( clientResponse.getHeaders() != null ) { // workaround for Github Issue #3 response.setDate( HttpUtil.safeHeaderParse( clientResponse.getHeaders().getFirst( RestUtil.HEADER_DATE ) ) ); response.setLastModified( HttpUtil.safeHeaderParse( clientResponse.getHeaders().getFirst( RestUtil.HEADER_LAST_MODIFIED ) ) ); response.setETag( clientResponse.getHeaders().getFirst( RestUtil.HEADER_ETAG ) ); } return response; } protected Object getContent( ContentRequest request ) { Object content = request.getContent(); if ( content == null ) return new byte[0]; // need this to provide Content-Length: 0 else if ( content instanceof InputStream ) { if ( request.getContentLength() < 0 ) throw new UnsupportedOperationException( "Content request with input stream must provide content length" ); if ( request.getContentLength() == 0 ) l4j.info( "Content request with input stream and zero-length will not send any data" ); return new MeasuredInputStream( (InputStream) content, request.getContentLength() ); } else return content; } @Override public String createSubtenant(CreateSubtenantRequest request) { ClientResponse response = build( request ).put( ClientResponse.class ); return response.getHeaders().get( "subtenantID" ).get( 0 ); } @Override public void deleteSubtenant( String subtenantId ) { client.resource( config.resolvePath( "subtenant/" + subtenantId, null ) ).delete(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy