org.qi4j.entitystore.jclouds.JCloudsMapEntityStoreMixin.java Source code

Java tutorial

Introduction

Here is the source code for org.qi4j.entitystore.jclouds.JCloudsMapEntityStoreMixin.java

Source

/*
 * Copyright (c) 2012-2014, Paul Merlin. 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.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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 org.qi4j.entitystore.jclouds;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.io.ByteSource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.jclouds.ContextBuilder;
import org.jclouds.apis.ApiMetadata;
import org.jclouds.apis.Apis;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.io.Payload;
import org.jclouds.providers.ProviderMetadata;
import org.jclouds.providers.Providers;
import org.qi4j.api.configuration.Configuration;
import org.qi4j.api.entity.EntityDescriptor;
import org.qi4j.api.entity.EntityReference;
import org.qi4j.api.injection.scope.This;
import org.qi4j.api.service.ServiceActivation;
import org.qi4j.io.Input;
import org.qi4j.io.Inputs;
import org.qi4j.io.Output;
import org.qi4j.io.Outputs;
import org.qi4j.io.Receiver;
import org.qi4j.io.Sender;
import org.qi4j.spi.entitystore.EntityNotFoundException;
import org.qi4j.spi.entitystore.EntityStoreException;
import org.qi4j.spi.entitystore.helpers.MapEntityStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Iterables.contains;
import static org.qi4j.api.entity.EntityReference.parseEntityReference;

/**
 * JClouds implementation of MapEntityStore.
 */
// TODO Expose Location in configuration
//      To be done once JClouds 1.5 has stabilized their Location API.
//      A list of ISO-3166 country codes.
//      It defines where your entities are allowed to be stored.
//      @UseDefaults Property<List<String>> geopoliticalBoundaries(); ???
//      SEE  http://www.jclouds.org/documentation/reference/location-metadata-design
public class JCloudsMapEntityStoreMixin implements ServiceActivation, MapEntityStore {

    private static final Logger LOGGER = LoggerFactory.getLogger("org.qi4j.entitystore.jclouds");

    private static final Map<String, ApiMetadata> allApis = Maps
            .uniqueIndex(Apis.viewableAs(BlobStoreContext.class), Apis.idFunction());

    private static final Map<String, ProviderMetadata> appProviders = Maps
            .uniqueIndex(Providers.viewableAs(BlobStoreContext.class), Providers.idFunction());

    private static final Set<String> allKeys = ImmutableSet
            .copyOf(Iterables.concat(appProviders.keySet(), allApis.keySet()));

    @This
    private Configuration<JCloudsMapEntityStoreConfiguration> configuration;

    private BlobStoreContext storeContext;

    private String container;

    @Override
    public void activateService() throws Exception {
        configuration.refresh();
        String provider = configuration.get().provider().get();
        String identifier = configuration.get().identifier().get();
        String credentials = configuration.get().credential().get();
        Map<String, String> properties = configuration.get().properties().get();
        container = configuration.get().container().get();
        if (provider != null) {
            checkArgument(contains(allKeys, provider), "provider %s not in supported list: %s", provider, allKeys);
        } else {
            provider = "transient";
        }
        if (container == null) {
            container = "qi4j-entities";
        }
        storeContext = ContextBuilder.newBuilder(provider).credentials(identifier, credentials)
                .overrides(asProperties(properties)).buildView(BlobStoreContext.class);
        BlobStore blobStore = storeContext.getBlobStore();
        if (!blobStore.containerExists(container)) {
            if (!blobStore.createContainerInLocation(null, container)) {
                throw new EntityStoreException("Unable to create JClouds Blob Container, cannot continue.");
            } else {
                LOGGER.debug("Created new container: {}", container);
            }
        }
        LOGGER.info("Activated using {} cloud provider [id:{}]", provider, identifier);
    }

    private Properties asProperties(Map<String, String> map) {
        Properties props = new Properties();
        for (Map.Entry<String, String> eachEntry : map.entrySet()) {
            props.put(eachEntry.getKey(), eachEntry.getValue());
        }
        return props;
    }

    @Override
    public void passivateService() throws Exception {
        if (storeContext != null) {
            storeContext.close();
            storeContext = null;
            container = null;
        }
    }

    @Override
    public Reader get(EntityReference entityReference) throws EntityStoreException {
        Blob blob = storeContext.getBlobStore().getBlob(container, entityReference.identity());
        if (blob == null) {
            throw new EntityNotFoundException(entityReference);
        }
        Payload payload = blob.getPayload();
        if (payload == null) {
            throw new EntityNotFoundException(entityReference);
        }
        InputStream input = null;
        try {
            input = payload.openStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            Inputs.byteBuffer(input, 4096).transferTo(Outputs.byteBuffer(baos));
            return new StringReader(baos.toString("UTF-8"));
        } catch (IOException ex) {
            throw new EntityStoreException("Unable to read entity state for: " + entityReference, ex);
        } finally {
            if (input != null) {
                try {
                    input.close();
                } catch (IOException ignored) {
                }
            }
        }
    }

    @Override
    public void applyChanges(MapChanges changes) throws IOException {
        final BlobStore blobStore = storeContext.getBlobStore();
        changes.visitMap(new MapChanger() {
            @Override
            public Writer newEntity(final EntityReference ref, EntityDescriptor entityDescriptor)
                    throws IOException {
                return new StringWriter() {
                    @Override
                    public void close() throws IOException {
                        super.close();
                        Blob blob = blobStore.blobBuilder(ref.identity())
                                .payload(ByteSource.wrap(toString().getBytes(UTF_8))).build();
                        blobStore.putBlob(container, blob);
                    }
                };
            }

            @Override
            public Writer updateEntity(final EntityReference ref, EntityDescriptor entityDescriptor)
                    throws IOException {
                if (!blobStore.blobExists(container, ref.identity())) {
                    throw new EntityNotFoundException(ref);
                }
                return new StringWriter() {
                    @Override
                    public void close() throws IOException {
                        super.close();
                        Blob blob = blobStore.blobBuilder(ref.identity())
                                .payload(ByteSource.wrap(toString().getBytes(UTF_8))).build();
                        blobStore.putBlob(container, blob);
                    }
                };
            }

            @Override
            public void removeEntity(EntityReference ref, EntityDescriptor entityDescriptor)
                    throws EntityNotFoundException {
                if (!blobStore.blobExists(container, ref.identity())) {
                    throw new EntityNotFoundException(ref);
                }
                blobStore.removeBlob(container, ref.identity());
            }
        });
    }

    @Override
    public Input<Reader, IOException> entityStates() {
        return new Input<Reader, IOException>() {
            @Override
            public <ReceiverThrowableType extends Throwable> void transferTo(
                    Output<? super Reader, ReceiverThrowableType> output)
                    throws IOException, ReceiverThrowableType {
                output.receiveFrom(new Sender<Reader, IOException>() {
                    @Override
                    public <ReceiverThrowableType extends Throwable> void sendTo(
                            Receiver<? super Reader, ReceiverThrowableType> receiver)
                            throws ReceiverThrowableType, IOException {
                        for (StorageMetadata stored : storeContext.getBlobStore().list()) {
                            Payload payload = storeContext.getBlobStore().getBlob(container, stored.getName())
                                    .getPayload();
                            if (payload == null) {
                                throw new EntityNotFoundException(parseEntityReference(stored.getName()));
                            }
                            InputStream input = null;
                            try {
                                input = payload.openStream();
                                receiver.receive(new InputStreamReader(input, "UTF-8"));
                            } finally {
                                if (input != null) {
                                    try {
                                        input.close();
                                    } catch (IOException ignored) {
                                    }
                                }
                            }
                        }
                    }
                });
            }
        };
    }
}