Java tutorial
/* * 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) { } } } } } }); } }; } }