[CAMEL-10786] Initial implementation of Camel Azure Component starting from Blob Service
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/a3b9b0f3 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/a3b9b0f3 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/a3b9b0f3 Branch: refs/heads/master Commit: a3b9b0f38397b8245f3774a20cc60782b2d94dd8 Parents: 0548f87 Author: Sergey Beryozkin <sberyoz...@gmail.com> Authored: Mon Feb 13 21:40:52 2017 +0000 Committer: Sergey Beryozkin <sberyoz...@gmail.com> Committed: Mon Feb 13 21:40:52 2017 +0000 ---------------------------------------------------------------------- components/camel-azure/pom.xml | 69 +++ .../camel/component/azure/blob/BlobBlock.java | 51 ++ .../azure/blob/BlobServiceComponent.java | 76 +++ .../azure/blob/BlobServiceConfiguration.java | 252 ++++++++++ .../azure/blob/BlobServiceConstants.java | 36 ++ .../azure/blob/BlobServiceConsumer.java | 69 +++ .../azure/blob/BlobServiceEndpoint.java | 95 ++++ .../azure/blob/BlobServiceOperations.java | 83 ++++ .../azure/blob/BlobServiceProducer.java | 481 +++++++++++++++++++ .../azure/blob/BlobServiceRequestOptions.java | 45 ++ .../component/azure/blob/BlobServiceUtil.java | 231 +++++++++ .../camel/component/azure/blob/BlobType.java | 30 ++ .../azure/common/AbstractConfiguration.java | 52 ++ .../component/azure/common/ExchangeUtil.java | 35 ++ .../src/main/resources/META-INF/LICENSE.txt | 203 ++++++++ .../src/main/resources/META-INF/NOTICE.txt | 11 + .../services/org/apache/camel/TypeConverter | 17 + .../org/apache/camel/component/azure-blob | 18 + .../blob/BlobServiceAppendConsumerTest.java | 93 ++++ .../blob/BlobServiceBlockConsumerTest.java | 91 ++++ .../BlobServiceComponentConfigurationTest.java | 203 ++++++++ .../blob/BlobServiceProducerSpringTest.java | 136 ++++++ .../azure/blob/BlobServiceUtilTest.java | 48 ++ .../src/test/resources/log4j2.properties | 28 ++ .../BlobServiceProducerSpringTest-context.xml | 56 +++ components/pom.xml | 1 + .../camel-azure-starter/pom.xml | 51 ++ .../BlobServiceComponentAutoConfiguration.java | 80 +++ .../src/main/resources/META-INF/LICENSE.txt | 203 ++++++++ .../src/main/resources/META-INF/NOTICE.txt | 11 + ...dditional-spring-configuration-metadata.json | 10 + .../main/resources/META-INF/spring.factories | 19 + .../src/main/resources/META-INF/spring.provides | 18 + .../spring-boot/components-starter/pom.xml | 1 + 34 files changed, 2903 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/pom.xml ---------------------------------------------------------------------- diff --git a/components/camel-azure/pom.xml b/components/camel-azure/pom.xml new file mode 100644 index 0000000..f36c875 --- /dev/null +++ b/components/camel-azure/pom.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.camel</groupId> + <artifactId>components</artifactId> + <version>2.19.0-SNAPSHOT</version> + </parent> + + <artifactId>camel-azure</artifactId> + <packaging>jar</packaging> + + <name>Camel :: Azure</name> + <description>Camel Microsoft Azure Components</description> + + <properties> + <azure-storage-java-sdk-version>5.0.0</azure-storage-java-sdk-version> + <powermock-easymock-version>1.6.6</powermock-easymock-version> + <camel.osgi.export.pkg>org.apache.camel.component.azure.*</camel.osgi.export.pkg> + <camel.osgi.export.service> + </camel.osgi.export.service> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-core</artifactId> + </dependency> + <dependency> + <groupId>com.microsoft.azure</groupId> + <artifactId>azure-storage</artifactId> + <version>${azure-storage-java-sdk-version}</version> + </dependency> + + <!-- for testing --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-test-spring</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobBlock.java ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobBlock.java b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobBlock.java new file mode 100644 index 0000000..87946ae --- /dev/null +++ b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobBlock.java @@ -0,0 +1,51 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.azure.blob; + +import java.io.InputStream; +import java.util.UUID; + +import com.microsoft.azure.storage.blob.BlockEntry; +import com.microsoft.azure.storage.blob.BlockSearchMode; +import com.microsoft.azure.storage.core.Base64; + +public class BlobBlock { + private InputStream blockStream; + private BlockEntry blockEntry; + public BlobBlock(InputStream blockStream) { + this(Base64.encode(UUID.randomUUID().toString().getBytes()), + blockStream); + } + public BlobBlock(String blockId, InputStream blockStream) { + this(blockId, BlockSearchMode.LATEST, blockStream); + } + public BlobBlock(String blockId, BlockSearchMode searchMode, InputStream blockStream) { + this(new BlockEntry(blockId, searchMode), blockStream); + } + public BlobBlock(BlockEntry blockEntry, InputStream blockStream) { + this.blockStream = blockStream; + this.blockEntry = blockEntry; + } + public InputStream getBlockStream() { + return blockStream; + } + public BlockEntry getBlockEntry() { + return blockEntry; + } + + +} http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceComponent.java ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceComponent.java b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceComponent.java new file mode 100644 index 0000000..33539a7 --- /dev/null +++ b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceComponent.java @@ -0,0 +1,76 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.azure.blob; + +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.Endpoint; +import org.apache.camel.impl.UriEndpointComponent; + +public class BlobServiceComponent extends UriEndpointComponent { + + public BlobServiceComponent() { + super(BlobServiceEndpoint.class); + } + + public BlobServiceComponent(CamelContext context) { + super(context, BlobServiceEndpoint.class); + } + + protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception { + BlobServiceConfiguration configuration = new BlobServiceConfiguration(); + setProperties(configuration, parameters); + + String[] parts = null; + if (remaining != null) { + parts = remaining.split("/"); + } + if (parts == null || parts.length < 2) { + throw new IllegalArgumentException("At least account and container names must be specified."); + } + + configuration.setAccountName(parts[0]); + configuration.setContainerName(parts[1]); + + if (parts.length > 2) { + // Blob names can contain forward slashes + StringBuilder sb = new StringBuilder(); + for (int i = 2; i < parts.length; i++) { + sb.append(parts[i]); + if (i + 1 < parts.length) { + sb.append('/'); + } + } + configuration.setBlobName(sb.toString()); + } + + checkCredentials(configuration); + + BlobServiceEndpoint endpoint = new BlobServiceEndpoint(uri, this, configuration); + setProperties(endpoint, parameters); + return endpoint; + } + + private void checkCredentials(BlobServiceConfiguration configuration) { + if (configuration.getAzureBlobClient() == null + && configuration.getCredentials() == null + && !configuration.isPublicForRead()) { + throw new IllegalArgumentException("Credentials must be specified."); + } + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceConfiguration.java ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceConfiguration.java b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceConfiguration.java new file mode 100644 index 0000000..ea155c4 --- /dev/null +++ b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceConfiguration.java @@ -0,0 +1,252 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.azure.blob; + +import java.util.Map; + +import com.microsoft.azure.storage.blob.CloudBlob; +import org.apache.camel.component.azure.common.AbstractConfiguration; +import org.apache.camel.spi.UriParam; +import org.apache.camel.spi.UriParams; + +@UriParams +public class BlobServiceConfiguration extends AbstractConfiguration { + + private String containerName; + + private String blobName; + + @UriParam + private CloudBlob azureBlobClient; + + @UriParam(defaultValue = "blockblob") + private BlobType blobType = BlobType.blockblob; + + @UriParam(label = "producer", defaultValue = "listBlobs") + private BlobServiceOperations operation = BlobServiceOperations.listBlobs; + + @UriParam(label = "producer") + private int streamWriteSize; + + @UriParam + private int streamReadSize; + + @UriParam(label = "producer") + private Map<String, String> blobMetadata; + + @UriParam(defaultValue = "true") + private boolean closeStreamAfterRead = true; + + @UriParam(label = "producer", defaultValue = "true") + private boolean closeStreamAfterWrite = true; + + @UriParam + private String fileDir; + + @UriParam(defaultValue = "0") + private Long blobOffset = 0L; + + @UriParam + private Long dataLength; + + @UriParam + private boolean publicForRead; + + @UriParam(label = "producer") + private String blobPrefix; + + @UriParam(label = "producer", defaultValue = "true") + private boolean useFlatListing = true; + + public BlobServiceOperations getOperation() { + return operation; + } + + /** + * Required blob service operation hint to the producer + */ + public void setOperation(BlobServiceOperations operation) { + this.operation = operation; + } + + public String getContainerName() { + return containerName; + } + + /** + * Set the blob service container name + */ + public void setContainerName(String containerName) { + this.containerName = containerName; + } + + public String getBlobName() { + return blobName; + } + + /** + * Blob name, required for most operations + */ + public void setBlobName(String blobName) { + this.blobName = blobName; + } + + public BlobType getBlobType() { + return blobType; + } + + /** + * Set a blob type, 'blockblob' is default + */ + public void setBlobType(BlobType blobType) { + this.blobType = blobType; + } + + public int getStreamWriteSize() { + return streamWriteSize; + } + + /** + * Set the size of the buffer for writing block and page blocks + */ + public void setStreamWriteSize(int streamWriteSize) { + this.streamWriteSize = streamWriteSize; + } + + public int getStreamReadSize() { + return streamReadSize; + } + + /** + * Set the minimum read size in bytes when reading the blob content + */ + public void setStreamReadSize(int streamReadSize) { + this.streamReadSize = streamReadSize; + } + + public Map<String, String> getBlobMetadata() { + return blobMetadata; + } + + /** + * Set the blob meta-data + */ + public void setBlobMetadata(Map<String, String> blobMetadata) { + this.blobMetadata = blobMetadata; + } + + public CloudBlob getAzureBlobClient() { + return azureBlobClient; + } + + /** + * The blob service client + */ + public void setAzureBlobClient(CloudBlob azureBlobClient) { + this.azureBlobClient = azureBlobClient; + } + + public boolean isCloseStreamAfterWrite() { + return closeStreamAfterWrite; + } + + /** + * Close the stream after write or keep it open, default is true + */ + public void setCloseStreamAfterWrite(boolean closeStreamAfterWrite) { + this.closeStreamAfterWrite = closeStreamAfterWrite; + } + + public boolean isCloseStreamAfterRead() { + return closeStreamAfterRead; + } + + /** + * Close the stream after read or keep it open, default is true + */ + public void setCloseStreamAfterRead(boolean closeStreamAfterRead) { + this.closeStreamAfterRead = closeStreamAfterRead; + } + + public String getFileDir() { + return fileDir; + } + + /** + * Set the file directory where the downloaded blobs will be saved to + */ + public void setFileDir(String fileDir) { + this.fileDir = fileDir; + } + + public Long getBlobOffset() { + return blobOffset; + } + + /** + * Set the blob offset for the upload or download operations, default is 0 + */ + public void setBlobOffset(Long dataOffset) { + this.blobOffset = dataOffset; + } + + public Long getDataLength() { + return dataLength; + } + + /** + * Set the data length for the download or page blob upload operations + */ + public void setDataLength(Long dataLength) { + this.dataLength = dataLength; + } + + public boolean isPublicForRead() { + return publicForRead; + } + + /** + * Blobs can be public for reading their content, if this property is enabled + * then the credentials do not have to be set + */ + + public void setPublicForRead(boolean publicForRead) { + this.publicForRead = publicForRead; + } + + public String getBlobPrefix() { + return blobPrefix; + } + + /** + * Set a prefix which can be used for listing the blobs + */ + public void setBlobPrefix(String blobPrefix) { + this.blobPrefix = blobPrefix; + } + + public boolean isUseFlatListing() { + return useFlatListing; + } + + /** + * Specify if the flat or hierarchical blob listing should be used + */ + public void setUseFlatListing(boolean useFlatListing) { + this.useFlatListing = useFlatListing; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceConstants.java ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceConstants.java b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceConstants.java new file mode 100644 index 0000000..f8f644a --- /dev/null +++ b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceConstants.java @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.azure.blob; + +public interface BlobServiceConstants { + + String OPERATION = "operation"; + String BLOB_CLIENT = "AzureBlobClient"; + + String SERVICE_URI_SEGMENT = ".blob.core.windows.net"; + String ACCESS_CONDITION = "BlobAccessCondition"; + String BLOB_REQUEST_OPTIONS = "BlobRequestOptions"; + String OPERATION_CONTEXT = "BlobOperationContext"; + + String BLOB_LISTING_DETAILS = "BlobListingDetails"; + + String COMMIT_BLOCK_LIST_LATER = "CommitBlobBlockListLater"; + String APPEND_BLOCK_CREATED = "AppendBlobCreated"; + String PAGE_BLOCK_CREATED = "PageBlobCreated"; + String PAGE_BLOB_RANGE = "PageBlobRange"; + String PAGE_BLOB_SIZE = "PageBlobSize"; +} http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceConsumer.java ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceConsumer.java b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceConsumer.java new file mode 100644 index 0000000..d91c02c --- /dev/null +++ b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceConsumer.java @@ -0,0 +1,69 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.azure.blob; + +import com.microsoft.azure.storage.StorageException; +import org.apache.camel.Exchange; +import org.apache.camel.NoFactoryAvailableException; +import org.apache.camel.Processor; +import org.apache.camel.impl.ScheduledPollConsumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A Consumer of the blob content from the Azure Blob Service + */ +// Extending DefaultConsumer is simpler if the blob must exist before this consumer is started, +// polling makes it easier to get the consumer working if no blob exists yet. +public class BlobServiceConsumer extends ScheduledPollConsumer { + private static final Logger LOG = LoggerFactory.getLogger(BlobServiceConsumer.class); + + public BlobServiceConsumer(BlobServiceEndpoint endpoint, Processor processor) throws NoFactoryAvailableException { + super(endpoint, processor); + } + + @Override + protected int poll() throws Exception { + Exchange exchange = super.getEndpoint().createExchange(); + try { + LOG.trace("Getting the blob content"); + getBlob(exchange); + super.getAsyncProcessor().process(exchange); + return 1; + } catch (StorageException ex) { + if (404 == ex.getHttpStatusCode()) { + return 0; + } else { + throw ex; + } + } + } + + private void getBlob(Exchange exchange) throws Exception { + BlobServiceUtil.getBlob(exchange, getConfiguration()); + } + + protected BlobServiceConfiguration getConfiguration() { + return getEndpoint().getConfiguration(); + } + @Override + public BlobServiceEndpoint getEndpoint() { + return (BlobServiceEndpoint) super.getEndpoint(); + } + + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceEndpoint.java ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceEndpoint.java b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceEndpoint.java new file mode 100644 index 0000000..d2aaa8c --- /dev/null +++ b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceEndpoint.java @@ -0,0 +1,95 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.azure.blob; + +import org.apache.camel.Component; +import org.apache.camel.Consumer; +import org.apache.camel.Processor; +import org.apache.camel.Producer; +import org.apache.camel.impl.DefaultEndpoint; +import org.apache.camel.spi.Metadata; +import org.apache.camel.spi.UriEndpoint; +import org.apache.camel.spi.UriParam; +import org.apache.camel.spi.UriPath; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The azure-blob component is used for storing and retrieving blobs from Azure Storage Blob Service. + */ +@UriEndpoint(scheme = "azure-blob", + title = "Azure Storage Blob Service", + syntax = "azure-blob:containerOrBlobUri", + consumerClass = BlobServiceConsumer.class, + label = "cloud, blob") +public class BlobServiceEndpoint extends DefaultEndpoint { + + private static final Logger LOG = LoggerFactory.getLogger(BlobServiceEndpoint.class); + + @UriPath(description = "Container or Blob compact Uri") + @Metadata(required = "true") + private String containerOrBlobUri; // to support component docs + @UriParam + private BlobServiceConfiguration configuration; + + + public BlobServiceEndpoint(String uri, Component comp, BlobServiceConfiguration configuration) { + super(uri, comp); + this.configuration = configuration; + } + + public Consumer createConsumer(Processor processor) throws Exception { + LOG.trace("Creating a consumer"); + if (getConfiguration().getBlobName() == null) { + throw new IllegalArgumentException("Blob name must be specified."); + } + BlobServiceConsumer consumer = new BlobServiceConsumer(this, processor); + configureConsumer(consumer); + return consumer; + } + + public Producer createProducer() throws Exception { + LOG.trace("Creating a producer"); + if (getConfiguration().getBlobName() == null + && getConfiguration().getOperation() != null + && BlobServiceOperations.listBlobs != configuration.getOperation()) { + // Omitting a blob name is only possible it is a (default) listBlobs producer operation + throw new IllegalArgumentException("Blob name must be specified."); + } + return new BlobServiceProducer(this); + } + + public boolean isSingleton() { + return true; + } + + @Override + public void doStart() throws Exception { + super.doStart(); + } + + public BlobServiceConfiguration getConfiguration() { + return configuration; + } + + public void setConfiguration(BlobServiceConfiguration configuration) { + this.configuration = configuration; + } + + + +} http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceOperations.java ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceOperations.java b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceOperations.java new file mode 100644 index 0000000..2e12d0f --- /dev/null +++ b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceOperations.java @@ -0,0 +1,83 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.azure.blob; + +public enum BlobServiceOperations { + /** + * Common to all block types + */ + // Get the content of the blob, can be restricted to a blob range + getBlob, + // Delete the blob + deleteBlob, + // List the blobs + listBlobs, + /* + * Bloc blob operations + */ + // Put a block blob content which either creates a new block blob + // or overwrites the existing block blob content + updateBlockBlob, + + // Upload a block blob content as a sequence of blob blocks first and then + // commit them to a blob. The commit can be executed later with the + // commitBlobBlockList operation if a message "CommitBlockListLater" + // property is enabled. Individual block blobs can be updated later. + uploadBlobBlocks, + + // Commit a sequence of blob blocks to the block list which was previously + // uploaded to the blob service with the putBlockBlob operation with the commit + // being delayed + commitBlobBlockList, + + // Get the block blob list, + getBlobBlockList, + + /* + * Append blob operations + */ + // Create an append block. By default if the block already exists then it is not reset. + // Note the updateAppendBlob will also try to create an append blob first unless + // a message "AppendBlobCreated" property is enabled + createAppendBlob, + + // Create an append block unless a message "AppendBlobCreated" property is enabled and no + // the identically named block already exists and append the new content to this blob. + updateAppendBlob, + + /** + * Page Block operations + */ + // Create a page block. By default if the block already exists then it is not reset. + // Note the updatePageBlob will also try to create a page blob first unless + // a message "PageBlobCreated" property is enabled + createPageBlob, + + // Create a page block unless a message "PageBlobCreated" property is enabled and no + // the identically named block already exists and set the content of this blob. + updatePageBlob, + + // Resize the page blob + resizePageBlob, + + // Clear the page blob + clearPageBlob, + + // Get the page blob page ranges + getPageBlobRanges + +} http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceProducer.java ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceProducer.java b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceProducer.java new file mode 100644 index 0000000..f50c115 --- /dev/null +++ b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceProducer.java @@ -0,0 +1,481 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.azure.blob; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import com.microsoft.azure.storage.AccessCondition; +import com.microsoft.azure.storage.StorageException; +import com.microsoft.azure.storage.blob.BlobListingDetails; +import com.microsoft.azure.storage.blob.BlockEntry; +import com.microsoft.azure.storage.blob.BlockListingFilter; +import com.microsoft.azure.storage.blob.CloudAppendBlob; +import com.microsoft.azure.storage.blob.CloudBlob; +import com.microsoft.azure.storage.blob.CloudBlobContainer; +import com.microsoft.azure.storage.blob.CloudBlockBlob; +import com.microsoft.azure.storage.blob.CloudPageBlob; +import com.microsoft.azure.storage.blob.ListBlobItem; +import com.microsoft.azure.storage.blob.PageRange; +import org.apache.camel.Endpoint; +import org.apache.camel.Exchange; +import org.apache.camel.component.azure.common.ExchangeUtil; +import org.apache.camel.impl.DefaultProducer; +import org.apache.camel.util.ObjectHelper; +import org.apache.camel.util.URISupport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * A Producer which sends messages to the Azure Storage Blob Service + */ +public class BlobServiceProducer extends DefaultProducer { + + private static final Logger LOG = LoggerFactory.getLogger(BlobServiceProducer.class); + + public BlobServiceProducer(final Endpoint endpoint) { + super(endpoint); + } + + + @Override + public void process(final Exchange exchange) throws Exception { + BlobServiceOperations operation = determineOperation(exchange); + if (ObjectHelper.isEmpty(operation)) { + operation = BlobServiceOperations.listBlobs; + } else { + switch (operation) { + case getBlob: + getBlob(exchange); + break; + case deleteBlob: + deleteBlob(exchange); + break; + case listBlobs: + listBlobs(exchange); + break; + case updateBlockBlob: + updateBlockBlob(exchange); + break; + case uploadBlobBlocks: + uploadBlobBlocks(exchange); + break; + case commitBlobBlockList: + commitBlobBlockList(exchange); + break; + case getBlobBlockList: + getBlobBlockList(exchange); + break; + case createAppendBlob: + createAppendBlob(exchange); + break; + case updateAppendBlob: + updateAppendBlob(exchange); + break; + case createPageBlob: + createPageBlob(exchange); + break; + case updatePageBlob: + uploadPageBlob(exchange); + break; + case resizePageBlob: + resizePageBlob(exchange); + break; + case clearPageBlob: + clearPageBlob(exchange); + break; + case getPageBlobRanges: + getPageBlobRanges(exchange); + break; + default: + throw new IllegalArgumentException("Unsupported operation"); + } + } + } + + private void listBlobs(Exchange exchange) throws Exception { + CloudBlobContainer client = BlobServiceUtil.createBlobContainerClient(getConfiguration()); + BlobServiceRequestOptions opts = BlobServiceUtil.getRequestOptions(exchange); + LOG.trace("Getting the blob list from the container [{}] from exchange [{}]...", + getConfiguration().getContainerName(), exchange); + BlobServiceConfiguration cfg = getConfiguration(); + @SuppressWarnings("unchecked") + EnumSet<BlobListingDetails> details = + (EnumSet<BlobListingDetails>)exchange.getIn().getHeader(BlobServiceConstants.BLOB_LISTING_DETAILS); + Iterable<ListBlobItem> items = + client.listBlobs(cfg.getBlobPrefix(), cfg.isUseFlatListing(), + details, opts.getRequestOpts(), opts.getOpContext()); + ExchangeUtil.getMessageForResponse(exchange).setBody(items); + } + + private void updateBlockBlob(Exchange exchange) throws Exception { + CloudBlockBlob client = BlobServiceUtil.createBlockBlobClient(getConfiguration(), true); + configureCloudBlobForWrite(client); + BlobServiceRequestOptions opts = BlobServiceUtil.getRequestOptions(exchange); + + InputStream inputStream = getInputStreamFromExchange(exchange); + + LOG.trace("Putting a block blob [{}] from exchange [{}]...", getConfiguration().getBlobName(), exchange); + try { + client.upload(inputStream, -1, + opts.getAccessCond(), opts.getRequestOpts(), opts.getOpContext()); + } finally { + closeInputStreamIfNeeded(inputStream); + } + } + + private void uploadBlobBlocks(Exchange exchange) throws Exception { + Object object = exchange.getIn().getMandatoryBody(); + + List<BlobBlock> blobBlocks = null; + if (object instanceof List) { + blobBlocks = (List<BlobBlock>)blobBlocks; + } else if (object instanceof BlobBlock) { + blobBlocks = Collections.singletonList((BlobBlock)object); + } + if (blobBlocks == null || blobBlocks.isEmpty()) { + throw new IllegalArgumentException("Illegal storageBlocks payload"); + } + + CloudBlockBlob client = BlobServiceUtil.createBlockBlobClient(getConfiguration(), true); + configureCloudBlobForWrite(client); + BlobServiceRequestOptions opts = BlobServiceUtil.getRequestOptions(exchange); + + LOG.trace("Putting a blob [{}] from blocks from exchange [{}]...", getConfiguration().getBlobName(), exchange); + List<BlockEntry> blockEntries = new LinkedList<BlockEntry>(); + for (BlobBlock blobBlock : blobBlocks) { + blockEntries.add(blobBlock.getBlockEntry()); + client.uploadBlock(blobBlock.getBlockEntry().getId(), blobBlock.getBlockStream(), -1, + opts.getAccessCond(), opts.getRequestOpts(), opts.getOpContext()); + } + Boolean commitBlockListLater = exchange.getIn().getHeader(BlobServiceConstants.COMMIT_BLOCK_LIST_LATER, + Boolean.class); + if (Boolean.TRUE != commitBlockListLater) { + client.commitBlockList(blockEntries, + opts.getAccessCond(), opts.getRequestOpts(), opts.getOpContext()); + } + } + + private void commitBlobBlockList(Exchange exchange) throws Exception { + Object object = exchange.getIn().getMandatoryBody(); + + List<BlockEntry> blockEntries = null; + if (object instanceof List) { + blockEntries = (List<BlockEntry>)blockEntries; + } else if (object instanceof BlockEntry) { + blockEntries = Collections.singletonList((BlockEntry)object); + } + if (blockEntries == null || blockEntries.isEmpty()) { + throw new IllegalArgumentException("Illegal commit block list payload"); + } + + CloudBlockBlob client = BlobServiceUtil.createBlockBlobClient(getConfiguration(), true); + BlobServiceRequestOptions opts = BlobServiceUtil.getRequestOptions(exchange); + + LOG.trace("Putting a blob [{}] block list from exchange [{}]...", getConfiguration().getBlobName(), exchange); + client.commitBlockList(blockEntries, + opts.getAccessCond(), opts.getRequestOpts(), opts.getOpContext()); + } + + private void getBlob(Exchange exchange) throws Exception { + BlobServiceUtil.getBlob(exchange, getConfiguration()); + } + + private void deleteBlob(Exchange exchange) throws Exception { + switch (getConfiguration().getBlobType()) { + case blockblob: + deleteBlockBlob(exchange); + break; + case appendblob: + deleteAppendBlob(exchange); + break; + case pageblob: + deletePageBlob(exchange); + break; + default: + throw new IllegalArgumentException("Unsupported blob type"); + } + } + + private void getBlobBlockList(Exchange exchange) throws Exception { + CloudBlockBlob client = BlobServiceUtil.createBlockBlobClient(getConfiguration(), false); + BlobServiceRequestOptions opts = BlobServiceUtil.getRequestOptions(exchange); + LOG.trace("Getting the blob block list [{}] from exchange [{}]...", getConfiguration().getBlobName(), exchange); + BlockListingFilter filter = exchange.getIn().getBody(BlockListingFilter.class); + if (filter == null) { + filter = BlockListingFilter.COMMITTED; + } + List<BlockEntry> blockEntries = + client.downloadBlockList(filter, opts.getAccessCond(), opts.getRequestOpts(), opts.getOpContext()); + ExchangeUtil.getMessageForResponse(exchange).setBody(blockEntries); + } + + private void deleteBlockBlob(Exchange exchange) throws Exception { + CloudBlockBlob client = BlobServiceUtil.createBlockBlobClient(getConfiguration(), true); + doDeleteBlock(client, exchange); + } + + private void createAppendBlob(Exchange exchange) throws Exception { + CloudAppendBlob client = BlobServiceUtil.createAppendBlobClient(getConfiguration(), true); + BlobServiceRequestOptions opts = BlobServiceUtil.getRequestOptions(exchange); + if (opts.getAccessCond() == null) { + // Default: do not reset the blob content if the blob already exists + opts.setAccessCond(AccessCondition.generateIfNotExistsCondition()); + } + doCreateAppendBlob(client, opts, exchange); + } + + private void doCreateAppendBlob(CloudAppendBlob client, BlobServiceRequestOptions opts, Exchange exchange) + throws Exception { + LOG.trace("Creating an append blob [{}] from exchange [{}]...", getConfiguration().getBlobName(), exchange); + try { + client.createOrReplace(opts.getAccessCond(), opts.getRequestOpts(), opts.getOpContext()); + } catch (StorageException ex) { + if (ex.getHttpStatusCode() != 409) { + throw ex; + } + } + ExchangeUtil.getMessageForResponse(exchange) + .setHeader(BlobServiceConstants.APPEND_BLOCK_CREATED, Boolean.TRUE); + } + + private void updateAppendBlob(Exchange exchange) throws Exception { + CloudAppendBlob client = BlobServiceUtil.createAppendBlobClient(getConfiguration(), true); + configureCloudBlobForWrite(client); + BlobServiceRequestOptions opts = BlobServiceUtil.getRequestOptions(exchange); + if (opts.getAccessCond() == null) { + // Default: do not reset the blob content if the blob already exists + opts.setAccessCond(AccessCondition.generateIfNotExistsCondition()); + } + + Boolean appendBlobCreated = exchange.getIn().getHeader(BlobServiceConstants.APPEND_BLOCK_CREATED, + Boolean.class); + if (Boolean.TRUE != appendBlobCreated) { + doCreateAppendBlob(client, opts, exchange); + } + + InputStream inputStream = getInputStreamFromExchange(exchange); + try { + client.appendBlock(inputStream, -1, + opts.getAccessCond(), opts.getRequestOpts(), opts.getOpContext()); + } finally { + closeInputStreamIfNeeded(inputStream); + } + } + + private void deleteAppendBlob(Exchange exchange) throws Exception { + CloudAppendBlob client = BlobServiceUtil.createAppendBlobClient(getConfiguration(), true); + doDeleteBlock(client, exchange); + } + + + private void createPageBlob(Exchange exchange) throws Exception { + CloudPageBlob client = BlobServiceUtil.createPageBlobClient(getConfiguration(), true); + BlobServiceRequestOptions opts = BlobServiceUtil.getRequestOptions(exchange); + if (opts.getAccessCond() == null) { + // Default: do not reset the blob content if the blob already exists + opts.setAccessCond(AccessCondition.generateIfNotExistsCondition()); + } + doCreatePageBlob(client, opts, exchange); + } + + private void doCreatePageBlob(CloudPageBlob client, BlobServiceRequestOptions opts, Exchange exchange) + throws Exception { + LOG.trace("Creating a page blob [{}] from exchange [{}]...", getConfiguration().getBlobName(), exchange); + Long pageSize = getPageBlobSize(exchange); + try { + client.create(pageSize, + opts.getAccessCond(), opts.getRequestOpts(), opts.getOpContext()); + } catch (StorageException ex) { + if (ex.getHttpStatusCode() != 409) { + throw ex; + } + } + ExchangeUtil.getMessageForResponse(exchange) + .setHeader(BlobServiceConstants.PAGE_BLOCK_CREATED, Boolean.TRUE); + + } + + private void uploadPageBlob(Exchange exchange) throws Exception { + LOG.trace("Updating a page blob [{}] from exchange [{}]...", getConfiguration().getBlobName(), exchange); + + CloudPageBlob client = BlobServiceUtil.createPageBlobClient(getConfiguration(), true); + configureCloudBlobForWrite(client); + BlobServiceRequestOptions opts = BlobServiceUtil.getRequestOptions(exchange); + if (opts.getAccessCond() == null) { + // Default: do not reset the blob content if the blob already exists + opts.setAccessCond(AccessCondition.generateIfNotExistsCondition()); + } + + Boolean pageBlobCreated = exchange.getIn().getHeader(BlobServiceConstants.PAGE_BLOCK_CREATED, + Boolean.class); + if (Boolean.TRUE != pageBlobCreated) { + doCreatePageBlob(client, opts, exchange); + } + InputStream inputStream = getInputStreamFromExchange(exchange); + doUpdatePageBlob(client, inputStream, opts, exchange); + + } + + private void resizePageBlob(Exchange exchange) throws Exception { + LOG.trace("Resizing a page blob [{}] from exchange [{}]...", getConfiguration().getBlobName(), exchange); + + CloudPageBlob client = BlobServiceUtil.createPageBlobClient(getConfiguration(), true); + BlobServiceRequestOptions opts = BlobServiceUtil.getRequestOptions(exchange); + Long pageSize = getPageBlobSize(exchange); + client.resize(pageSize, opts.getAccessCond(), opts.getRequestOpts(), opts.getOpContext()); + } + + private void clearPageBlob(Exchange exchange) throws Exception { + LOG.trace("Clearing a page blob [{}] from exchange [{}]...", getConfiguration().getBlobName(), exchange); + + CloudPageBlob client = BlobServiceUtil.createPageBlobClient(getConfiguration(), true); + BlobServiceRequestOptions opts = BlobServiceUtil.getRequestOptions(exchange); + + Long blobOffset = getConfiguration().getBlobOffset(); + Long blobDataLength = getConfiguration().getDataLength(); + PageRange range = exchange.getIn().getHeader(BlobServiceConstants.PAGE_BLOB_RANGE, PageRange.class); + if (range != null) { + blobOffset = range.getStartOffset(); + blobDataLength = range.getEndOffset() - range.getStartOffset(); + } + if (blobDataLength == null) { + blobDataLength = blobOffset == 0 ? getPageBlobSize(exchange) : 512L; + } + client.clearPages(blobOffset, blobDataLength, + opts.getAccessCond(), opts.getRequestOpts(), opts.getOpContext()); + } + + private void doUpdatePageBlob(CloudPageBlob client, InputStream is, BlobServiceRequestOptions opts, Exchange exchange) + throws Exception { + + Long blobOffset = getConfiguration().getBlobOffset(); + Long blobDataLength = getConfiguration().getDataLength(); + PageRange range = exchange.getIn().getHeader(BlobServiceConstants.PAGE_BLOB_RANGE, PageRange.class); + if (range != null) { + blobOffset = range.getStartOffset(); + blobDataLength = range.getEndOffset() - range.getStartOffset(); + } + if (blobDataLength == null) { + blobDataLength = (long)is.available(); + } + try { + client.uploadPages(is, blobOffset, blobDataLength, + opts.getAccessCond(), opts.getRequestOpts(), opts.getOpContext()); + } finally { + closeInputStreamIfNeeded(is); + } + + } + + private void getPageBlobRanges(Exchange exchange) throws Exception { + CloudPageBlob client = BlobServiceUtil.createPageBlobClient(getConfiguration(), false); + BlobServiceUtil.configureCloudBlobForRead(client, getConfiguration()); + BlobServiceRequestOptions opts = BlobServiceUtil.getRequestOptions(exchange); + LOG.trace("Getting the page blob ranges [{}] from exchange [{}]...", getConfiguration().getBlobName(), exchange); + List<PageRange> ranges = + client.downloadPageRanges(opts.getAccessCond(), opts.getRequestOpts(), opts.getOpContext()); + ExchangeUtil.getMessageForResponse(exchange).setBody(ranges); + } + + private void deletePageBlob(Exchange exchange) throws Exception { + CloudPageBlob client = BlobServiceUtil.createPageBlobClient(getConfiguration(), true); + doDeleteBlock(client, exchange); + } + + private Long getPageBlobSize(Exchange exchange) { + Long pageSize = exchange.getIn().getHeader(BlobServiceConstants.PAGE_BLOB_SIZE, Long.class); + if (pageSize == null) { + pageSize = 512L; + } + return pageSize; + } + + + private void doDeleteBlock(CloudBlob client, Exchange exchange) throws Exception { + LOG.trace("Deleting a blob [{}] from exchange [{}]...", getConfiguration().getBlobName(), exchange); + client.delete(); + } + + private String getCharsetName(Exchange exchange) { + String charset = exchange.getIn().getHeader(Exchange.CHARSET_NAME, String.class); + return charset == null ? "UTF-8" : charset; + } + + private void configureCloudBlobForWrite(CloudBlob client) { + if (getConfiguration().getStreamWriteSize() > 0) { + client.setStreamWriteSizeInBytes(getConfiguration().getStreamWriteSize()); + } + if (getConfiguration().getBlobMetadata() != null) { + client.setMetadata(new HashMap<String, String>(getConfiguration().getBlobMetadata())); + } + } + + + private BlobServiceOperations determineOperation(Exchange exchange) { + BlobServiceOperations operation = exchange.getIn().getHeader(BlobServiceConstants.OPERATION, BlobServiceOperations.class); + if (operation == null) { + operation = getConfiguration().getOperation(); + } + return operation; + } + + protected BlobServiceConfiguration getConfiguration() { + return getEndpoint().getConfiguration(); + } + + @Override + public String toString() { + return "StorageBlobProducer[" + URISupport.sanitizeUri(getEndpoint().getEndpointUri()) + "]"; + } + + @Override + public BlobServiceEndpoint getEndpoint() { + return (BlobServiceEndpoint) super.getEndpoint(); + } + + private InputStream getInputStreamFromExchange(Exchange exchange) throws Exception { + Object blobObject = exchange.getIn().getMandatoryBody(); + InputStream inputStream = null; + if (blobObject instanceof String) { + String charset = getCharsetName(exchange); + inputStream = new ByteArrayInputStream(((String)blobObject).getBytes(charset)); + } else if (blobObject instanceof InputStream) { + inputStream = (InputStream)blobObject; + } else if (blobObject instanceof File) { + inputStream = new FileInputStream((File)blobObject); + } else { + throw new IllegalArgumentException("Unsupported blob type:" + blobObject.getClass().getName()); + } + return inputStream; + } + + private void closeInputStreamIfNeeded(InputStream inputStream) throws IOException { + if (getConfiguration().isCloseStreamAfterWrite()) { + inputStream.close(); + } + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceRequestOptions.java ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceRequestOptions.java b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceRequestOptions.java new file mode 100644 index 0000000..6e0b237 --- /dev/null +++ b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceRequestOptions.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.azure.blob; + +import com.microsoft.azure.storage.AccessCondition; +import com.microsoft.azure.storage.OperationContext; +import com.microsoft.azure.storage.blob.BlobRequestOptions; + +public class BlobServiceRequestOptions { + private AccessCondition accessCond; + private BlobRequestOptions requestOpts; + private OperationContext opContext; + public AccessCondition getAccessCond() { + return accessCond; + } + public void setAccessCond(AccessCondition accessCond) { + this.accessCond = accessCond; + } + public BlobRequestOptions getRequestOpts() { + return requestOpts; + } + public void setRequestOpts(BlobRequestOptions requestOpts) { + this.requestOpts = requestOpts; + } + public OperationContext getOpContext() { + return opContext; + } + public void setOpContext(OperationContext opContext) { + this.opContext = opContext; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceUtil.java ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceUtil.java b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceUtil.java new file mode 100644 index 0000000..274bffa --- /dev/null +++ b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobServiceUtil.java @@ -0,0 +1,231 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.azure.blob; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.net.URI; + +import com.microsoft.azure.storage.AccessCondition; +import com.microsoft.azure.storage.OperationContext; +import com.microsoft.azure.storage.StorageCredentials; +import com.microsoft.azure.storage.blob.BlobRequestOptions; +import com.microsoft.azure.storage.blob.CloudAppendBlob; +import com.microsoft.azure.storage.blob.CloudBlob; +import com.microsoft.azure.storage.blob.CloudBlobContainer; +import com.microsoft.azure.storage.blob.CloudBlockBlob; +import com.microsoft.azure.storage.blob.CloudPageBlob; +import com.microsoft.azure.storage.blob.PageRange; +import org.apache.camel.Exchange; +import org.apache.camel.component.azure.common.ExchangeUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class BlobServiceUtil { + private static final Logger LOG = LoggerFactory.getLogger(BlobServiceUtil.class); + + private BlobServiceUtil() { + } + public static void getBlob(Exchange exchange, BlobServiceConfiguration cfg) + throws Exception { + switch (cfg.getBlobType()) { + case blockblob: + getBlockBlob(exchange, cfg); + break; + case appendblob: + getAppendBlob(exchange, cfg); + break; + case pageblob: + getPageBlob(exchange, cfg); + break; + default: + throw new IllegalArgumentException("Unsupported blob type"); + } + } + + private static void getBlockBlob(Exchange exchange, BlobServiceConfiguration cfg) + throws Exception { + CloudBlockBlob client = createBlockBlobClient(cfg, false); + doGetBlob(client, exchange, cfg); + } + private static void getAppendBlob(Exchange exchange, BlobServiceConfiguration cfg) throws Exception { + CloudAppendBlob client = createAppendBlobClient(cfg, false); + doGetBlob(client, exchange, cfg); + } + private static void getPageBlob(Exchange exchange, BlobServiceConfiguration cfg) throws Exception { + CloudPageBlob client = createPageBlobClient(cfg, false); + doGetBlob(client, exchange, cfg); + } + + private static void doGetBlob(CloudBlob client, Exchange exchange, BlobServiceConfiguration cfg) + throws Exception { + BlobServiceUtil.configureCloudBlobForRead(client, cfg); + BlobServiceRequestOptions opts = getRequestOptions(exchange); + LOG.trace("Getting a blob [{}] from exchange [{}]...", cfg.getBlobName(), exchange); + Long blobOffset = cfg.getBlobOffset(); + Long blobDataLength = cfg.getDataLength(); + if (client instanceof CloudPageBlob) { + PageRange range = exchange.getIn().getHeader(BlobServiceConstants.PAGE_BLOB_RANGE, PageRange.class); + if (range != null) { + blobOffset = range.getStartOffset(); + blobDataLength = range.getEndOffset() - range.getStartOffset(); + } + } + OutputStream os = exchange.getIn().getBody(OutputStream.class); + if (os == null) { + String fileDir = cfg.getFileDir(); + if (fileDir != null) { + // Should the range if it is set be reflected in the file name ? + String name = cfg.getBlobName(); + File file = new File(fileDir, name + ".blob"); + ExchangeUtil.getMessageForResponse(exchange).setBody(file); + os = new FileOutputStream(file); + } + } + if (os == null) { + throw new IllegalArgumentException("OutputStream is not available"); + } + try { + client.downloadRange(blobOffset, blobDataLength, os, + opts.getAccessCond(), opts.getRequestOpts(), opts.getOpContext()); + } finally { + if (cfg.isCloseStreamAfterRead()) { + os.close(); + } + } + } + public static CloudBlobContainer createBlobContainerClient(BlobServiceConfiguration cfg) + throws Exception { + URI uri = prepareStorageBlobUri(cfg, false); + StorageCredentials creds = getAccountCredentials(cfg, false); + return new CloudBlobContainer(uri, creds); + } + public static CloudBlockBlob createBlockBlobClient(BlobServiceConfiguration cfg, boolean isWrite) + throws Exception { + CloudBlockBlob client = (CloudBlockBlob)getConfiguredClient(cfg, BlobType.blockblob, isWrite); + if (client == null) { + URI uri = prepareStorageBlobUri(cfg); + StorageCredentials creds = getAccountCredentials(cfg, isWrite); + client = new CloudBlockBlob(uri, creds); + } + return client; + } + public static CloudAppendBlob createAppendBlobClient(BlobServiceConfiguration cfg, boolean isWrite) + throws Exception { + CloudAppendBlob client = (CloudAppendBlob)getConfiguredClient(cfg, BlobType.appendblob, isWrite); + if (client == null) { + URI uri = prepareStorageBlobUri(cfg); + StorageCredentials creds = getAccountCredentials(cfg, isWrite); + client = new CloudAppendBlob(uri, creds); + } + return client; + } + + public static CloudPageBlob createPageBlobClient(BlobServiceConfiguration cfg, boolean isWrite) + throws Exception { + CloudPageBlob client = (CloudPageBlob)getConfiguredClient(cfg, BlobType.pageblob, isWrite); + if (client == null) { + URI uri = prepareStorageBlobUri(cfg); + StorageCredentials creds = getAccountCredentials(cfg, isWrite); + client = new CloudPageBlob(uri, creds); + } + return client; + } + + public static CloudBlob getConfiguredClient(BlobServiceConfiguration cfg, + BlobType blobType, + boolean isWrite) { + CloudBlob client = cfg.getAzureBlobClient(); + if (client != null) { + Class<?> expectedCls = null; + if (blobType == BlobType.blockblob) { + expectedCls = CloudBlockBlob.class; + } else if (blobType == BlobType.appendblob) { + expectedCls = CloudAppendBlob.class; + } else if (blobType == BlobType.pageblob) { + expectedCls = CloudPageBlob.class; + } + if (client.getClass() != expectedCls) { + throw new IllegalArgumentException("Invalid Blob Client Type"); + } + if (!client.getUri().equals(prepareStorageBlobUri(cfg))) { + throw new IllegalArgumentException("Invalid Client Uri"); + } + if (client.getServiceClient().getCredentials() == null && (isWrite || !cfg.isPublicForRead())) { + throw new IllegalArgumentException("Storage credentials must be specified"); + } + } + return client; + } + + public static StorageCredentials getAccountCredentials(BlobServiceConfiguration cfg, + boolean isWrite) { + if (cfg.getCredentials() == null && (isWrite || !cfg.isPublicForRead())) { + throw new IllegalArgumentException("Storage credentials must be specified"); + } + return cfg.getCredentials(); + } + + public static void configureCloudBlobForRead(CloudBlob client, BlobServiceConfiguration cfg) { + if (cfg.getStreamReadSize() > 0) { + client.setStreamMinimumReadSizeInBytes(cfg.getStreamReadSize()); + } + } + + public static URI prepareStorageBlobUri(BlobServiceConfiguration cfg) { + return prepareStorageBlobUri(cfg, true); + } + + public static URI prepareStorageBlobUri(BlobServiceConfiguration cfg, boolean blobNameRequired) { + if (blobNameRequired && cfg.getBlobName() == null) { + throw new IllegalArgumentException("Blob name must be specified"); + } + + StringBuilder uriBuilder = new StringBuilder(); + uriBuilder.append("https://") + .append(cfg.getAccountName()) + .append(BlobServiceConstants.SERVICE_URI_SEGMENT) + .append("/") + .append(cfg.getContainerName()); + if (cfg.getBlobName() != null) { + uriBuilder.append("/") + .append(cfg.getBlobName()); + } + return URI.create(uriBuilder.toString()); + } + + + public static BlobServiceRequestOptions getRequestOptions(Exchange exchange) { + BlobServiceRequestOptions opts = exchange.getIn().getBody(BlobServiceRequestOptions.class); + if (opts != null) { + return opts; + } else { + opts = new BlobServiceRequestOptions(); + } + AccessCondition accessCond = + exchange.getIn().getHeader(BlobServiceConstants.ACCESS_CONDITION, AccessCondition.class); + BlobRequestOptions requestOpts = + exchange.getIn().getHeader(BlobServiceConstants.BLOB_REQUEST_OPTIONS, BlobRequestOptions.class); + OperationContext opContext = + exchange.getIn().getHeader(BlobServiceConstants.OPERATION_CONTEXT, OperationContext.class); + opts.setAccessCond(accessCond); + opts.setOpContext(opContext); + opts.setRequestOpts(requestOpts); + return opts; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobType.java ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobType.java b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobType.java new file mode 100644 index 0000000..ee2f5ed --- /dev/null +++ b/components/camel-azure/src/main/java/org/apache/camel/component/azure/blob/BlobType.java @@ -0,0 +1,30 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.azure.blob; + +/** + * Blob Type + */ +// The lower case naming is done to make component URI look better +// when a type needs to be set and make it consistent with the values +// used by Azure SDK BlobType parse implementation which is currently not +// directly accessible +public enum BlobType { + blockblob, + appendblob, + pageblob +} http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/java/org/apache/camel/component/azure/common/AbstractConfiguration.java ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/java/org/apache/camel/component/azure/common/AbstractConfiguration.java b/components/camel-azure/src/main/java/org/apache/camel/component/azure/common/AbstractConfiguration.java new file mode 100644 index 0000000..c230044 --- /dev/null +++ b/components/camel-azure/src/main/java/org/apache/camel/component/azure/common/AbstractConfiguration.java @@ -0,0 +1,52 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.azure.common; + +import com.microsoft.azure.storage.StorageCredentials; +import org.apache.camel.spi.UriParam; + +public abstract class AbstractConfiguration implements Cloneable { + + @UriParam + private StorageCredentials credentials; + + private String accountName; + + public String getAccountName() { + return accountName; + } + + /** + * Set the Azure account name + */ + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + + public StorageCredentials getCredentials() { + return credentials; + } + + /** + * Set the storage credentials, required in most cases + */ + public void setCredentials(StorageCredentials credentials) { + this.credentials = credentials; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/java/org/apache/camel/component/azure/common/ExchangeUtil.java ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/java/org/apache/camel/component/azure/common/ExchangeUtil.java b/components/camel-azure/src/main/java/org/apache/camel/component/azure/common/ExchangeUtil.java new file mode 100644 index 0000000..5bc979e --- /dev/null +++ b/components/camel-azure/src/main/java/org/apache/camel/component/azure/common/ExchangeUtil.java @@ -0,0 +1,35 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.azure.common; + +import org.apache.camel.Exchange; +import org.apache.camel.Message; + +public final class ExchangeUtil { + + private ExchangeUtil() { + } + + public static Message getMessageForResponse(final Exchange exchange) { + if (exchange.getPattern().isOutCapable()) { + Message out = exchange.getOut(); + out.copyFrom(exchange.getIn()); + return out; + } + return exchange.getIn(); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/resources/META-INF/LICENSE.txt ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/resources/META-INF/LICENSE.txt b/components/camel-azure/src/main/resources/META-INF/LICENSE.txt new file mode 100755 index 0000000..6b0b127 --- /dev/null +++ b/components/camel-azure/src/main/resources/META-INF/LICENSE.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + http://git-wip-us.apache.org/repos/asf/camel/blob/a3b9b0f3/components/camel-azure/src/main/resources/META-INF/NOTICE.txt ---------------------------------------------------------------------- diff --git a/components/camel-azure/src/main/resources/META-INF/NOTICE.txt b/components/camel-azure/src/main/resources/META-INF/NOTICE.txt new file mode 100755 index 0000000..2e215bf --- /dev/null +++ b/components/camel-azure/src/main/resources/META-INF/NOTICE.txt @@ -0,0 +1,11 @@ + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for the Apache Camel distribution. == + ========================================================================= + + This product includes software developed by + The Apache Software Foundation (http://www.apache.org/). + + Please read the different LICENSE files present in the licenses directory of + this distribution.