com.emc.atmos.api.jersey.EsuApiJerseyAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.emc.atmos.api.jersey.EsuApiJerseyAdapter.java

Source

/*
 * Copyright 2013 EMC Corporation. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
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.request.*;
import com.emc.esu.api.Acl;
import com.emc.esu.api.BufferSegment;
import com.emc.esu.api.*;
import com.emc.esu.api.DirectoryEntry;
import com.emc.esu.api.Metadata;
import com.emc.esu.api.ObjectId;
import com.emc.esu.api.ObjectInfo;
import com.emc.esu.api.ObjectMetadata;
import com.emc.esu.api.ObjectPath;
import com.emc.esu.api.ServiceInformation;
import com.emc.esu.api.rest.AbstractEsuRestApi;
import com.emc.util.StreamUtil;
import org.apache.commons.codec.binary.Base64;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.*;
import java.net.URISyntaxException;
import java.util.*;

public class EsuApiJerseyAdapter extends AbstractEsuRestApi {
    private JAXBContext jaxbContext;
    private ThreadLocal<Marshaller> marshaller = new ThreadLocal<Marshaller>();
    private AtmosApi adaptee;

    public EsuApiJerseyAdapter(AtmosConfig config)
            throws URISyntaxException, UnsupportedEncodingException, JAXBException {
        super(config.getEndpoints()[0].getHost(), config.getEndpoints()[0].getPort(), config.getTokenId(),
                new String(Base64.encodeBase64(config.getSecretKey()), "UTF-8"));
        adaptee = new AtmosApiClient(config);
        jaxbContext = JAXBContext.newInstance(com.emc.atmos.api.bean.ObjectInfo.class);
    }

    @Override
    public long calculateServerOffset() {
        return adaptee.calculateServerClockSkew();
    }

    @Override
    public ObjectId createObjectFromStream(Acl acl, MetadataList metadata, InputStream data, long length,
            String mimeType) {
        try {
            CreateObjectRequest request = new CreateObjectRequest();
            request.acl(adaptAcl(acl)).userMetadata(adaptMetadata(metadata)).content(data).contentLength(length)
                    .contentType(mimeType);
            return (ObjectId) adaptIdentifier(adaptee.createObject(request).getObjectId());
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public ObjectId createObjectFromStreamOnPath(ObjectPath path, Acl acl, MetadataList metadata, InputStream data,
            long length, String mimeType) {
        try {
            CreateObjectRequest request = new CreateObjectRequest();
            request.identifier(adaptIdentifier(path)).acl(adaptAcl(acl)).userMetadata(adaptMetadata(metadata))
                    .content(data).contentLength(length).contentType(mimeType);
            return (ObjectId) adaptIdentifier(adaptee.createObject(request).getObjectId());
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public ObjectId createObjectFromSegment(Acl acl, MetadataList metadata, BufferSegment data, String mimeType,
            Checksum checksum) {
        try {
            CreateObjectRequest request = new CreateObjectRequest();
            request.acl(adaptAcl(acl)).userMetadata(adaptMetadata(metadata)).content(adaptBuffer(data))
                    .contentType(mimeType).wsChecksum(adaptChecksum(checksum, data));
            return (ObjectId) adaptIdentifier(adaptee.createObject(request).getObjectId());
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public ObjectId createObjectFromSegmentOnPath(ObjectPath path, Acl acl, MetadataList metadata,
            BufferSegment data, String mimeType, Checksum checksum) {
        try {
            CreateObjectRequest request = new CreateObjectRequest();
            request.identifier(adaptIdentifier(path)).acl(adaptAcl(acl)).userMetadata(adaptMetadata(metadata))
                    .content(adaptBuffer(data)).contentType(mimeType).wsChecksum(adaptChecksum(checksum, data));
            return (ObjectId) adaptIdentifier(adaptee.createObject(request).getObjectId());
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public void updateObjectFromStream(Identifier id, Acl acl, MetadataList metadata, Extent extent,
            InputStream data, long length, String mimeType) {
        try {
            UpdateObjectRequest request = new UpdateObjectRequest();
            request.identifier(adaptIdentifier(id)).acl(adaptAcl(acl)).userMetadata(adaptMetadata(metadata))
                    .range(adaptExtent(extent)).content(data).contentLength(length).contentType(mimeType);
            adaptee.updateObject(request);
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public void updateObjectFromSegment(Identifier id, Acl acl, MetadataList metadata, Extent extent,
            BufferSegment data, String mimeType, Checksum checksum) {
        try {
            UpdateObjectRequest request = new UpdateObjectRequest();
            request.identifier(adaptIdentifier(id)).acl(adaptAcl(acl)).userMetadata(adaptMetadata(metadata))
                    .range(adaptExtent(extent)).content(adaptBuffer(data)).contentType(mimeType)
                    .wsChecksum(adaptChecksum(checksum, data));
            adaptee.updateObject(request);
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public void setUserMetadata(Identifier id, MetadataList metadata) {
        try {
            adaptee.setUserMetadata(adaptIdentifier(id), adaptMetadata(metadata));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public void setAcl(Identifier id, Acl acl) {
        try {
            adaptee.setAcl(adaptIdentifier(id), adaptAcl(acl));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public void deleteObject(Identifier id) {
        try {
            adaptee.delete(adaptIdentifier(id));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public void deleteVersion(ObjectId id) {
        try {
            adaptee.deleteVersion((com.emc.atmos.api.ObjectId) adaptIdentifier(id));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public MetadataList getUserMetadata(Identifier id, MetadataTags tags) {
        try {
            return adaptMetadata(adaptee.getUserMetadata(adaptIdentifier(id), adaptNames(tags)));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public MetadataList getSystemMetadata(Identifier id, MetadataTags tags) {
        try {
            return adaptMetadata(adaptee.getSystemMetadata(adaptIdentifier(id), adaptNames(tags)));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public byte[] readObject(Identifier id, Extent extent, byte[] buffer, Checksum checksum) {
        try {
            return readObject(adaptIdentifier(id), extent, buffer, checksum);
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public InputStream readObjectStream(Identifier id, Extent extent) {
        try {
            return adaptee.readObjectStream(adaptIdentifier(id), adaptExtent(extent)).getObject();
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public Acl getAcl(Identifier id) {
        try {
            return adaptAcl(adaptee.getAcl(adaptIdentifier(id)));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public void deleteUserMetadata(Identifier id, MetadataTags tags) {
        try {
            adaptee.deleteUserMetadata(adaptIdentifier(id), adaptNames(tags));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public List<Identifier> listVersions(Identifier id) {
        try {
            List<Identifier> identifiers = new ArrayList<Identifier>();
            List<Version> versions = listVersions((ObjectId) id, null);
            for (Version version : versions) {
                identifiers.add(version.getId());
            }
            return identifiers;
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public List<Version> listVersions(ObjectId id, ListOptions options) {
        try {
            List<Version> versions = new ArrayList<Version>();
            ListVersionsRequest request = new ListVersionsRequest();
            request.setObjectId((com.emc.atmos.api.ObjectId) adaptIdentifier(id));
            if (options != null)
                request.limit(options.getLimit()).token(options.getToken());
            ListVersionsResponse response = adaptee.listVersions(request);
            if (response.getVersions() != null) {
                for (ObjectVersion version : response.getVersions()) {
                    versions.add(adaptVersion(version));
                }
            }
            if (options != null)
                options.setToken(request.getToken());
            return versions;
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public ObjectId versionObject(Identifier id) {
        try {
            return (ObjectId) adaptIdentifier(adaptee.createVersion(adaptIdentifier(id)));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public List<ObjectResult> listObjects(String tag, ListOptions options) {
        try {
            List<ObjectResult> results = new ArrayList<ObjectResult>();
            ListObjectsRequest request = new ListObjectsRequest().metadataName(tag);
            if (options != null) {
                request.limit(options.getLimit());
                request.token(options.getToken()).includeMetadata(options.isIncludeMetadata());
                if (options.getUserMetadata() != null)
                    request.userMetadataNames(
                            options.getUserMetadata().toArray(new String[options.getUserMetadata().size()]));
                if (options.getSystemMetadata() != null)
                    request.systemMetadataNames(
                            options.getSystemMetadata().toArray(new String[options.getSystemMetadata().size()]));
            }
            for (ObjectEntry entry : adaptee.listObjects(request).getEntries()) {
                results.add(adaptObjectEntry(entry));
            }
            if (options != null)
                options.setToken(request.getToken());
            return results;
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public MetadataTags getListableTags(MetadataTag tag) {
        try {
            return getListableTags(tag.getName());
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public MetadataTags getListableTags(String tag) {
        try {
            return adaptTags(adaptee.listMetadata(tag));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public MetadataTags listUserMetadataTags(Identifier id) {
        try {
            return adaptTags(adaptee.getUserMetadataNames(adaptIdentifier(id)));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public List<ObjectId> queryObjects(String xquery) {
        throw new UnsupportedOperationException("This implementation does not support object query");
    }

    @Override
    public List<DirectoryEntry> listDirectory(ObjectPath path, ListOptions options) {
        try {
            List<DirectoryEntry> entries = new ArrayList<DirectoryEntry>();
            ListDirectoryRequest request = new ListDirectoryRequest();
            request.path((com.emc.atmos.api.ObjectPath) adaptIdentifier(path));
            if (options != null) {
                request.limit(options.getLimit()).token(options.getToken())
                        .includeMetadata(options.isIncludeMetadata());
                if (options.getUserMetadata() != null)
                    request.userMetadataNames(
                            options.getUserMetadata().toArray(new String[options.getUserMetadata().size()]));
                if (options.getSystemMetadata() != null)
                    request.systemMetadataNames(
                            options.getSystemMetadata().toArray(new String[options.getSystemMetadata().size()]));
            }
            for (com.emc.atmos.api.bean.DirectoryEntry entry : adaptee.listDirectory(request).getEntries()) {
                entries.add(adaptDirectoryEntry(entry, (com.emc.atmos.api.ObjectPath) adaptIdentifier(path)));
            }
            if (options != null)
                options.setToken(request.getToken());
            return entries;
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public ObjectMetadata getAllMetadata(Identifier id) {
        try {
            return adaptObjectMetadata(adaptee.getObjectMetadata(adaptIdentifier(id)));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public void rename(ObjectPath source, ObjectPath destination, boolean force) {
        try {
            adaptee.move((com.emc.atmos.api.ObjectPath) adaptIdentifier(source),
                    (com.emc.atmos.api.ObjectPath) adaptIdentifier(destination), force);
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public void restoreVersion(ObjectId id, ObjectId vId) {
        try {
            adaptee.restoreVersion((com.emc.atmos.api.ObjectId) adaptIdentifier(id),
                    (com.emc.atmos.api.ObjectId) adaptIdentifier(vId));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public ServiceInformation getServiceInformation() {
        try {
            return adaptServiceInformation(adaptee.getServiceInformation());
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public ObjectInfo getObjectInfo(Identifier id) {
        try {
            return adaptObjectInfo(adaptee.getObjectInfo(adaptIdentifier(id)));
        } catch (AtmosException e) {
            throw adaptException(e);
        } catch (JAXBException e) {
            throw new RuntimeException("Could not marshall result to XML", e);
        }
    }

    @Override
    public void hardLink(ObjectPath source, ObjectPath target) {
        throw new UnsupportedOperationException("This implementation does not support hard link");
    }

    @Override
    public ObjectId createObjectWithKeyFromSegment(String keyPool, String key, Acl acl, MetadataList metadata,
            BufferSegment data, String mimeType, Checksum checksum) {
        try {
            CreateObjectRequest request = new CreateObjectRequest().identifier(new ObjectKey(keyPool, key));
            request.acl(adaptAcl(acl)).userMetadata(adaptMetadata(metadata)).content(adaptBuffer(data));
            request.contentType(mimeType).wsChecksum(adaptChecksum(checksum, data));
            return (ObjectId) adaptIdentifier(adaptee.createObject(request).getObjectId());
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public ObjectId createObjectWithKeyFromStream(String keyPool, String key, Acl acl, MetadataList metadata,
            InputStream data, long length, String mimeType) {
        try {
            CreateObjectRequest request = new CreateObjectRequest().identifier(new ObjectKey(keyPool, key));
            request.acl(adaptAcl(acl)).userMetadata(adaptMetadata(metadata)).content(data);
            request.contentLength(length).contentType(mimeType);
            return (ObjectId) adaptIdentifier(adaptee.createObject(request).getObjectId());
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public void deleteObjectWithKey(String keyPool, String key) {
        try {
            adaptee.delete(new ObjectKey(keyPool, key));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public ObjectMetadata getAllMetadata(String keyPool, String key) {
        try {
            return adaptObjectMetadata(adaptee.getObjectMetadata(new ObjectKey(keyPool, key)));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public MetadataList getSystemMetadata(String keyPool, String key, MetadataTags tags) {
        try {
            return adaptMetadata(adaptee.getSystemMetadata(new ObjectKey(keyPool, key), adaptNames(tags)));
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public byte[] readObjectWithKey(String keyPool, String key, Extent extent, byte[] buffer, Checksum checksum) {
        try {
            return readObject(new ObjectKey(keyPool, key), extent, buffer, checksum);
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public InputStream readObjectStreamWithKey(String keyPool, String key, Extent extent) {
        try {
            return adaptee.readObjectStream(new ObjectKey(keyPool, key), adaptExtent(extent)).getObject();
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public void updateObjectWithKeyFromStream(String keyPool, String key, Acl acl, MetadataList metadata,
            Extent extent, InputStream data, long length, String mimeType) {
        try {
            UpdateObjectRequest request = new UpdateObjectRequest();
            request.identifier(new ObjectKey(keyPool, key)).acl(adaptAcl(acl)).userMetadata(adaptMetadata(metadata))
                    .range(adaptExtent(extent)).content(data).contentLength(length).contentType(mimeType);
            adaptee.updateObject(request);
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    @Override
    public void updateObjectWithKeyFromSegment(String keyPool, String key, Acl acl, MetadataList metadata,
            Extent extent, BufferSegment data, String mimeType, Checksum checksum) {
        try {
            UpdateObjectRequest request = new UpdateObjectRequest();
            request.identifier(new ObjectKey(keyPool, key)).acl(adaptAcl(acl)).userMetadata(adaptMetadata(metadata))
                    .range(adaptExtent(extent)).content(adaptBuffer(data)).contentType(mimeType)
                    .wsChecksum(adaptChecksum(checksum, data));
            adaptee.updateObject(request);
        } catch (AtmosException e) {
            throw adaptException(e);
        }
    }

    private byte[] readObject(ObjectIdentifier identifier, Extent extent, byte[] buffer, Checksum checksum) {
        try {
            if (buffer != null && extent != null && extent.getSize() > (long) buffer.length) {
                throw new IllegalArgumentException("The buffer is smaller than the requested extent");
            }

            ReadObjectResponse<InputStream> response = adaptee.readObjectStream(identifier, adaptExtent(extent));

            long contentLength = response.getContentLength();
            if (buffer != null && contentLength > -1) {
                if ((long) buffer.length < contentLength) {
                    throw new EsuException("The response buffer was not long enough to hold the response: "
                            + buffer.length + "<" + contentLength);
                }
                int read, c = 0;
                InputStream in = response.getObject();
                while (c < contentLength) {
                    read = in.read(buffer, c, (int) contentLength - c);
                    if (read == -1) {
                        throw new EOFException(
                                "EOF reading response at position " + c + " size " + (contentLength - c));
                    }
                    c += read;
                }
            } else {
                if (contentLength > Integer.MAX_VALUE)
                    throw new EsuException("Object is too large to hold in a byte array");
                buffer = StreamUtil.readAsBytes(response.getObject());
            }

            if (checksum != null && response.getWsChecksum() != null) {
                checksum.setExpectedValue(response.getWsChecksum().toString());
                if (contentLength > -1)
                    checksum.update(buffer, 0, (int) contentLength);
                else
                    checksum.update(buffer, 0, buffer.length);
            }

            return buffer;
        } catch (IOException e) {
            throw new EsuException("Error connecting to server", e);
        }
    }

    private ObjectIdentifier adaptIdentifier(Identifier identifier) {
        if (identifier == null)
            return null;
        if (identifier instanceof ObjectId)
            return new com.emc.atmos.api.ObjectId(identifier.toString());
        if (identifier instanceof ObjectPath)
            return new com.emc.atmos.api.ObjectPath(identifier.toString());
        throw new RuntimeException(
                "Unable to convert identifier " + identifier + " (" + identifier.getClass().getName() + ")");
    }

    private Identifier adaptIdentifier(ObjectIdentifier identifier) {
        if (identifier == null)
            return null;
        if (identifier instanceof com.emc.atmos.api.ObjectId)
            return new ObjectId(identifier.toString());
        if (identifier instanceof com.emc.atmos.api.ObjectPath)
            return new ObjectPath(identifier.toString());
        throw new RuntimeException(
                "Unable to convert identifier " + identifier + " (" + identifier.getClass().getName() + ")");
    }

    private com.emc.atmos.api.Acl adaptAcl(Acl acl) {
        if (acl == null)
            return null;
        com.emc.atmos.api.Acl newAcl = new com.emc.atmos.api.Acl();
        for (Grant grant : acl) {
            switch (grant.getGrantee().getType()) {
            case GROUP:
                newAcl.addGroupGrant(grant.getGrantee().getName(),
                        com.emc.atmos.api.bean.Permission.valueOf(grant.getPermission()));
                break;
            case USER:
                newAcl.addUserGrant(grant.getGrantee().getName(),
                        com.emc.atmos.api.bean.Permission.valueOf(grant.getPermission()));
                break;
            }
        }
        return newAcl;
    }

    private Acl adaptAcl(com.emc.atmos.api.Acl acl) {
        if (acl == null)
            return null;
        Acl newAcl = new Acl();
        for (String name : acl.getGroupAcl().keySet()) {
            newAcl.addGrant(
                    new Grant(new Grantee(name, Grantee.GRANT_TYPE.GROUP), acl.getGroupAcl().get(name).toString()));
        }
        for (String name : acl.getUserAcl().keySet()) {
            newAcl.addGrant(
                    new Grant(new Grantee(name, Grantee.GRANT_TYPE.USER), acl.getUserAcl().get(name).toString()));
        }
        return newAcl;
    }

    private com.emc.atmos.api.bean.Metadata[] adaptMetadata(MetadataList metadataList) {
        if (metadataList == null)
            return null;
        List<com.emc.atmos.api.bean.Metadata> newMetadata = new ArrayList<com.emc.atmos.api.bean.Metadata>();
        for (Metadata metadata : metadataList) {
            newMetadata.add(new com.emc.atmos.api.bean.Metadata(metadata.getName(), metadata.getValue(),
                    metadata.isListable()));
        }
        return newMetadata.toArray(new com.emc.atmos.api.bean.Metadata[newMetadata.size()]);
    }

    private MetadataList adaptMetadata(Map<String, com.emc.atmos.api.bean.Metadata> metadataMap) {
        if (metadataMap == null)
            return null;
        MetadataList metadataList = new MetadataList();
        for (com.emc.atmos.api.bean.Metadata oneMetadata : metadataMap.values()) {
            metadataList.addMetadata(
                    new Metadata(oneMetadata.getName(), oneMetadata.getValue(), oneMetadata.isListable()));
        }
        return metadataList;
    }

    private String[] adaptNames(MetadataTags tags) {
        if (tags == null)
            return null;
        List<String> names = new ArrayList<String>();
        for (MetadataTag tag : tags) {
            names.add(tag.getName());
        }
        return names.toArray(new String[names.size()]);
    }

    private MetadataTags adaptTags(Set<String> names) {
        if (names == null)
            return null;
        MetadataTags newTags = new MetadataTags();
        for (String name : names) {
            newTags.addTag(new MetadataTag(name, false));
        }
        return newTags;
    }

    private MetadataTags adaptTags(Map<String, Boolean> tags) {
        if (tags == null)
            return null;
        MetadataTags newTags = new MetadataTags();
        for (String name : tags.keySet()) {
            newTags.addTag(new MetadataTag(name, tags.get(name)));
        }
        return newTags;
    }

    private Range adaptExtent(Extent extent) {
        if (extent == null)
            return null;
        return new Range(extent.getOffset(), extent.getOffset() + extent.getSize() - 1);
    }

    private com.emc.atmos.api.BufferSegment adaptBuffer(BufferSegment bufferSegment) {
        if (bufferSegment == null)
            return null;
        return new com.emc.atmos.api.BufferSegment(bufferSegment.getBuffer(), bufferSegment.getOffset(),
                bufferSegment.getSize());
    }

    private ChecksumValue adaptChecksum(Checksum checksum, BufferSegment bufferSegment) {
        if (checksum == null)
            return null;
        checksum.update(bufferSegment.getBuffer(), bufferSegment.getOffset(), bufferSegment.getSize());
        return new ChecksumValueImpl(checksum.toString());
    }

    private Version adaptVersion(ObjectVersion version) {
        if (version == null)
            return null;
        return new Version((ObjectId) adaptIdentifier(version.getVersionId()), version.getVersionNumber(),
                version.getItime());
    }

    private ObjectResult adaptObjectEntry(ObjectEntry entry) {
        if (entry == null)
            return null;
        ObjectResult result = new ObjectResult();
        result.setId((ObjectId) adaptIdentifier(entry.getObjectId()));
        Map<String, com.emc.atmos.api.bean.Metadata> allMetadata = new TreeMap<String, com.emc.atmos.api.bean.Metadata>();
        if (entry.getSystemMetadata() != null)
            allMetadata.putAll(entry.getSystemMetadataMap());
        if (entry.getUserMetadata() != null)
            allMetadata.putAll(entry.getUserMetadataMap());
        if (!allMetadata.isEmpty())
            result.setMetadata(adaptMetadata(allMetadata));
        return result;
    }

    private DirectoryEntry adaptDirectoryEntry(com.emc.atmos.api.bean.DirectoryEntry entry,
            com.emc.atmos.api.ObjectPath parentPath) {
        if (entry == null)
            return null;
        String path = parentPath.getPath() + entry.getFilename();
        if (entry.isDirectory())
            path += "/";
        DirectoryEntry newEntry = new DirectoryEntry();
        newEntry.setId((ObjectId) adaptIdentifier(entry.getObjectId()));
        newEntry.setPath(new ObjectPath(path));
        newEntry.setType(entry.getFileType().toString());
        newEntry.setSystemMetadata(adaptMetadata(entry.getSystemMetadataMap()));
        newEntry.setUserMetadata(adaptMetadata(entry.getUserMetadataMap()));
        return newEntry;
    }

    private ObjectMetadata adaptObjectMetadata(com.emc.atmos.api.bean.ObjectMetadata objectMetadata) {
        if (objectMetadata == null)
            return null;
        ObjectMetadata newMetadata = new ObjectMetadata();
        newMetadata.setAcl(adaptAcl(objectMetadata.getAcl()));
        newMetadata.setMimeType(objectMetadata.getContentType());
        newMetadata.setMetadata(adaptMetadata(objectMetadata.getMetadata()));
        return newMetadata;
    }

    private ObjectInfo adaptObjectInfo(com.emc.atmos.api.bean.ObjectInfo objectInfo) throws JAXBException {
        if (objectInfo == null)
            return null;

        ObjectInfo newObjectInfo = new ObjectInfo();
        newObjectInfo.setObjectId((ObjectId) adaptIdentifier(objectInfo.getObjectId()));
        newObjectInfo.setSelection(objectInfo.getSelection());

        if (objectInfo.getExpiration() != null) {
            ObjectExpiration objectExpiration = new ObjectExpiration();
            objectExpiration.setEnabled(objectInfo.getExpiration().isEnabled());
            objectExpiration.setEndAt(objectInfo.getExpiration().getEndAt());
            newObjectInfo.setExpiration(objectExpiration);
        }

        if (objectInfo.getRetention() != null) {
            ObjectRetention objectRetention = new ObjectRetention();
            objectRetention.setEnabled(objectInfo.getRetention().isEnabled());
            objectRetention.setEndAt(objectInfo.getRetention().getEndAt());
            newObjectInfo.setRetention(objectRetention);
        }

        if (objectInfo.getReplicas() != null) {
            List<ObjectReplica> replicas = new ArrayList<ObjectReplica>();
            for (Replica replica : objectInfo.getReplicas()) {
                ObjectReplica newReplica = new ObjectReplica();
                newReplica.setId("" + replica.getId());
                newReplica.setCurrent(replica.isCurrent());
                newReplica.setLocation(replica.getLocation());
                newReplica.setReplicaType(replica.getType());
                newReplica.setStorageType(replica.getStorageType());
                replicas.add(newReplica);
            }
            newObjectInfo.setReplicas(replicas);
        }

        StringWriter xmlString = new StringWriter();
        getMarshaller().marshal(objectInfo, xmlString);
        newObjectInfo.setRawXml(xmlString.toString());

        return newObjectInfo;
    }

    private ServiceInformation adaptServiceInformation(
            com.emc.atmos.api.bean.ServiceInformation serviceInformation) {
        if (serviceInformation == null)
            return null;
        ServiceInformation newServiceInformation = new ServiceInformation();
        newServiceInformation.setAtmosVersion(serviceInformation.getAtmosVersion());
        newServiceInformation.setUnicodeMetadataSupported(
                serviceInformation.hasFeature(com.emc.atmos.api.bean.ServiceInformation.Feature.Utf8));
        for (com.emc.atmos.api.bean.ServiceInformation.Feature feature : serviceInformation.getFeatures()) {
            newServiceInformation.addFeature(feature.getHeaderName());
        }
        return newServiceInformation;
    }

    private EsuException adaptException(AtmosException e) {
        return new EsuException(e.getMessage(), e.getCause(), e.getHttpCode(), e.getErrorCode());
    }

    private Marshaller getMarshaller() throws JAXBException {
        Marshaller m = marshaller.get();
        if (m == null) {
            m = jaxbContext.createMarshaller();
            marshaller.set(m);
        }
        return m;
    }
}