initial import to svn

git-svn-id: http://jclouds.googlecode.com/svn/trunk@3 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
ferncam1 2009-04-28 09:59:49 +00:00
parent d81912748b
commit 483043ea87
138 changed files with 14651 additions and 0 deletions

92
core/pom.xml Normal file
View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
====================================================================
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.html
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">
<parent>
<artifactId>project</artifactId>
<groupId>org.jclouds</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../project/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jclouds-core</artifactId>
<name>jclouds Components Core</name>
<packaging>jar</packaging>
<description>Core components to access jclouds services</description>
<scm>
<connection>scm:svn:http://jclouds.googlecode.com/svn/trunk/core</connection>
<developerConnection>scm:svn:https://jclouds.googlecode.com/svn/trunk/core</developerConnection>
<url>http://jclouds.googlecode.com/svn/trunk/core</url>
</scm>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>com.google.code.guice</groupId>
<artifactId>guice</artifactId>
<version>2.0-r936</version>
</dependency>
<dependency>
<groupId>com.google.code.guice</groupId>
<artifactId>guice-assistedinject</artifactId>
<version>2.0-r936</version>
</dependency>
<dependency>
<groupId>com.google.collections</groupId>
<artifactId>google-collections</artifactId>
<version>1.0-rc1</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.16</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>guice-snapshot</id>
<url>http://guice-maven.googlecode.com/svn/trunk</url>
</repository>
</repositories>
</project>

View File

@ -0,0 +1,77 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds;
import java.util.logging.Level;
public class Logger {
private final java.util.logging.Logger logger;
public Logger(java.util.logging.Logger logger) {
this.logger = logger;
}
public void trace(String message, Object... args) {
if (isTraceEnabled())
logger.finest(String.format(message, args));
}
public boolean isTraceEnabled() {
return logger.isLoggable(Level.FINEST);
}
public void debug(String message, Object... args) {
if (isDebugEnabled())
logger.fine(String.format(message, args));
}
private boolean isDebugEnabled() {
return logger.isLoggable(Level.FINE);
}
public void info(String message, Object... args) {
if (logger.isLoggable(Level.INFO))
logger.info(String.format(message, args));
}
public void warn(String message, Object... args) {
if (logger.isLoggable(Level.WARNING))
logger.log(Level.WARNING, String.format(message, args));
}
public void warn(Throwable throwable, String message, Object... args) {
if (logger.isLoggable(Level.WARNING))
logger.log(Level.WARNING, String.format(message, args), throwable);
}
public void error(String message, Object... args) {
if (logger.isLoggable(Level.SEVERE))
logger.log(Level.SEVERE, String.format(message, args));
}
public void error(Throwable throwable, String message, Object... args) {
if (logger.isLoggable(Level.SEVERE))
logger.log(Level.SEVERE, String.format(message, args), throwable);
}
}

View File

@ -0,0 +1,67 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ExecutionException;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class Utils {
public static <E extends Exception> void rethrowIfRuntimeOrSameType(Exception e) throws E {
if (e instanceof ExecutionException) {
Throwable nested = e.getCause();
if (nested instanceof Error)
throw (Error) nested;
e = (Exception) nested;
}
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
try {
throw (E) e;
} catch (ClassCastException throwAway) {
// using cce as there's no way to do instanceof E in current java
}
}
}
public static String toStringAndClose(InputStream input) throws IOException {
try {
return IOUtils.toString(input);
} finally {
IOUtils.closeQuietly(input);
}
}
}

View File

@ -0,0 +1,136 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.command;
import java.util.concurrent.*;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class FutureCommand<Q, R, T> implements Future<T> {
private Q request;
private ResponseRunnableFuture<R, T> responseRunnableFuture;
private volatile int failureCount;
public int incrementFailureCount() {
return ++failureCount;
}
public int getFailureCount() {
return failureCount;
}
public FutureCommand(Q request, ResponseCallable<R, T> responseCallable) {
this.request = request;
this.responseRunnableFuture = new ResponseRunnableFutureTask<R, T>(responseCallable);
}
public Q getRequest() {
return request;
}
public ResponseRunnableFuture<R, T> getResponseFuture() {
return responseRunnableFuture;
}
public void setException(Exception e) {
responseRunnableFuture.setException(e);
}
public boolean cancel(boolean b) {
return responseRunnableFuture.cancel(b);
}
public boolean isCancelled() {
return responseRunnableFuture.isCancelled();
}
public boolean isDone() {
return responseRunnableFuture.isDone();
}
public T get() throws InterruptedException, ExecutionException {
return responseRunnableFuture.get();
}
public T get(long l, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
return responseRunnableFuture.get(l, timeUnit);
}
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public static class ResponseRunnableFutureTask<R, T> extends FutureTask<T> implements ResponseRunnableFuture<R, T> {
private final ResponseCallable<R, T> tCallable;
public ResponseRunnableFutureTask(ResponseCallable<R, T> tCallable) {
super(tCallable);
this.tCallable = tCallable;
}
@Override
public String toString() {
return "ApacheHttpResponseFuture{" +
"tCallable=" + tCallable +
'}';
}
public R getResponse() {
return tCallable.getResponse();
}
public void setResponse(R response) {
tCallable.setResponse(response);
}
/**
* opening this to public so that other errors can be associated with the request, for example i/o errors.
*
* @param throwable
*/
@Override
public void setException(Throwable throwable) {
super.setException(throwable);
}
}
public interface ResponseRunnableFuture<R, T> extends Response<R>, Runnable, Future<T> {
public void setException(Throwable throwable);
}
public interface ResponseCallable<R, T> extends Response<R>, Callable<T> {
}
public interface Response<R> {
public R getResponse();
public void setResponse(R response);
}
}

View File

@ -0,0 +1,35 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.command;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public interface FutureCommandClient {
@SuppressWarnings("unchecked")
<O extends FutureCommand> void submit(O operation);
void close();
}

View File

@ -0,0 +1,104 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.command.pool;
import com.google.inject.assistedinject.Assisted;
import org.jclouds.Logger;
import org.jclouds.command.FutureCommand;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Semaphore;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public abstract class FutureCommandConnectionHandle<C> {
protected final BlockingQueue<C> available;
protected final Semaphore maxConnections;
protected final Semaphore completed;
protected C conn;
@SuppressWarnings("unchecked")
protected FutureCommand operation;
protected final Logger logger;
@SuppressWarnings("unchecked")
public FutureCommandConnectionHandle(java.util.logging.Logger logger, Semaphore maxConnections, @Assisted FutureCommand operation, @Assisted C conn, BlockingQueue<C> available) throws InterruptedException {
this.maxConnections = maxConnections;
this.operation = operation;
this.conn = conn;
this.available = available;
this.logger = new Logger(logger);
this.completed = new Semaphore(1);
completed.acquire();
}
@SuppressWarnings("unchecked")
public FutureCommand getOperation() {
return operation;
}
public abstract void startConnection();
public boolean isCompleted() {
return (completed.availablePermits() == 1);
}
public void release() throws InterruptedException {
if (isCompleted()) {
return;
}
logger.trace("%1s - %2d - releasing to pool", conn, conn.hashCode());
available.put(conn);
conn = null;
operation = null;
completed.release();
}
public void cancel() throws IOException {
if (isCompleted()) {
return;
}
if (conn != null) {
logger.trace("%1s - %2d - cancelled; shutting down connection", conn, conn.hashCode());
try {
shutdownConnection();
} finally {
conn = null;
operation = null;
maxConnections.release();
}
}
completed.release();
}
public abstract void shutdownConnection() throws IOException;
public void waitFor() throws InterruptedException {
completed.acquire();
completed.release();
}
}

View File

@ -0,0 +1,118 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.command.pool;
import com.google.inject.Provides;
import com.google.inject.name.Named;
import org.jclouds.Logger;
import org.jclouds.command.FutureCommand;
import org.jclouds.lifecycle.BaseLifeCycle;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public abstract class FutureCommandConnectionPool<C> extends BaseLifeCycle {
protected final Semaphore allConnections;
protected final BlockingQueue<C> available;
protected final FutureCommandConnectionHandleFactory<C> futureCommandConnectionHandleFactory;
protected final int maxConnectionReuse;
protected final AtomicInteger currentSessionFailures = new AtomicInteger(0);
protected final FutureCommandConnectionRetry<C> futureCommandConnectionRetry;
protected volatile boolean hitBottom = false;
public FutureCommandConnectionPool(Logger logger, ExecutorService executor, FutureCommandConnectionRetry<C> futureCommandConnectionRetry, Semaphore allConnections, FutureCommandConnectionHandleFactory<C> futureCommandConnectionHandleFactory, @Named("maxConnectionReuse") int maxConnectionReuse, BlockingQueue<C> available, BaseLifeCycle... dependencies) {
super(logger, executor, dependencies);
this.futureCommandConnectionRetry = futureCommandConnectionRetry;
this.allConnections = allConnections;
this.futureCommandConnectionHandleFactory = futureCommandConnectionHandleFactory;
this.maxConnectionReuse = maxConnectionReuse;
this.available = available;
}
@SuppressWarnings("unchecked")
protected void setResponseException(Exception ex, C conn) {
FutureCommand command = futureCommandConnectionRetry.getHandleFromConnection(conn).getOperation();
command.getResponseFuture().setException(ex);
}
@SuppressWarnings("unchecked")
protected void cancel(C conn) {
FutureCommand command = futureCommandConnectionRetry.getHandleFromConnection(conn).getOperation();
command.cancel(true);
}
@Provides
public C getConnection() throws InterruptedException, TimeoutException {
exceptionIfNotActive();
if (!hitBottom) {
hitBottom = available.size() == 0 && allConnections.availablePermits() == 0;
if (hitBottom)
logger.warn("%1s - saturated connection pool", this);
}
logger.debug("%1s - attempting to acquire connection; %d currently available", this, available.size());
C conn = available.poll(1, TimeUnit.SECONDS);
if (conn == null)
throw new TimeoutException("could not obtain a pooled connection within 1 seconds");
logger.trace("%1s - %2d - aquired", conn, conn.hashCode());
if (connectionValid(conn)) {
logger.debug("%1s - %2d - reusing", conn, conn.hashCode());
return conn;
} else {
logger.debug("%1s - %2d - unusable", conn, conn.hashCode());
allConnections.release();
return getConnection();
}
}
protected void fatalException(Exception ex, C conn) {
setResponseException(ex, conn);
this.exception = ex;
allConnections.release();
shutdown();
}
protected abstract boolean connectionValid(C conn);
public FutureCommandConnectionHandle<C> getHandle(FutureCommand<?,?,?> command) throws InterruptedException, TimeoutException {
exceptionIfNotActive();
C conn = getConnection();
FutureCommandConnectionHandle<C> handle = futureCommandConnectionHandleFactory.create(command, conn);
futureCommandConnectionRetry.associateHandleWithConnection(handle, conn);
return handle;
}
protected abstract void createNewConnection() throws InterruptedException;
public interface FutureCommandConnectionHandleFactory<C> {
@SuppressWarnings("unchecked")
FutureCommandConnectionHandle<C> create(FutureCommand command, C conn);
}
}

View File

@ -0,0 +1,138 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.command.pool;
import com.google.inject.Inject;
import org.jclouds.Logger;
import org.jclouds.Utils;
import org.jclouds.command.FutureCommand;
import org.jclouds.command.FutureCommandClient;
import org.jclouds.lifecycle.BaseLifeCycle;
import org.jclouds.lifecycle.Closer;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class FutureCommandConnectionPoolClient<C> extends BaseLifeCycle implements FutureCommandClient {
@Inject private Closer closer;
private final FutureCommandConnectionPool<C> futureCommandConnectionPool;
private final BlockingQueue<FutureCommand> commandQueue;
@Inject
public FutureCommandConnectionPoolClient(java.util.logging.Logger logger, ExecutorService executor, FutureCommandConnectionPool<C> futureCommandConnectionPool, BlockingQueue<FutureCommand> commandQueue) {
super(new Logger(logger), executor, futureCommandConnectionPool);
this.futureCommandConnectionPool = futureCommandConnectionPool;
this.commandQueue = commandQueue;
}
@Override
protected boolean shouldDoWork() {
return super.shouldDoWork() && futureCommandConnectionPool.getStatus().equals(Status.ACTIVE);
}
@Override
protected void doShutdown() {
if (exception == null && futureCommandConnectionPool.getException() != null)
exception = futureCommandConnectionPool.getException();
while (!commandQueue.isEmpty()) {
FutureCommand command = commandQueue.remove();
if (command != null) {
if (exception != null)
command.setException(exception);
else
command.cancel(true);
}
}
}
protected void doWork() throws InterruptedException {
FutureCommand command = commandQueue.poll(1, TimeUnit.SECONDS);
if (command != null) {
try {
invoke(command);
} catch (Exception e) {
Utils.<InterruptedException>rethrowIfRuntimeOrSameType(e);
logger.error(e, "Error processing command %s", command);
}
}
}
public <O extends FutureCommand> void submit(O operation) {
exceptionIfNotActive();
commandQueue.add(operation);
}
protected <O extends FutureCommand> void invoke(O operation) {
exceptionIfNotActive();
FutureCommandConnectionHandle<C> connectionHandle = null;
try {
connectionHandle = futureCommandConnectionPool.getHandle(operation);
} catch (InterruptedException e) {
logger.warn(e, "Interrupted getting a connection for operation %1s; retrying", operation);
commandQueue.add(operation);
return;
} catch (TimeoutException e) {
logger.warn(e, "Timeout getting a connection for operation %1s; retrying", operation);
commandQueue.add(operation);
return;
}
if (connectionHandle == null) {
logger.error("Failed to obtain connection for operation %1s; retrying", operation);
commandQueue.add(operation);
return;
}
connectionHandle.startConnection();
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("FutureCommandConnectionPoolClient");
sb.append("{status=").append(status);
sb.append(", commandQueue=").append((commandQueue != null) ? commandQueue.size() : 0);
sb.append(", futureCommandConnectionPool=").append(futureCommandConnectionPool);
sb.append('}');
return sb.toString();
}
public void close(){
try {
closer.close();
} catch (IOException e) {
e.printStackTrace(); // TODO: Adrian: Customise this generated block
}
}
}

View File

@ -0,0 +1,71 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.command.pool;
import org.jclouds.Logger;
import org.jclouds.command.FutureCommand;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public abstract class FutureCommandConnectionRetry<C> {
protected final BlockingQueue<FutureCommand> commandQueue;
protected final AtomicInteger errors;
protected final Logger logger;
public FutureCommandConnectionRetry(Logger logger, BlockingQueue<FutureCommand> commandQueue, AtomicInteger errors) {
this.logger = logger;
this.commandQueue = commandQueue;
this.errors = errors;
}
public abstract void associateHandleWithConnection(FutureCommandConnectionHandle<C> handle, C connection);
public abstract FutureCommandConnectionHandle<C> getHandleFromConnection(C connection);
public void shutdownConnectionAndRetryOperation(C connection) {
FutureCommandConnectionHandle<C> handle = getHandleFromConnection(connection);
if (handle != null) {
try {
logger.info("%1s - shutting down connection", connection);
handle.shutdownConnection();
incrementErrorCountAndRetry(handle.getOperation());
} catch (IOException e) {
logger.error(e, "%1s - error shutting down connection", connection);
}
}
}
public void incrementErrorCountAndRetry(FutureCommand command) {
errors.getAndIncrement();
logger.info("resubmitting command %1s", command);
commandQueue.add(command);
}
}

View File

@ -0,0 +1,70 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.command.pool.config;
import com.google.inject.*;
import com.google.inject.name.Named;
import org.jclouds.command.FutureCommand;
import org.jclouds.lifecycle.config.LifeCycleModule;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public abstract class FutureCommandConnectionPoolClientModule<C> extends AbstractModule {
protected void configure() {
install(new LifeCycleModule());
bind(AtomicInteger.class).toInstance(new AtomicInteger());// max errors
bind(new TypeLiteral<BlockingQueue<FutureCommand>>() {
}).to(new TypeLiteral<LinkedBlockingQueue<FutureCommand>>() {
}).in(Scopes.SINGLETON);
}
@Provides
@Singleton
public abstract BlockingQueue<C> provideAvailablePool(@Named("jclouds.pool.max_connections") int max) throws Exception;
/**
* controls production and destruction of real connections.
* <p/>
* aquire before a new connection is created
* release after an error has occurred
*
* @param max
* @return
* @throws Exception
*/
@Provides
@Singleton
public Semaphore provideTotalConnectionSemaphore(@Named("jclouds.pool.max_connections") int max) throws Exception {
return new Semaphore(max, true);
}
}

View File

@ -0,0 +1,39 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class HttpConstants {
public static final String CONTENT_LENGTH = "Content-Length";
public static final String CONTENT_TYPE = "Content-Type";
public static final String HOST = "Host";
public static final String DATE = "Date";
public static final String BINARY = "application/octet-stream";
public static final String PLAIN = "text/plain";
public static final String TRANSFER_ENCODING = "Transfer-Encoding";
}

View File

@ -0,0 +1,43 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class HttpException extends Exception {
public HttpException(String s) {
super(s); // TODO: Adrian: Customise this generated block
}
public HttpException(String s, Throwable throwable) {
super(s, throwable); // TODO: Adrian: Customise this generated block
}
public HttpException(Throwable throwable) {
super(throwable); // TODO: Adrian: Customise this generated block
}
}

View File

@ -0,0 +1,60 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http;
import org.jclouds.command.FutureCommand;
import org.jclouds.Logger;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class HttpFutureCommand<T> extends FutureCommand<HttpRequest, HttpResponse, T> {
public HttpFutureCommand(String method, String uri, ResponseCallable<T> responseCallable) {
super(new HttpRequest(method, uri), responseCallable);
}
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public abstract static class ResponseCallable<T> implements FutureCommand.ResponseCallable<HttpResponse, T> {
protected final Logger logger;
private HttpResponse response;
public ResponseCallable(Logger logger) {
this.logger = logger;
}
public HttpResponse getResponse() {
return response;
}
public void setResponse(HttpResponse response) {
this.response = response;
}
}
}

View File

@ -0,0 +1,46 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http;
import com.google.inject.Inject;
import org.jclouds.command.FutureCommand;
import org.jclouds.command.FutureCommandClient;
import java.util.List;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public interface HttpFutureCommandClient extends FutureCommandClient {
List<HttpRequestFilter> getRequestFilters();
@Inject
void setRequestFilters(List<HttpRequestFilter> requestFilters);
<O extends FutureCommand> void submit(O operation);
void close();
}

View File

@ -0,0 +1,119 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Collection;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class HttpRequest {
String method;
String uri;
Multimap<String, String> headers = HashMultimap.create();
Object content;
String contentType;
long contentLength = -1;
public HttpRequest(String method, String uri) {
this.method = checkNotNull(method, "method");
this.uri = checkNotNull(uri, "uri");
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("HttpRequest");
sb.append("{method='").append(method).append('\'');
sb.append(", uri='").append(uri).append('\'');
sb.append(", headers=").append(headers);
sb.append(", content set=").append(content != null);
sb.append(", contentType='").append(contentType).append('\'');
sb.append(", contentLength=").append(contentLength);
sb.append('}');
return sb.toString();
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public Multimap<String, String> getHeaders() {
return headers;
}
public void setHeaders(Multimap<String, String> headers) {
this.headers = headers;
}
public Object getContent() {
return content;
}
public void setContent(Object content) {
this.content = content;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public long getContentLength() {
return contentLength;
}
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}
public String getFirstHeaderOrNull(String string) {
Collection<String> values = headers.get(string);
return (values != null && values.size() >= 1) ? values.iterator()
.next() : null;
}
}

View File

@ -0,0 +1,34 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public interface HttpRequestFilter {
void filter(HttpRequest request) throws HttpException;
}

View File

@ -0,0 +1,102 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http;
import java.io.InputStream;
import java.util.Collection;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class HttpResponse {
int statusCode;
Multimap<String, String> headers = HashMultimap.create();
String message;
InputStream content;
String contentType;
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("HttpResponse");
sb.append("{statusCode=").append(statusCode);
sb.append(", headers=").append(headers);
sb.append(", message='").append(message).append('\'');
sb.append(", content set=").append(content != null);
sb.append(", contentType='").append(contentType).append('\'');
sb.append('}');
return sb.toString();
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public Multimap<String, String> getHeaders() {
return headers;
}
public void setHeaders(Multimap<String, String> headers) {
this.headers = headers;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public InputStream getContent() {
return content;
}
public void setContent(InputStream content) {
this.content = content;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public String getFirstHeaderOrNull(String string) {
Collection<String> values = headers.get(string);
return (values != null && values.size() >= 1) ? values.iterator()
.next() : null;
}
}

View File

@ -0,0 +1,166 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.jclouds.Logger;
import org.jclouds.Utils;
import org.jclouds.command.FutureCommand;
import com.google.inject.Inject;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class JavaUrlHttpFutureCommandClient implements HttpFutureCommandClient {
private URL target;
private List<HttpRequestFilter> requestFilters = Collections.emptyList();
private Logger logger;
public List<HttpRequestFilter> getRequestFilters() {
return requestFilters;
}
@Inject(optional = true)
public void setRequestFilters(List<HttpRequestFilter> requestFilters) {
this.requestFilters = requestFilters;
}
@Inject
public JavaUrlHttpFutureCommandClient(java.util.logging.Logger logger,
URL target) throws MalformedURLException {
this.logger = new Logger(logger);
this.target = target;
this.logger.info("configured to connect to target: %1s", target);
}
public <O extends FutureCommand> void submit(O operation) {
HttpRequest request = (HttpRequest) operation.getRequest();
HttpURLConnection connection = null;
try {
for (HttpRequestFilter filter : getRequestFilters()) {
filter.filter(request);
}
logger.trace("%1s - submitting request %2s", target, request);
connection = openJavaConnection(request);
HttpResponse response = getResponse(connection);
logger.trace("%1s - received response %2s", target, response);
operation.getResponseFuture().setResponse(response);
operation.getResponseFuture().run();
} catch (Exception e) {
if (connection != null) {
InputStream errorStream = connection.getErrorStream();
if (errorStream != null) {
try {
String errorMessage = Utils.toStringAndClose(connection
.getErrorStream());
logger.error(e,
"error encountered during the exception: %1s",
errorMessage);
} catch (IOException e1) {
}
}
}
operation.setException(e);
} finally {
// DO NOT disconnect, as it will also close the unconsumed outputStream from above.
// connection.disconnect();
}
}
public void close() {
// Nothing to stop;
}
private HttpResponse getResponse(HttpURLConnection connection)
throws IOException {
HttpResponse response = new HttpResponse();
response.setStatusCode(connection.getResponseCode());
for (String header : connection.getHeaderFields().keySet()) {
response.getHeaders().putAll(header,
connection.getHeaderFields().get(header));
}
response.setMessage(connection.getResponseMessage());
response.setContent(connection.getInputStream());
response.setContentType(connection
.getHeaderField(HttpConstants.CONTENT_TYPE));
return response;
}
private HttpURLConnection openJavaConnection(HttpRequest request)
throws IOException {
URL url = new URL(target, request.getUri());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod(request.getMethod());
for (String header : request.getHeaders().keySet()) {
for (String value : request.getHeaders().get(header))
connection.setRequestProperty(header, value);
}
connection.setRequestProperty(HttpConstants.CONTENT_TYPE, request
.getContentType());
if (request.getContent() != null) {
OutputStream out = connection.getOutputStream();
try {
if (request.getContent() instanceof String) {
OutputStreamWriter writer = new OutputStreamWriter(out);
writer.write((String) request.getContent());
writer.close();
} else if (request.getContent() instanceof InputStream) {
IOUtils.copy((InputStream) request.getContent(), out);
} else if (request.getContent() instanceof File) {
IOUtils.copy(new FileInputStream((File) request
.getContent()), out);
} else if (request.getContent() instanceof byte[]) {
IOUtils.write((byte[]) request.getContent(), out);
} else {
throw new UnsupportedOperationException(
"Content not supported "
+ request.getContent().getClass());
}
} finally {
IOUtils.closeQuietly(out);
}
}
return connection;
}
}

View File

@ -0,0 +1,71 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.commands;
import org.jclouds.http.commands.callables.xml.ParseSax;
import com.google.inject.Inject;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class CommandFactory {
@Inject
private ParseSaxFactory parseSaxFactory;
public static interface ParseSaxFactory {
ParseSax<?> create(ParseSax.HandlerWithResult<?> handler);
}
public GetAndParseSax<?> createGetAndParseSax(String uri,
ParseSax.HandlerWithResult<?> handler) {
return new GetAndParseSax(uri, parseSaxFactory.create(handler));
}
@Inject
private GetStringFactory getStringFactory;
public static interface GetStringFactory {
GetString create(String uri);
}
public GetString createGetString(String uri) {
return getStringFactory.create(uri);
}
@Inject
private HeadFactory headFactory;
public static interface HeadFactory {
Head create(String uri);
}
public Head createHead(String uri) {
return headFactory.create(uri);
}
}

View File

@ -0,0 +1,39 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.commands;
import org.jclouds.http.HttpFutureCommand;
import org.jclouds.http.commands.callables.xml.ParseSax;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class GetAndParseSax<T> extends HttpFutureCommand<T> {
public GetAndParseSax(String uri, ParseSax<T> callable) {
super("GET", uri, callable);
}
}

View File

@ -0,0 +1,42 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.commands;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.jclouds.http.HttpFutureCommand;
import org.jclouds.http.commands.callables.ReturnStringIf200;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class GetString extends HttpFutureCommand<String> {
@Inject
public GetString(ReturnStringIf200 callable, @Assisted String uri) {
super("GET", uri, callable);
}
}

View File

@ -0,0 +1,42 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.commands;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.jclouds.http.HttpFutureCommand;
import org.jclouds.http.commands.callables.ReturnTrueIf200;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class Head extends HttpFutureCommand<Boolean> {
@Inject
public Head(ReturnTrueIf200 callable, @Assisted String uri) {
super("HEAD", uri, callable);
}
}

View File

@ -0,0 +1,67 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.commands.callables;
import com.google.inject.Inject;
import org.jclouds.Logger;
import org.jclouds.Utils;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpFutureCommand;
import java.io.IOException;
import java.io.InputStream;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class ReturnStringIf200 extends HttpFutureCommand.ResponseCallable<String> {
@Inject
public ReturnStringIf200(java.util.logging.Logger logger) {
super(new Logger(logger));
}
public String call() throws HttpException {
int code = getResponse().getStatusCode();
if (code >= 400 && code < 500) {
throw new HttpException(String.format("Content not found - %1s", getResponse()));
} else if (code == 200) {
InputStream entity = getResponse().getContent();
if (entity == null)
throw new HttpException("no content");
String toReturn = null;
try {
toReturn = Utils.toStringAndClose(entity);
} catch (IOException e) {
throw new HttpException(String.format("Couldn't receive response %1s, entity: %2s ", getResponse(), toReturn), e);
}
return toReturn;
} else {
throw new HttpException(String.format("Unhandled status code - %1s", getResponse()));
}
}
}

View File

@ -0,0 +1,52 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.commands.callables;
import com.google.inject.Inject;
import org.jclouds.Logger;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpFutureCommand;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class ReturnTrueIf200 extends HttpFutureCommand.ResponseCallable<Boolean> {
@Inject
public ReturnTrueIf200(java.util.logging.Logger logger) {
super(new Logger(logger));
}
public Boolean call() throws HttpException {
if (getResponse().getStatusCode() == 200) {
return true;
} else if (getResponse().getStatusCode() == 404) {
return false;
} else {
throw new HttpException("Error checking bucket " + getResponse());
}
}
}

View File

@ -0,0 +1,111 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.commands.callables.xml;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.apache.commons.io.IOUtils;
import org.jclouds.Logger;
import org.jclouds.Utils;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpFutureCommand;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import java.io.InputStream;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class ParseSax<T> extends HttpFutureCommand.ResponseCallable<T> {
private XMLReader parser;
private HandlerWithResult<T> handler;
private boolean suckFirst = false;
@Inject
public ParseSax(java.util.logging.Logger logger, XMLReader parser, @Assisted HandlerWithResult<T> handler) {
super(new Logger(logger));
this.parser = parser;
this.handler = handler;
}
public T call() throws HttpException {
InputStream input = null;
try {
input = getResponse().getContent();
if (input != null) {
return parse(input);
} else {
throw new HttpException("No input to parse");
}
} catch (Exception e) {
Utils.<HttpException>rethrowIfRuntimeOrSameType(e);
throw new HttpException("Error parsing input for " + getResponse(), e);
}
}
public T parse(InputStream xml) throws HttpException {
parseAndCloseStream(xml, handler);
return handler.getResult();
}
private void parseAndCloseStream(InputStream xml, ContentHandler handler) throws HttpException {
parser.setContentHandler(handler);
String response = null;
try {
if (suckFirst) {
response = IOUtils.toString(xml);
logger.trace("received content %n%s", response);
IOUtils.closeQuietly(xml);
xml = IOUtils.toInputStream(response);
}
parser.parse(new InputSource(xml));
} catch (Exception e) {
StringBuilder message = new StringBuilder();
message.append("Error parsing input for ").append(handler);
if (response != null) {
message.append("\n").append(response);
}
logger.error(e, message.toString());
Utils.<HttpException>rethrowIfRuntimeOrSameType(e);
throw new HttpException(message.toString(), e);
} finally {
IOUtils.closeQuietly(xml);
}
}
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public abstract static class HandlerWithResult<T> extends DefaultHandler {
public abstract T getResult();
}
}

View File

@ -0,0 +1,62 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.commands.callables.xml.config;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class SaxModule extends AbstractModule {
@Provides
XMLReader provideXMLReader(SAXParserFactory factory) throws ParserConfigurationException, SAXException {
SAXParser saxParser = factory.newSAXParser();
XMLReader parser = saxParser.getXMLReader();
return parser;
}
@Provides
@Singleton
SAXParserFactory provideSAXParserFactory() {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(false);
factory.setValidating(false);
factory.setXIncludeAware(false);
return factory;
}
protected void configure() {
}
}

View File

@ -0,0 +1,63 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.commands.config;
import org.jclouds.http.commands.CommandFactory;
import org.jclouds.http.commands.GetString;
import org.jclouds.http.commands.Head;
import org.jclouds.http.commands.callables.xml.ParseSax;
import org.jclouds.http.commands.callables.xml.config.SaxModule;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryProvider;
/**
* note that all this private factory clutter will go away when the following is
* implemented @link http://code.google.com/p/google-guice/issues/detail?id=346
* it will be replaced with a configuration:
*
* @author Adrian Cole
*/
public class HttpCommandsModule extends AbstractModule {
protected void configure() {
bind(CommandFactory.GetStringFactory.class)
.toProvider(
FactoryProvider.newFactory(
CommandFactory.GetStringFactory.class,
GetString.class));
bind(CommandFactory.HeadFactory.class).toProvider(
FactoryProvider.newFactory(CommandFactory.HeadFactory.class,
Head.class));
install(new SaxModule());
bind(CommandFactory.ParseSaxFactory.class).toProvider(
FactoryProvider.newFactory(
new TypeLiteral<CommandFactory.ParseSaxFactory>() {
}, new TypeLiteral<ParseSax<?>>() {
}));
}
}

View File

@ -0,0 +1,58 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.config;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import org.jclouds.http.HttpFutureCommandClient;
import org.jclouds.http.JavaUrlHttpFutureCommandClient;
import java.net.MalformedURLException;
import java.net.URL;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class JavaUrlHttpFutureCommandClientModule extends AbstractModule {
@Override
protected void configure() {
//note this is not threadsafe, so it cannot be singleton
bind(HttpFutureCommandClient.class).to(JavaUrlHttpFutureCommandClient.class);
}
@Singleton
@Provides
protected URL provideAddress(@Named("jclouds.http.address") String address, @Named("jclouds.http.port") int port, @Named("jclouds.http.secure") boolean isSecure) throws MalformedURLException {
return new URL(isSecure ? "https" : "http", address, port, "/");
}
}

View File

@ -0,0 +1,167 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.lifecycle;
import org.jclouds.Logger;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.ExecutorService;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public abstract class BaseLifeCycle implements Runnable, LifeCycle {
protected final Logger logger;
protected final ExecutorService executor;
protected final BaseLifeCycle[] dependencies;
protected final Object statusLock;
protected volatile Status status;
protected Exception exception;
public BaseLifeCycle(Logger logger, ExecutorService executor, BaseLifeCycle... dependencies) {
this.logger = logger;
this.executor = executor;
this.dependencies = dependencies;
this.statusLock = new Object();
this.status = Status.INACTIVE;
}
public Status getStatus() {
return status;
}
public void run() {
try {
while (shouldDoWork()) {
doWork();
}
} catch (Exception e) {
logger.error(e, "Exception doing work");
this.exception = e;
}
this.status = Status.SHUTTING_DOWN;
doShutdown();
this.status = Status.SHUT_DOWN;
logger.info("%1s", this);
}
protected abstract void doWork() throws Exception;
protected abstract void doShutdown();
protected boolean shouldDoWork() {
try {
exceptionIfDepedenciesNotActive();
} catch (IllegalStateException e) {
return false;
}
return status.equals(Status.ACTIVE) && exception == null;
}
@PostConstruct
public void start() {
logger.info("starting %1s", this);
synchronized (this.statusLock) {
if (this.status.compareTo(Status.SHUTDOWN_REQUEST) >= 0) {
doShutdown();
this.status = Status.SHUT_DOWN;
this.statusLock.notifyAll();
return;
}
if (this.status.compareTo(Status.ACTIVE) == 0) {
this.statusLock.notifyAll();
return;
}
if (this.status.compareTo(Status.INACTIVE) != 0) {
throw new IllegalStateException("Illegal state: " + this.status);
}
exceptionIfDepedenciesNotActive();
this.status = Status.ACTIVE;
}
executor.execute(this);
}
protected void exceptionIfDepedenciesNotActive() {
for (BaseLifeCycle dependency : dependencies) {
if (dependency.status.compareTo(Status.ACTIVE) != 0) {
throw new IllegalStateException(String.format("Illegal state: %1s for component: %2s", dependency.status, dependency));
}
}
}
public Exception getException() {
return this.exception;
}
protected void awaitShutdown(long timeout) throws InterruptedException {
awaitStatus(Status.SHUT_DOWN, timeout);
}
protected void awaitStatus(Status intended, long timeout) throws InterruptedException {
synchronized (this.statusLock) {
long deadline = System.currentTimeMillis() + timeout;
long remaining = timeout;
while (this.status != intended) {
this.statusLock.wait(remaining);
if (timeout > 0) {
remaining = deadline - System.currentTimeMillis();
if (remaining <= 0) {
break;
}
}
}
}
}
@PreDestroy
public void shutdown() {
shutdown(2000);
}
public void shutdown(long waitMs) {
synchronized (this.statusLock) {
if (this.status.compareTo(Status.ACTIVE) > 0) {
return;
}
this.status = Status.SHUTDOWN_REQUEST;
try {
awaitShutdown(waitMs);
} catch (InterruptedException ignore) {
}
}
}
protected void exceptionIfNotActive() {
if (!status.equals(Status.ACTIVE))
throw new IllegalStateException(String.format("not active: %1s", this));
}
}

View File

@ -0,0 +1,50 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.lifecycle;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class Closer implements Closeable {
List<Closeable> methodsToClose = new ArrayList<Closeable>();
public void addToClose(Closeable toClose) {
methodsToClose.add(toClose);
}
public void close() throws IOException {
Collections.reverse(methodsToClose);
for (Closeable toClose : methodsToClose) {
toClose.close();
}
}
}

View File

@ -0,0 +1,95 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.lifecycle;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public interface LifeCycle {
/**
* @return the current state of the component;
*/
Status getStatus();
/**
* @return Exception or null, if there are no fatal Exceptions encountered in the lifecycle of this component.
*/
Exception getException();
/**
* Asynchronously starts the component
*/
@PostConstruct
void start();
/**
* Requests shutdown of the component.
*/
@PreDestroy
void shutdown();
/**
* Requests shutdown, but will only wait @link waitms milliseconds
*
* @param waitMs maximum time to wait in milliseconds
*/
void shutdown(long waitMs);
/**
* States that are possible for a component.
*/
public static enum Status {
/**
* The component is inactive / has not been started
*/
INACTIVE,
/**
* The component is active / processing I/O events.
*/
ACTIVE,
/**
* Shutdown of the component has been requested.
*/
SHUTDOWN_REQUEST,
/**
* The component is shutting down.
*/
SHUTTING_DOWN,
/**
* The component has shut down.
*/
SHUT_DOWN;
}
}

View File

@ -0,0 +1,129 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.lifecycle.config;
import static com.google.inject.matcher.Matchers.any;
import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.jclouds.lifecycle.Closer;
import com.google.inject.AbstractModule;
import com.google.inject.ProvisionException;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class LifeCycleModule extends AbstractModule {
protected void configure() {
final ExecutorService executor = Executors.newCachedThreadPool();
bind(ExecutorService.class).toInstance(executor);
Closer closer = new Closer();
closer.addToClose(new Closeable() {
public void close() throws IOException {
executor.shutdownNow();
}
});
bind(Closer.class).toInstance(closer);
bindPostInjectionInvoke(closer);
}
protected void bindPostInjectionInvoke(final Closer closer) {
bindListener(any(), new TypeListener() {
public <I> void hear(TypeLiteral<I> injectableType,
TypeEncounter<I> encounter) {
Set<Method> methods = new HashSet<Method>();
Class<? super I> type = injectableType.getRawType();
while (type != null) {
methods.addAll(Arrays.asList(type.getDeclaredMethods()));
type = type.getSuperclass();
}
for (final Method method : methods) {
PostConstruct postConstruct = method
.getAnnotation(PostConstruct.class);
if (postConstruct != null) {
encounter.register(new InjectionListener<I>() {
public void afterInjection(I injectee) {
try {
method.invoke(injectee);
} catch (InvocationTargetException ie) {
Throwable e = ie.getTargetException();
throw new ProvisionException(
e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new ProvisionException(
e.getMessage(), e);
}
}
});
}
PreDestroy preDestroy = method
.getAnnotation(PreDestroy.class);
if (preDestroy != null) {
encounter.register(new InjectionListener<I>() {
public void afterInjection(final I injectee) {
closer.addToClose(new Closeable() {
public void close() throws IOException {
try {
method.invoke(injectee);
} catch (InvocationTargetException ie) {
Throwable e = ie
.getTargetException();
throw new IOException(e
.getMessage());
} catch (IllegalAccessException e) {
throw new IOException(e
.getMessage());
}
}
});
}
});
}
}
}
});
}
}

View File

@ -0,0 +1,64 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.guice;
import java.util.logging.*;
/**
* Enable or disable Guice debug output
* on the console.
*/
public class GuiceDebug {
private static final Handler HANDLER;
static {
HANDLER = new StreamHandler(System.out, new Formatter() {
public String format(LogRecord record) {
return String.format("[Guice %s] %s%n",
record.getLevel().getName(),
record.getMessage());
}
});
HANDLER.setLevel(Level.ALL);
}
private GuiceDebug() {
}
public static Logger getLogger() {
return Logger.getLogger("com.google.inject");
}
public static void enable() {
Logger guiceLogger = getLogger();
guiceLogger.addHandler(GuiceDebug.HANDLER);
guiceLogger.setLevel(Level.ALL);
}
public static void disable() {
Logger guiceLogger = getLogger();
guiceLogger.setLevel(Level.OFF);
guiceLogger.removeHandler(GuiceDebug.HANDLER);
}
}

View File

@ -0,0 +1,210 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jclouds.http.commands.CommandFactory;
import org.jclouds.http.commands.GetAndParseSax;
import org.jclouds.http.commands.GetString;
import org.jclouds.http.commands.Head;
import org.jclouds.http.commands.callables.xml.ParseSax;
import org.jclouds.http.commands.config.HttpCommandsModule;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.AbstractHandler;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Test(threadPoolSize = 100)
public abstract class BaseHttpFutureCommandClientTest {
protected static final String XML = "<foo><bar>whoppers</bar></foo>";
protected Server server = null;
protected CommandFactory factory;
protected HttpFutureCommandClient client;
protected Injector injector;
@BeforeClass
@Parameters( { "test-jetty-port" })
public void setUpJetty(@Optional("8123") final int testPort)
throws Exception {
Handler handler = new AbstractHandler() {
public void handle(String target, HttpServletRequest request,
HttpServletResponse response, int dispatch)
throws IOException, ServletException {
if (request.getHeader("test") != null) {
response.setContentType("text/plain");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("test");
} else {
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(XML);
}
((Request) request).setHandled(true);
}
};
server = new Server(testPort);
server.setHandler(handler);
server.start();
final Properties properties = new Properties();
properties.put("jclouds.http.address", "localhost");
properties.put("jclouds.http.port", testPort + "");
properties.put("jclouds.http.secure", "false");
addConnectionProperties(properties);
final List<HttpRequestFilter> filters = new ArrayList<HttpRequestFilter>(
1);
filters.add(new HttpRequestFilter() {
public void filter(HttpRequest request) throws HttpException {
if (request.getHeaders().containsKey("filterme")) {
request.getHeaders().put("test", "test");
}
}
});
injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
Names.bindProperties(binder(), properties);
}
}, new HttpCommandsModule(), createClientModule(),
new AbstractModule() {
@Override
protected void configure() {
bind(new TypeLiteral<List<HttpRequestFilter>>() {
}).toInstance(filters);
}
});
factory = injector.getInstance(CommandFactory.class);
client = injector.getInstance(HttpFutureCommandClient.class);
assert client != null;
}
protected abstract void addConnectionProperties(Properties props);
protected abstract Module createClientModule();
@AfterClass
public void tearDownJetty() throws Exception {
client.close();
server.stop();
}
@Test(invocationCount = 500, timeOut = 1000)
void testRequestFilter() throws MalformedURLException, ExecutionException,
InterruptedException {
GetString get = factory.createGetString("/");
get.getRequest().getHeaders().put("filterme", "filterme");
client.submit(get);
assert get.get().trim().equals("test") : String.format(
"expected: [%1s], but got [%2s]", "test", get.get());
}
@Test(invocationCount = 500, timeOut = 1000)
void testGetStringWithHeader() throws MalformedURLException,
ExecutionException, InterruptedException {
GetString get = factory.createGetString("/");
get.getRequest().getHeaders().put("test", "test");
client.submit(get);
assert get.get().trim().equals("test") : String.format(
"expected: [%1s], but got [%2s]", "test", get.get());
}
@Test(invocationCount = 500, timeOut = 1000)
void testGetString() throws MalformedURLException, ExecutionException,
InterruptedException {
GetString get = factory.createGetString("/");
assert get != null;
client.submit(get);
assert get.get().trim().equals(XML) : String.format(
"expected: [%1s], but got [%2s]", XML, get.get());
}
@Test(invocationCount = 500, timeOut = 1000)
void testHead() throws MalformedURLException, ExecutionException,
InterruptedException {
Head head = factory.createHead("/");
assert head != null;
client.submit(head);
assert head.get();
}
@Test(invocationCount = 500, timeOut = 1000)
void testGetAndParseSax() throws MalformedURLException, ExecutionException,
InterruptedException {
GetAndParseSax getAndParseSax = factory.createGetAndParseSax("/",
new ParseSax.HandlerWithResult<String>() {
@Override
public String getResult() {
return bar;
}
private String bar = null;
private StringBuilder currentText = new StringBuilder();
@Override
public void endElement(String uri, String name, String qName) {
if (qName.equals("bar")) {
bar = currentText.toString();
}
currentText = new StringBuilder();
}
@Override
public void characters(char ch[], int start, int length) {
currentText.append(ch, start, length);
}
});
assert getAndParseSax != null;
client.submit(getAndParseSax);
assert getAndParseSax.get().equals("whoppers");
}
}

View File

@ -0,0 +1,48 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http;
import com.google.inject.Module;
import org.jclouds.http.config.JavaUrlHttpFutureCommandClientModule;
import org.testng.annotations.Test;
import java.util.Properties;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Test
public class JavaUrlHttpFutureCommandFutureCommandClientTest extends BaseHttpFutureCommandClientTest {
protected Module createClientModule() {
return new JavaUrlHttpFutureCommandClientModule();
}
protected void addConnectionProperties(Properties props) {
//NONE
}
}

View File

@ -0,0 +1,66 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.commands;
import static org.easymock.classextension.EasyMock.createMock;
import org.jclouds.http.commands.callables.ReturnStringIf200;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.logging.Logger;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Test
public class GetStringTest {
private static final String GOOD_PATH = "/index.html";
private GetString get = null;
private ReturnStringIf200 callable = null;
@BeforeMethod
void setUp() {
callable = new ReturnStringIf200(createMock(Logger.class));
get = new GetString(callable, GOOD_PATH);
}
@AfterMethod
void tearDown() {
get = null;
callable = null;
}
@Test
public void testConstructor() {
assert get.getResponseFuture() != null;
assert get.getRequest().getUri().equals(GOOD_PATH);
assert get.getRequest().getMethod().equals("GET");
}
}

View File

@ -0,0 +1,95 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.commands.callables;
import org.apache.commons.io.IOUtils;
import static org.easymock.classextension.EasyMock.*;
import org.jclouds.http.HttpFutureCommand;
import org.jclouds.http.HttpResponse;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
@Test
public class ReturnStringIf200Test {
private HttpFutureCommand.ResponseCallable<String> callable = null;
@BeforeMethod
void setUp() {
callable = new ReturnStringIf200(createMock(Logger.class));
}
@AfterMethod
void tearDown() {
callable = null;
}
@Test
public void testExceptionWhenNoContentOn200() throws ExecutionException, InterruptedException, TimeoutException, IOException {
HttpResponse response = createMock(HttpResponse.class);
expect(response.getStatusCode()).andReturn(200);
expect(response.getContent()).andReturn(null);
replay(response);
callable.setResponse(response);
try {
callable.call();
} catch (Exception e) {
assert e.getMessage().equals("no content");
}
verify(response);
}
@Test
public void testExceptionWhenIOExceptionOn200() throws ExecutionException, InterruptedException, TimeoutException, IOException {
HttpResponse response = createMock(HttpResponse.class);
expect(response.getStatusCode()).andReturn(200);
RuntimeException exception = new RuntimeException("bad");
expect(response.getContent()).andThrow(exception);
replay(response);
callable.setResponse(response);
try {
callable.call();
} catch (Exception e) {
assert e.equals(exception);
}
verify(response);
}
@Test
public void testResponseOk() throws Exception {
HttpResponse response = createMock(HttpResponse.class);
expect(response.getStatusCode()).andReturn(200);
expect(response.getContent()).andReturn(IOUtils.toInputStream("hello"));
replay(response);
callable.setResponse(response);
assert "hello".equals(callable.call());
verify(response);
}
}

View File

@ -0,0 +1,68 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.commands.config;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.jclouds.http.HttpFutureCommand;
import org.jclouds.http.commands.CommandFactory;
import org.jclouds.http.commands.callables.xml.ParseSax;
import org.testng.annotations.Test;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Test
public class HttpCommandsModuleTest {
public void testGetString() {
Injector i = Guice.createInjector(new HttpCommandsModule());
CommandFactory factory = i.getInstance(CommandFactory.class);
HttpFutureCommand get = factory.createGetString("/index.html");
assert get != null;
assert get.getResponseFuture() != null;
}
public void testHead() {
Injector i = Guice.createInjector(new HttpCommandsModule());
CommandFactory factory = i.getInstance(CommandFactory.class);
HttpFutureCommand Head = factory.createHead("/index.html");
assert Head != null;
assert Head.getResponseFuture() != null;
}
public void testGetAndParseXml() {
Injector i = Guice.createInjector(new HttpCommandsModule());
CommandFactory factory = i.getInstance(CommandFactory.class);
HttpFutureCommand GetAndParseXml = factory.createGetAndParseSax("/index.html", new ParseSax.HandlerWithResult<String>(){
public String getResult() {
return "hello";
}
});
assert GetAndParseXml != null;
assert GetAndParseXml.getResponseFuture() != null;
}
}

View File

@ -0,0 +1,123 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.lifecycle.config;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import org.jclouds.lifecycle.Closer;
import org.testng.annotations.Test;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Test
public class LifeCycleModuleTest {
@Test
void testBindsExecutor() {
Injector i = Guice.createInjector(new LifeCycleModule());
assert i.getInstance(ExecutorService.class) != null;
}
@Test
void testBindsCloser() {
Injector i = Guice.createInjector(new LifeCycleModule());
assert i.getInstance(Closer.class) != null;
}
@Test
void testCloserClosesExecutor() throws IOException {
Injector i = Guice.createInjector(new LifeCycleModule());
ExecutorService executor = i.getInstance(ExecutorService.class);
assert !executor.isShutdown();
Closer closer = i.getInstance(Closer.class);
closer.close();
assert executor.isShutdown();
}
static class PreDestroyable {
boolean isClosed = false;
@Inject
PreDestroyable(ExecutorService executor) {
this.executor = executor;
}
ExecutorService executor;
@PreDestroy
public void close() {
assert !executor.isShutdown();
isClosed = true;
}
}
@Test
void testCloserPreDestroyOrder() throws IOException {
Injector i = Guice.createInjector(new LifeCycleModule(), new AbstractModule() {
protected void configure() {
bind(PreDestroyable.class);
}
});
ExecutorService executor = i.getInstance(ExecutorService.class);
assert !executor.isShutdown();
PreDestroyable preDestroyable = i.getInstance(PreDestroyable.class);
assert !preDestroyable.isClosed;
Closer closer = i.getInstance(Closer.class);
closer.close();
assert preDestroyable.isClosed;
assert executor.isShutdown();
}
static class PostConstructable {
boolean isStarted;
@PostConstruct
void start() {
isStarted = true;
}
}
@Test
void testPostConstruct() {
Injector i = Guice.createInjector(new LifeCycleModule(), new AbstractModule() {
protected void configure() {
bind(PostConstructable.class);
}
});
PostConstructable postConstructable = i.getInstance(PostConstructable.class);
assert postConstructable.isStarted;
}
}

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
====================================================================
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.html
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">
<parent>
<artifactId>project</artifactId>
<groupId>org.jclouds</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../../project/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jclouds-httpnio</artifactId>
<name>jclouds HttpNio Client</name>
<packaging>jar</packaging>
<description>HttpNio Connection Pooling client</description>
<scm>
<connection>scm:svn:http://jclouds.googlecode.com/svn/trunk/extensions/httpnio</connection>
<developerConnection>scm:svn:https://jclouds.googlecode.com/svn/trunk/extensions/httpnio</developerConnection>
<url>http://jclouds.googlecode.com/svn/trunk/extensions/httpnio</url>
</scm>
<dependencies>
<!-- TODO find out how to get this transatively from core -->
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.16</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-core</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore-nio</artifactId>
<version>4.0</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,107 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.httpnio;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import org.apache.http.Header;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpVersion;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.nio.entity.NByteArrayEntity;
import org.apache.http.nio.entity.NFileEntity;
import org.apache.http.nio.entity.NStringEntity;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
public class HttpNioUtils {
public static HttpEntityEnclosingRequest convertToApacheRequest(
HttpRequest object) {
BasicHttpEntityEnclosingRequest apacheRequest = new BasicHttpEntityEnclosingRequest(
object.getMethod(), object.getUri(), HttpVersion.HTTP_1_1);
for (String header : object.getHeaders().keySet()) {
for (String value : object.getHeaders().get(header))
apacheRequest.addHeader(header, value);
}
Object content = object.getContent();
if (content != null) {
addEntityForContent(apacheRequest, content,
object.getContentType(), object.getContentLength());
}
return apacheRequest;
}
public static void addEntityForContent(
BasicHttpEntityEnclosingRequest apacheRequest, Object content,
String contentType, long length) {
if (content instanceof InputStream) {
InputStream inputStream = (InputStream) content;
if (length <= 0)
throw new IllegalArgumentException(
"you must specify size when content is an InputStream");
InputStreamEntity entity = new InputStreamEntity(inputStream,
length);
entity.setContentType(contentType);
apacheRequest.setEntity(entity);
} else if (content instanceof String) {
NStringEntity nStringEntity = null;
try {
nStringEntity = new NStringEntity((String) content);
} catch (UnsupportedEncodingException e) {
throw new UnsupportedOperationException(
"Encoding not supported", e);
}
nStringEntity.setContentType(contentType);
apacheRequest.setEntity(nStringEntity);
} else if (content instanceof File) {
apacheRequest.setEntity(new NFileEntity((File) content,
contentType, true));
} else if (content instanceof byte[]) {
NByteArrayEntity entity = new NByteArrayEntity((byte[]) content);
entity.setContentType(contentType);
apacheRequest.setEntity(entity);
} else {
throw new UnsupportedOperationException(
"Content class not supported: "
+ content.getClass().getName());
}
}
public static HttpResponse convertToJavaCloudsResponse(
org.apache.http.HttpResponse apacheResponse) throws IOException {
HttpResponse response = new HttpResponse();
if (apacheResponse.getEntity() != null) {
response.setContent(apacheResponse.getEntity().getContent());
}
for (Header header : apacheResponse.getAllHeaders()) {
response.getHeaders().put(header.getName(), header.getValue());
}
response.setStatusCode(apacheResponse.getStatusLine().getStatusCode());
return response;
}
}

View File

@ -0,0 +1,71 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.httpnio.config;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
import org.apache.http.nio.NHttpConnection;
import org.jclouds.command.pool.FutureCommandConnectionRetry;
import org.jclouds.http.HttpFutureCommandClient;
import org.jclouds.http.httpnio.config.internal.NonSSLHttpNioConnectionPoolClientModule;
import org.jclouds.http.httpnio.config.internal.SSLHttpNioConnectionPoolClientModule;
import org.jclouds.http.httpnio.pool.HttpNioConnectionPoolClient;
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandConnectionRetry;
import java.net.InetSocketAddress;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class HttpNioConnectionPoolClientModule extends AbstractModule {
@Named("jclouds.http.secure")
boolean isSecure;
@Override
protected void configure() {
requestInjection(this);
//TODO test...
if (isSecure)
install(new SSLHttpNioConnectionPoolClientModule());
else
install(new NonSSLHttpNioConnectionPoolClientModule());
bind(new TypeLiteral<FutureCommandConnectionRetry<NHttpConnection>>(){}).to(HttpNioFutureCommandConnectionRetry.class);
bind(HttpFutureCommandClient.class).to(HttpNioConnectionPoolClient.class);
}
@Singleton
@Provides
protected InetSocketAddress provideAddress(@Named("jclouds.http.address") String address, @Named("jclouds.http.port") int port) {
return new InetSocketAddress(address, port);
}
}

View File

@ -0,0 +1,133 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.httpnio.config.internal;
import com.google.inject.*;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.FactoryProvider;
import com.google.inject.name.Named;
import org.apache.http.ConnectionReuseStrategy;
import org.apache.http.HttpEntity;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.nio.NHttpConnection;
import org.apache.http.nio.entity.BufferingNHttpEntity;
import org.apache.http.nio.protocol.AsyncNHttpClientHandler;
import org.apache.http.nio.protocol.NHttpRequestExecutionHandler;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.nio.reactor.IOReactorException;
import org.apache.http.nio.util.ByteBufferAllocator;
import org.apache.http.nio.util.HeapByteBufferAllocator;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.*;
import org.jclouds.command.pool.FutureCommandConnectionRetry;
import org.jclouds.command.pool.config.FutureCommandConnectionPoolClientModule;
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandConnectionHandle;
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandConnectionPool;
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandConnectionRetry;
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandExecutionHandler;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public abstract class BaseHttpNioConnectionPoolClientModule extends FutureCommandConnectionPoolClientModule<NHttpConnection> {
@Provides
@Singleton
public AsyncNHttpClientHandler provideAsyncNttpClientHandler(BasicHttpProcessor httpProcessor, NHttpRequestExecutionHandler execHandler, ConnectionReuseStrategy connStrategy, ByteBufferAllocator allocator, HttpParams params) {
return new AsyncNHttpClientHandler(httpProcessor, execHandler, connStrategy, allocator, params);
}
@Provides
@Singleton
public BasicHttpProcessor provideClientProcessor() {
BasicHttpProcessor httpproc = new BasicHttpProcessor();
httpproc.addInterceptor(new RequestContent());
httpproc.addInterceptor(new RequestTargetHost());
httpproc.addInterceptor(new RequestConnControl());
httpproc.addInterceptor(new RequestUserAgent());
httpproc.addInterceptor(new RequestExpectContinue());
return httpproc;
}
@Provides
@Singleton
public HttpParams provideHttpParams() {
HttpParams params = new BasicHttpParams();
params
.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000)
.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
.setParameter(CoreProtocolPNames.ORIGIN_SERVER, "jclouds/1.0");
return params;
}
protected void configure() {
super.configure();
bind(HttpNioFutureCommandExecutionHandler.ConsumingNHttpEntityFactory.class).toProvider(FactoryProvider.newFactory(HttpNioFutureCommandExecutionHandler.ConsumingNHttpEntityFactory.class, InjectableBufferingNHttpEntity.class)).in(Scopes.SINGLETON);
bind(NHttpRequestExecutionHandler.class).to(HttpNioFutureCommandExecutionHandler.class).in(Scopes.SINGLETON);
bind(ConnectionReuseStrategy.class).to(DefaultConnectionReuseStrategy.class).in(Scopes.SINGLETON);
bind(ByteBufferAllocator.class).to(HeapByteBufferAllocator.class);
bind(FutureCommandConnectionRetry.class).to(HttpNioFutureCommandConnectionRetry.class);
bind(HttpNioFutureCommandConnectionPool.FutureCommandConnectionHandleFactory.class).toProvider(FactoryProvider.newFactory(new TypeLiteral<HttpNioFutureCommandConnectionPool.FutureCommandConnectionHandleFactory>() {
}, new TypeLiteral<HttpNioFutureCommandConnectionHandle>() {
}));
}
static class InjectableBufferingNHttpEntity extends BufferingNHttpEntity {
@Inject
public InjectableBufferingNHttpEntity(@Assisted HttpEntity httpEntity, ByteBufferAllocator allocator) {
super(httpEntity, allocator);
}
}
@Override
public BlockingQueue<NHttpConnection> provideAvailablePool(@Named("jclouds.pool.max_connections") int max) throws Exception {
return new ArrayBlockingQueue<NHttpConnection>(max, true);
}
@Provides
@Singleton
public abstract IOEventDispatch provideClientEventDispatch(AsyncNHttpClientHandler handler, HttpParams params) throws Exception;
@Provides
@Singleton
public DefaultConnectingIOReactor provideDefaultConnectingIOReactor(@Named("jclouds.http.pool.io_worker_threads") int ioWorkerThreads, HttpParams params) throws IOReactorException {
return new DefaultConnectingIOReactor(ioWorkerThreads, params);
}
}

View File

@ -0,0 +1,43 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.httpnio.config.internal;
import org.apache.http.impl.nio.DefaultClientIOEventDispatch;
import org.apache.http.nio.protocol.AsyncNHttpClientHandler;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.params.HttpParams;
import org.jclouds.http.httpnio.config.internal.BaseHttpNioConnectionPoolClientModule;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class NonSSLHttpNioConnectionPoolClientModule extends BaseHttpNioConnectionPoolClientModule {
public IOEventDispatch provideClientEventDispatch(AsyncNHttpClientHandler handler, HttpParams params) throws Exception {
return new DefaultClientIOEventDispatch(
handler,
params);
}
}

View File

@ -0,0 +1,56 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.httpnio.config.internal;
import org.apache.http.impl.nio.SSLClientIOEventDispatch;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.nio.protocol.AsyncNHttpClientHandler;
import org.apache.http.params.HttpParams;
import org.jclouds.http.httpnio.config.internal.BaseHttpNioConnectionPoolClientModule;
import javax.net.ssl.SSLContext;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class SSLHttpNioConnectionPoolClientModule extends BaseHttpNioConnectionPoolClientModule {
protected void configure() {
super.configure();
}
// note until a bug is fixed, you cannot annotate overriding methods with google annotations
// http://code.google.com/p/google-guice/issues/detail?id=347
@Override
public IOEventDispatch provideClientEventDispatch(AsyncNHttpClientHandler handler, HttpParams params) throws Exception {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
return new SSLClientIOEventDispatch(
handler,
context,
params);
}
}

View File

@ -0,0 +1,78 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.httpnio.pool;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.http.nio.NHttpConnection;
import org.jclouds.command.FutureCommand;
import org.jclouds.command.pool.FutureCommandConnectionPoolClient;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpFutureCommandClient;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.logging.Logger;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Singleton
public class HttpNioConnectionPoolClient extends FutureCommandConnectionPoolClient<NHttpConnection> implements HttpFutureCommandClient {
private List<HttpRequestFilter> requestFilters = Collections.emptyList();
public List<HttpRequestFilter> getRequestFilters() {
return requestFilters;
}
@Inject(optional = true)
public void setRequestFilters(List<HttpRequestFilter> requestFilters) {
this.requestFilters = requestFilters;
}
@Override
protected <O extends FutureCommand> void invoke(O operation) {
HttpRequest request = (HttpRequest) operation.getRequest();
try {
for (HttpRequestFilter filter : getRequestFilters()) {
filter.filter(request);
}
super.invoke(operation);
} catch (HttpException e) {
operation.setException(e);
}
}
@Inject
public HttpNioConnectionPoolClient(Logger logger, ExecutorService executor, HttpNioFutureCommandConnectionPool httpFutureCommandConnectionHandleNHttpConnectionNioFutureCommandConnectionPool, BlockingQueue<FutureCommand> commandQueue) {
super(logger, executor, httpFutureCommandConnectionHandleNHttpConnectionNioFutureCommandConnectionPool, commandQueue); // TODO: Adrian: Customise this generated block
}
}

View File

@ -0,0 +1,60 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.httpnio.pool;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.apache.http.nio.NHttpConnection;
import org.jclouds.command.FutureCommand;
import org.jclouds.command.pool.FutureCommandConnectionHandle;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Semaphore;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class HttpNioFutureCommandConnectionHandle extends FutureCommandConnectionHandle<NHttpConnection> {
@Inject
public HttpNioFutureCommandConnectionHandle(java.util.logging.Logger logger, BlockingQueue<NHttpConnection> available, Semaphore maxConnections, @Assisted NHttpConnection conn, @Assisted FutureCommand operation) throws InterruptedException {
super(logger, maxConnections, operation, conn, available);
}
public void startConnection() {
conn.getContext().setAttribute("operation", operation);
logger.trace("invoking %1s on connection %2s", operation, conn);
conn.requestOutput();
}
public void shutdownConnection() throws IOException {
conn.shutdown();
}
}

View File

@ -0,0 +1,213 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.httpnio.pool;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import org.apache.http.HttpException;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.nio.NHttpConnection;
import org.apache.http.nio.protocol.AsyncNHttpClientHandler;
import org.apache.http.nio.protocol.EventListener;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.nio.reactor.IOReactorStatus;
import org.apache.http.nio.reactor.SessionRequest;
import org.apache.http.nio.reactor.SessionRequestCallback;
import org.jclouds.Logger;
import org.jclouds.command.FutureCommand;
import org.jclouds.command.pool.FutureCommandConnectionPool;
import org.jclouds.command.pool.FutureCommandConnectionRetry;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class HttpNioFutureCommandConnectionPool extends FutureCommandConnectionPool<NHttpConnection> implements EventListener {
private final NHttpClientConnectionPoolSessionRequestCallback sessionCallback;
private final DefaultConnectingIOReactor ioReactor;
private final IOEventDispatch dispatch;
private final InetSocketAddress target;
private final int maxSessionFailures;
@Inject
public HttpNioFutureCommandConnectionPool(java.util.logging.Logger logger, ExecutorService executor, Semaphore allConnections, BlockingQueue<NHttpConnection> available, AsyncNHttpClientHandler clientHandler, DefaultConnectingIOReactor ioReactor, IOEventDispatch dispatch, FutureCommandConnectionHandleFactory requestHandleFactory, InetSocketAddress target, FutureCommandConnectionRetry<NHttpConnection> futureCommandConnectionRetry, @Named("jclouds.http.pool.max_connection_reuse") int maxConnectionReuse, @Named("jclouds.http.pool.max_session_failures") int maxSessionFailures) {
super(new Logger(logger), executor, futureCommandConnectionRetry, allConnections, requestHandleFactory, maxConnectionReuse, available);
this.ioReactor = ioReactor;
this.dispatch = dispatch;
this.target = target;
this.maxSessionFailures = maxSessionFailures;
this.sessionCallback = new NHttpClientConnectionPoolSessionRequestCallback();
clientHandler.setEventListener(this);
}
@Override
public void start() {
synchronized (this.statusLock) {
if (this.status.compareTo(Status.INACTIVE) == 0) {
executor.execute(new Runnable() {
public void run() {
try {
ioReactor.execute(dispatch);
} catch (IOException e) {
exception = e;
logger.error(e, "Error dispatching %1s", dispatch);
status = Status.SHUTDOWN_REQUEST;
}
}
});
}
super.start();
}
}
public void shutdownReactor(long waitMs) {
try {
this.ioReactor.shutdown(waitMs);
} catch (IOException e) {
logger.error(e, "Error shutting down reactor");
}
}
public boolean connectionValid(NHttpConnection conn) {
return conn.isOpen() && !conn.isStale() && conn.getMetrics().getRequestCount() < maxConnectionReuse;
}
protected void doWork() throws Exception {
createNewConnection();
}
@Override
protected void doShutdown() {
// Give the I/O reactor 10 sec to shut down
shutdownReactor(10000);
}
protected void createNewConnection() throws InterruptedException {
boolean acquired = allConnections.tryAcquire(1, TimeUnit.SECONDS);
if (acquired) {
if (shouldDoWork()) {
logger.debug("%1s - opening new connection", target);
ioReactor.connect(target, null, null, sessionCallback);
} else {
allConnections.release();
}
}
}
@Override
protected boolean shouldDoWork() {
return super.shouldDoWork() && ioReactor.getStatus().equals(IOReactorStatus.ACTIVE);
}
class NHttpClientConnectionPoolSessionRequestCallback implements SessionRequestCallback {
public void completed(SessionRequest request) {
logger.trace("%1s - %2s - operation complete", request, request.getAttachment());
}
public void cancelled(SessionRequest request) {
logger.info("%1s - %2s - operation cancelled", request, request.getAttachment());
releaseConnectionAndCancelResponse(request);
}
private void releaseConnectionAndCancelResponse(SessionRequest request) {
allConnections.release();
FutureCommand frequest = (FutureCommand) request.getAttachment();
if (frequest != null) {
frequest.cancel(true);
}
}
private void releaseConnectionAndSetResponseException(SessionRequest request, Exception e) {
allConnections.release();
FutureCommand frequest = (FutureCommand) request.getAttachment();
if (frequest != null) {
frequest.setException(e);
}
}
public void failed(SessionRequest request) {
int count = currentSessionFailures.getAndIncrement();
logger.error(request.getException(), "%1s - %2s - operation failed", request, request.getAttachment());
releaseConnectionAndSetResponseException(request, request.getException());
if (count >= maxSessionFailures) {
exception = request.getException();
}
}
public void timeout(SessionRequest request) {
logger.warn("%1s - %2s - operation timed out", request, request.getAttachment());
releaseConnectionAndCancelResponse(request);
}
}
public void connectionOpen(NHttpConnection conn) {
conn.setSocketTimeout(0);
available.offer(conn);
logger.trace("%1s - %2d - open", conn, conn.hashCode());
}
public void connectionTimeout(NHttpConnection conn) {
logger.warn("%1s - %2d - timeout %2d", conn, conn.hashCode(), conn.getSocketTimeout());
allConnections.release();
futureCommandConnectionRetry.shutdownConnectionAndRetryOperation(conn);
}
public void connectionClosed(NHttpConnection conn) {
allConnections.release();
logger.trace("%1s - %2d - closed", conn, conn.hashCode());
}
public void fatalIOException(IOException ex, NHttpConnection conn) {
exception = ex;
logger.error(ex, "%1s - %2d - %3s - pool error", conn, conn.hashCode(), target);
futureCommandConnectionRetry.shutdownConnectionAndRetryOperation(conn);
}
public void fatalProtocolException(HttpException ex, NHttpConnection conn) {
exception = ex;
logger.error(ex, "%1s - %2d - %3s - http error", conn, conn.hashCode(), target);
fatalException(ex, conn);
}
public static interface FutureCommandConnectionHandleFactory extends FutureCommandConnectionPool.FutureCommandConnectionHandleFactory<NHttpConnection> {
HttpNioFutureCommandConnectionHandle create(FutureCommand command, NHttpConnection conn);
}
}

View File

@ -0,0 +1,60 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.httpnio.pool;
import com.google.inject.Inject;
import org.apache.http.nio.NHttpConnection;
import org.jclouds.command.FutureCommand;
import org.jclouds.Logger;
import org.jclouds.command.pool.FutureCommandConnectionRetry;
import org.jclouds.command.pool.FutureCommandConnectionHandle;
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandConnectionHandle;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
public class HttpNioFutureCommandConnectionRetry extends FutureCommandConnectionRetry<NHttpConnection> {
@Inject
public HttpNioFutureCommandConnectionRetry(java.util.logging.Logger logger, BlockingQueue<FutureCommand> commandQueue, AtomicInteger errors) {
super(new Logger(logger), commandQueue, errors);
}
@Override
public void associateHandleWithConnection(FutureCommandConnectionHandle<NHttpConnection> handle, NHttpConnection connection) {
connection.getContext().setAttribute("operation-handle", handle);
}
@Override
public HttpNioFutureCommandConnectionHandle getHandleFromConnection(NHttpConnection connection) {
return (HttpNioFutureCommandConnectionHandle) connection.getContext().getAttribute("operation-handle");
}
// @Override
// public void incrementErrorCountAndRetry(FutureCommand operation) {
// ((HttpEntityEnclosingRequest) operation.getRequest()).removeHeaders(HTTP.CONTENT_LEN);
// ((HttpEntityEnclosingRequest) operation.getRequest()).removeHeaders(HTTP.DATE_HEADER);
// super.incrementErrorCountAndRetry(operation);
// }
}

View File

@ -0,0 +1,148 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.httpnio.pool;
import com.google.inject.Inject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.nio.NHttpClientConnection;
import org.apache.http.nio.entity.ConsumingNHttpEntity;
import org.apache.http.nio.protocol.NHttpRequestExecutionHandler;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.jclouds.Logger;
import org.jclouds.command.FutureCommand;
import org.jclouds.http.HttpFutureCommand;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.httpnio.HttpNioUtils;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class HttpNioFutureCommandExecutionHandler implements NHttpRequestExecutionHandler {
private final ExecutorService executor;
protected final Logger logger;
private final ConsumingNHttpEntityFactory entityFactory;
private final HttpNioFutureCommandConnectionRetry futureOperationRetry;
public interface ConsumingNHttpEntityFactory {
public ConsumingNHttpEntity create(HttpEntity httpEntity);
}
@Inject
public HttpNioFutureCommandExecutionHandler(java.util.logging.Logger logger, ConsumingNHttpEntityFactory entityFactory, ExecutorService executor, HttpNioFutureCommandConnectionRetry futureOperationRetry) {
this.logger = new Logger(logger);
this.executor = executor;
this.entityFactory = entityFactory;
this.futureOperationRetry = futureOperationRetry;
}
public void initalizeContext(HttpContext context, Object attachment) {
}
public HttpEntityEnclosingRequest submitRequest(HttpContext context) {
HttpFutureCommand operation = (HttpFutureCommand) context.removeAttribute("operation");
if (operation != null) {
//TODO determine why type is lost
HttpRequest object = (HttpRequest) operation.getRequest();
return HttpNioUtils.convertToApacheRequest(object);
}
return null;
}
public ConsumingNHttpEntity responseEntity(HttpResponse response, HttpContext context) throws IOException {
return entityFactory.create(response.getEntity());
}
public void handleResponse(HttpResponse response, HttpContext context) throws IOException {
HttpNioFutureCommandConnectionHandle handle = (HttpNioFutureCommandConnectionHandle) context.removeAttribute("operation-handle");
if (handle != null) {
try {
FutureCommand command = handle.getOperation();
int code = response.getStatusLine().getStatusCode();
//normal codes for rest commands
if ((code >= 200 && code < 300) || code == 404) {
processResponse(response, command);
} else {
if (isRetryable(response)) {
futureOperationRetry.shutdownConnectionAndRetryOperation((NHttpClientConnection) context.getAttribute(ExecutionContext.HTTP_CONNECTION));
} else {
operationFailed(command);
}
}
} finally {
releaseConnectionToPool(handle);
}
} else {
throw new IllegalStateException(String.format("No operation-handle associated with operation %1s", context));
}
}
protected boolean isRetryable(HttpResponse response) throws IOException {
int code = response.getStatusLine().getStatusCode();
return code == 500 || code == 503;
}
protected void releaseConnectionToPool(HttpNioFutureCommandConnectionHandle handle) {
try {
handle.release();
} catch (InterruptedException e) {
logger.error(e, "Interrupted releasing handle %1s", handle);
}
}
protected void operationFailed(FutureCommand command) throws IOException {
String message = String.format("command failed: %1s", command);
logger.error(message);
command.getResponseFuture().setException(new IOException(message));
}
protected void processResponse(HttpResponse apacheResponse, FutureCommand command) throws IOException {
org.jclouds.http.HttpResponse response = HttpNioUtils.convertToJavaCloudsResponse(apacheResponse);
command.getResponseFuture().setResponse(response);
logger.trace("submitting response task %1s", command.getResponseFuture());
executor.submit(command.getResponseFuture());
}
public void finalizeContext(HttpContext context) {
HttpNioFutureCommandConnectionHandle handle = (HttpNioFutureCommandConnectionHandle) context.removeAttribute("operation-handle");
if (handle != null) {
try {
handle.cancel();
} catch (Exception e) {
logger.error(e, "Error cancelling handle %1s", handle);
}
}
}
}

View File

@ -0,0 +1,52 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.http.httpnio.pool;
import com.google.inject.Module;
import org.jclouds.http.BaseHttpFutureCommandClientTest;
import org.jclouds.http.httpnio.config.HttpNioConnectionPoolClientModule;
import org.testng.annotations.Test;
import java.util.Properties;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Test
public class HttpNioConnectionPoolFutureCommandClientTest extends BaseHttpFutureCommandClientTest {
protected void addConnectionProperties(Properties properties) {
properties.setProperty("jclouds.http.pool.max_connection_reuse", "75");
properties.setProperty("jclouds.http.pool.max_session_failures", "2");
properties.setProperty("jclouds.http.pool.request_invoker_threads", "1");
properties.setProperty("jclouds.http.pool.io_worker_threads", "2");
properties.setProperty("jclouds.pool.max_connections", "12");
}
protected Module createClientModule() {
return new HttpNioConnectionPoolClientModule();
}
}

56
extensions/s3nio/pom.xml Normal file
View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
====================================================================
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.html
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">
<parent>
<groupId>org.jclouds</groupId>
<artifactId>project</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../../project/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-s3nio</artifactId>
<name>Connection pooling NIO connection for S3</name>
<packaging>jar</packaging>
<description>Connection pooling NIO connection for S3</description>
<scm>
<connection>scm:svn:http://jclouds.googlecode.com/svn/trunk/extensions/s3nio</connection>
<developerConnection>scm:svn:https://jclouds.googlecode.com/svn/trunk/extensions/s3nio</developerConnection>
<url>http://jclouds.googlecode.com/svn/trunk/extensions/s3nio</url>
</scm>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-httpnio</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,86 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3.nio;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpEntity;
import org.apache.http.nio.entity.NStringEntity;
import org.apache.http.entity.InputStreamEntity;
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandConnectionRetry;
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandExecutionHandler;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ExecutorService;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Singleton
public class S3HttpNioFutureCommandExecutionHandler extends HttpNioFutureCommandExecutionHandler {
@Inject
public S3HttpNioFutureCommandExecutionHandler(java.util.logging.Logger logger, ConsumingNHttpEntityFactory entityFactory, ExecutorService executor, HttpNioFutureCommandConnectionRetry futureOperationRetry) {
super(logger, entityFactory, executor, futureOperationRetry);
}
@Override
protected boolean isRetryable(HttpResponse response) throws IOException {
if (super.isRetryable(response))
return true;
int code = response.getStatusLine().getStatusCode();
if (code == 409) {
return true;
} else if (code == 400) {
if (response.getEntity() != null) {
InputStream input = response.getEntity().getContent();
if (input != null) {
String reason = null;
try {
reason = IOUtils.toString(input);
} finally {
IOUtils.closeQuietly(input);
}
if (reason != null) {
try {
if (reason.indexOf("RequestTime") >= 0) return true;
} finally {
IOUtils.closeQuietly(input);
response.setEntity(new NStringEntity(reason));
}
}
}
}
}
return false;
}
}

View File

@ -0,0 +1,47 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3.nio.config;
import com.google.inject.AbstractModule;
import com.google.inject.Scopes;
import com.google.inject.util.Modules;
import org.apache.http.nio.protocol.NHttpRequestExecutionHandler;
import org.jclouds.aws.s3.nio.S3HttpNioFutureCommandExecutionHandler;
import org.jclouds.http.httpnio.config.HttpNioConnectionPoolClientModule;
/**
* This installs a {@link HttpNioConnectionPoolClientModule}, but overrides it binding {@link S3HttpNioFutureCommandExecutionHandler}.
*
* @author Adrian Cole
*/
public class S3HttpNioConnectionPoolClientModule extends AbstractModule {
protected void configure() {
install(Modules.override(new HttpNioConnectionPoolClientModule()).with(new AbstractModule() {
protected void configure() {
bind(NHttpRequestExecutionHandler.class).to(S3HttpNioFutureCommandExecutionHandler.class);
}
}));
}
}

View File

@ -0,0 +1,141 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3.nio;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.nio.entity.NStringEntity;
import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.*;
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandConnectionRetry;
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandExecutionHandler;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Test
public class S3HttpNioFutureCommandExecutionHandlerTest {
S3HttpNioFutureCommandExecutionHandler handler = null;
HttpResponse response = null;
StatusLine statusline = null;
@BeforeMethod
public void createHandler() {
handler = new S3HttpNioFutureCommandExecutionHandler(createMock(
java.util.logging.Logger.class), createMock(HttpNioFutureCommandExecutionHandler.ConsumingNHttpEntityFactory.class),
createMock(ExecutorService.class),
createMock(HttpNioFutureCommandConnectionRetry.class));
response = createMock(HttpResponse.class);
statusline = createMock(StatusLine.class);
expect(response.getStatusLine()).andReturn(statusline).atLeastOnce();
}
@Test
void test500isRetryable() throws IOException {
isRetryable(500);
}
@Test
void test503isRetryable() throws IOException {
isRetryable(503);
}
@Test
void test409isRetryable() throws IOException {
isRetryable(409);
}
@Test
void test404NotRetryable() throws IOException {
expect(statusline.getStatusCode()).andReturn(404).atLeastOnce();
replay(statusline);
replay(response);
assert !handler.isRetryable(response);
verify(statusline);
verify(response);
}
@Test
void test400WithNoEnitityNotRetryable() throws IOException {
expect(statusline.getStatusCode()).andReturn(400).atLeastOnce();
expect(response.getEntity()).andReturn(null);
replay(statusline);
replay(response);
assert !handler.isRetryable(response);
verify(statusline);
verify(response);
}
@Test
void test400WithIrrelevantEnitityNotRetryable() throws IOException {
expect(statusline.getStatusCode()).andReturn(400).atLeastOnce();
HttpEntity entity = createMock(HttpEntity.class);
expect(response.getEntity()).andReturn(entity).atLeastOnce();
expect(entity.getContent()).andReturn(IOUtils.toInputStream("hello"));
response.setEntity(isA(NStringEntity.class));
replay(entity);
replay(statusline);
replay(response);
assert !handler.isRetryable(response);
verify(statusline);
verify(response);
verify(entity);
}
@Test
void test400WithRequestTimeTooSkewedTimeEnitityRetryable() throws IOException {
expect(statusline.getStatusCode()).andReturn(400).atLeastOnce();
HttpEntity entity = createMock(HttpEntity.class);
expect(response.getEntity()).andReturn(entity).atLeastOnce();
expect(entity.getContent()).andReturn(IOUtils.toInputStream("RequestTimeTooSkewed"));
response.setEntity(isA(NStringEntity.class));
replay(entity);
replay(statusline);
replay(response);
assert handler.isRetryable(response);
verify(statusline);
verify(response);
verify(entity);
}
private void isRetryable(int code) throws IOException {
expect(statusline.getStatusCode()).andReturn(code).atLeastOnce();
replay(statusline);
replay(response);
assert handler.isRetryable(response);
verify(statusline);
verify(response);
}
}

View File

@ -0,0 +1,64 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3.nio.config;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.name.Names;
import org.apache.http.nio.protocol.NHttpRequestExecutionHandler;
import org.jclouds.aws.s3.nio.S3HttpNioFutureCommandExecutionHandler;
import org.testng.annotations.Test;
import java.util.Properties;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Test
public class S3HttpNioConnectionPoolClientModuleTest {
public void testConfigureBindsS3Handler() {
final Properties properties = new Properties();
properties.put("jclouds.http.address", "localhost");
properties.put("jclouds.http.port", "8088");
properties.put("jclouds.http.secure", "false");
properties.setProperty("jclouds.http.pool.max_connection_reuse", "75");
properties.setProperty("jclouds.http.pool.max_session_failures", "2");
properties.setProperty("jclouds.http.pool.request_invoker_threads", "1");
properties.setProperty("jclouds.http.pool.io_worker_threads", "2");
properties.setProperty("jclouds.pool.max_connections", "12");
Injector i = Guice.createInjector(new S3HttpNioConnectionPoolClientModule() {
@Override
protected void configure() {
Names.bindProperties(binder(), properties);
super.configure();
}
});
NHttpRequestExecutionHandler handler = i.getInstance(NHttpRequestExecutionHandler.class);
assert handler instanceof S3HttpNioFutureCommandExecutionHandler;
}
}

71
pom.xml Normal file
View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
====================================================================
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.html
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">
<parent>
<artifactId>project</artifactId>
<groupId>org.jclouds</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>project/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>default</artifactId>
<packaging>pom</packaging>
<name>JClouds</name>
<modules>
<module>project</module>
<module>core</module>
<module>extensions/httpnio</module>
<module>extensions/s3nio</module>
<module>s3</module>
<module>s3/perftest</module>
<module>samples/googleappengine</module>
</modules>
<build>
<plugins>
<plugin>
<groupId>com.google.code.maven-license-plugin</groupId>
<artifactId>maven-license-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<header>project/src/etc/header.txt</header>
<aggregate>true</aggregate>
<excludes>
<!-- amazon files have their own license -->
<exclude>**/src/main/java/com/amazon/**</exclude>
<exclude>**/S3Driver.java</exclude>
</excludes>
<properties>
<year>${project.inceptionYear}</year>
<email>adriancole@jclouds.org</email>
</properties>
</configuration>
</plugin>
</plugins>
</build>
</project>

199
project/LICENSE.txt Normal file
View File

@ -0,0 +1,199 @@
====
Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
====================================================================
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.
====================================================================
====
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

303
project/pom.xml Normal file
View File

@ -0,0 +1,303 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
====================================================================
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.html
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>
<groupId>org.jclouds</groupId>
<artifactId>project</artifactId>
<packaging>pom</packaging>
<name>jclouds</name>
<url>http://code.google.com/p/jclouds</url>
<description>Concurrent API for Amazon Web Services</description>
<inceptionYear>2009</inceptionYear>
<version>1.0-SNAPSHOT</version>
<licenses>
<license>
<name>Apache License</name>
<url>LICENSE.txt</url>
</license>
</licenses>
<organization>
<name>jclouds</name>
<url>http://www.jclouds.org/</url>
</organization>
<issueManagement>
<system>Google Code</system>
<url>http://code.google.com/p/jclouds/issues</url>
</issueManagement>
<mailingLists>
<mailingList>
<name>Contributors</name>
<subscribe>
http://groups.google.com/group/jclouds-contributors
</subscribe>
<unsubscribe>
http://groups.google.com/group/jclouds-contributors
</unsubscribe>
<post>jclouds-contributors@googlegroups.com</post>
<archive>
http://groups.google.com/group/jclouds-contributors
</archive>
</mailingList>
<mailingList>
<name>SVN Commits</name>
<subscribe>
http://groups.google.com/group/jclouds-commits
</subscribe>
<unsubscribe>
http://groups.google.com/group/jclouds-commits
</unsubscribe>
<post>jclouds-commits@googlegroups.com</post>
<archive>http://groups.google.com/group/jclouds-commits</archive>
</mailingList>
</mailingLists>
<scm>
<connection>scm:svn:http://jclouds.googlecode.com/svn/trunk/project</connection>
<developerConnection>scm:svn:https://jclouds.googlecode.com/svn/trunk/project</developerConnection>
<url>http://jclouds.googlecode.com/svn/trunk/project</url>
</scm>
<repositories>
<repository>
<id>jclouds</id>
<url>http://jclouds.googlecode.com/svn/trunk/repo</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>mc-release</id>
<name>Alternate Maven repository of releases</name>
<url>dav:https://mc-repo.googlecode.com/svn/maven2/releases</url>
</repository>
<snapshotRepository>
<id>mc-snapshot</id>
<name>Alternate Maven repository of snapshots</name>
<url>dav:https://mc-repo.googlecode.com/svn/maven2/snapshots</url>
<uniqueVersion>false</uniqueVersion>
</snapshotRepository>
<site>
<id>website</id>
<name>website</name>
<url>file://${basedir}/target/dist/site/jclouds-testing/</url>
</site>
</distributionManagement>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://repo1.maven.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<developers>
<developer>
<name>Adrian Cole</name>
<id>ferncam1</id>
<email>adriancole -at- jclouds.org</email>
<organization>jclouds</organization>
<roles>
<role>Java Developer</role>
<role>PMC</role>
</roles>
<url>http://www.jclouds.org</url>
<timezone>+0</timezone>
</developer>
</developers>
<properties>
<parent-basedir>${basedir}</parent-basedir>
<maven.compile.source>1.5</maven.compile.source>
<maven.compile.target>1.5</maven.compile.target>
<maven.compile.optimize>true</maven.compile.optimize>
<maven.compile.deprecation>true</maven.compile.deprecation>
</properties>
<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>5.8</version>
<scope>test</scope>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymockclassextension</artifactId>
<version>2.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<id>enforce-banned-dependencies</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<excludes>
<exclude>:maven-depedency-plugin</exclude>
</excludes>
</bannedDependencies>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compile.source}</source>
<target>${maven.compile.target}</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifestEntries>
<Specification-Title>jclouds ${pom.name}</Specification-Title>
<Specification-Vendor>jclouds</Specification-Vendor>
<Implementation-Vendor>jclouds</Implementation-Vendor>
<Implementation-Vendor-Id>org.jclouds.aws.s3</Implementation-Vendor-Id>
<Implementation-Version>${pom.version}</Implementation-Version>
<X-Compile-Source-JDK>${maven.compile.source}</X-Compile-Source-JDK>
<X-Compile-Target-JDK>${maven.compile.target}</X-Compile-Target-JDK>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
</plugin>
<!-- enforce java 1.5 and maven 2.1.0 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<id>enforce-java</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<version>[1.5,)</version>
</requireJavaVersion>
<requireMavenVersion>
<version>[2.1.0,)</version>
</requireMavenVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<reportSets>
<reportSet>
<reports>
<report>cpd</report>
<report>findbugs</report>
<report>issue-tracking</report>
<report>mail-lists</report>
<report>pmd</report>
<report>rat-report</report>
<report>source-repository</report>
<report>surefire-report</report>
<report>maven-emma-plugin</report>
<report>team-list</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>emma-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>surefire-report-maven-plugin</artifactId>
<inherited>true</inherited>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rat-maven-plugin</artifactId>
<inherited>true</inherited>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<configuration>
<targetJdk>${maven.compile.source}</targetJdk>
</configuration>
<inherited>true</inherited>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
<xmlOutput>true</xmlOutput>
</configuration>
<inherited>true</inherited>
</plugin>
</plugins>
</reporting>
</project>

View File

@ -0,0 +1,21 @@
Copyright (C) ${year} Adrian Cole <adriancole@jclouds.org>
====================================================================
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.
====================================================================

97
s3/perftest/pom.xml Normal file
View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
====================================================================
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.html
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.
====================================================================
-->
<!--
Note that the code that these performance tests are evaluated against exist in src/main. The code in that
location was copied straight from Amazon's website. There have been small modifications to make unit testing
possible. That code exists with the following license:
This software code is made available "AS IS" without warranties of any
kind. You may copy, display, modify and redistribute the software
code either by itself or as incorporated into your code; provided that
you do not remove any proprietary notices. Your use of this software
code is at your own risk and you waive any claim against Amazon
Digital Services, Inc. or its affiliates with respect to your use of
this software code. (c) 2006 Amazon Digital Services, Inc. or its
affiliates.
-->
<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">
<parent>
<artifactId>project</artifactId>
<groupId>org.jclouds</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../../project/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>s3core-perftest</artifactId>
<name>Performance test verses Amazon samples implementation</name>
<packaging>jar</packaging>
<description>Performance test verses Amazon samples implementation</description>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-s3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-s3</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-s3nio</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>net.java.dev.jets3t</groupId>
<artifactId>jets3t</artifactId>
<version>0.7.0</version>
<optional>true</optional>
</dependency>
</dependencies>
<scm>
<connection>scm:svn:http://jclouds.googlecode.com/svn/trunk/s3core/perftest</connection>
<developerConnection>scm:svn:https://jclouds.googlecode.com/svn/trunk/s3core/perftest</developerConnection>
<url>http://jclouds.googlecode.com/svn/trunk/s3core/perftest</url>
</scm>
<repositories>
<!-- For Amazon S3 artifacts -->
<repository>
<name>jets3t</name>
<id>jets3t</id>
<url>http://jets3t.s3.amazonaws.com/maven2</url>
</repository>
</repositories>
</project>

View File

@ -0,0 +1,159 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006-2007 Amazon Digital Services, Inc. or its
// affiliates.
import java.util.Map;
import java.util.TreeMap;
import java.util.Arrays;
import com.amazon.s3.AWSAuthConnection;
import com.amazon.s3.CallingFormat;
import com.amazon.s3.QueryStringAuthGenerator;
import com.amazon.s3.S3Object;
public class S3Driver {
static final String awsAccessKeyId = "<INSERT YOUR AWS ACCESS KEY ID HERE>";
static final String awsSecretAccessKey = "<INSERT YOUR AWS SECRET ACCESS KEY HERE>";
// convert the bucket to lowercase for vanity domains
// the bucket name must be lowercase since DNS is case-insensitive
static final String bucketName = awsAccessKeyId.toLowerCase() + "-test-bucket";
static final String keyName = "test-key";
static final String copiedKeyName = "copy-of-" + keyName;
public static void main(String args[]) throws Exception {
if (awsAccessKeyId.startsWith("<INSERT")) {
System.err.println("Please examine S3Driver.java and update it with your credentials");
System.exit(-1);
}
AWSAuthConnection conn =
new AWSAuthConnection(awsAccessKeyId, awsSecretAccessKey);
QueryStringAuthGenerator generator =
new QueryStringAuthGenerator(awsAccessKeyId, awsSecretAccessKey);
// Check if the bucket exists. The high availability engineering of
// Amazon S3 is focused on get, put, list, and delete operations.
// Because bucket operations work against a centralized, global
// resource space, it is not appropriate to make bucket create or
// delete calls on the high availability code path of your application.
// It is better to create or delete buckets in a separate initialization
// or setup routine that you run less often.
if (!conn.checkBucketExists(bucketName))
{
System.out.println("----- creating bucket -----");
System.out.println(conn.createBucket(bucketName, AWSAuthConnection.LOCATION_DEFAULT, null).connection.getResponseMessage());
// sample creating an EU located bucket.
// (note path-style urls will not work with location-constrained buckets)
//System.out.println(conn.createBucket(bucketName, AWSAuthConnection.LOCATION_EU, null).connection.getResponseMessage());
}
System.out.println("----- listing bucket -----");
System.out.println(conn.listBucket(bucketName, null, null, null, null).entries);
System.out.println("----- bucket location -----");
System.out.println(conn.getBucketLocation(bucketName).getLocation());
System.out.println("----- putting object -----");
S3Object object = new S3Object("this is a test".getBytes(), null);
Map headers = new TreeMap();
headers.put("Content-Type", Arrays.asList(new String[] { "text/plain" }));
System.out.println(
conn.put(bucketName, keyName, object, headers).connection.getResponseMessage()
);
System.out.println("----- copying object -----");
// Straight Copy; destination key will be private.
conn.copy( bucketName, keyName, bucketName, copiedKeyName, null );
{
// Update the metadata; destination key will be private.
Map updateMetadata = new TreeMap();
updateMetadata.put("metadata-key", Arrays.asList("this will be the metadata in the copied key"));
conn.copy( bucketName, copiedKeyName, bucketName, copiedKeyName, updateMetadata, null );
}
System.out.println("----- listing bucket -----");
System.out.println(conn.listBucket(bucketName, null, null, null, null).entries);
System.out.println("----- getting object -----");
System.out.println(
new String(conn.get(bucketName, keyName, null).object.data)
);
System.out.println("----- query string auth example -----");
generator.setExpiresIn(60 * 1000);
System.out.println("Try this url in your web browser (it will only work for 60 seconds)\n");
System.out.println(generator.get(bucketName, keyName, null));
System.out.print("\npress enter> ");
System.in.read();
System.out.println("\nNow try just the url without the query string arguments. It should fail.\n");
System.out.println(generator.makeBareURL(bucketName, keyName));
System.out.print("\npress enter> ");
System.in.read();
System.out.println("----- putting object with metadata and public read acl -----");
Map metadata = new TreeMap();
metadata.put("blah", Arrays.asList(new String[] { "foo" }));
object = new S3Object("this is a publicly readable test".getBytes(), metadata);
headers = new TreeMap();
headers.put("x-amz-acl", Arrays.asList(new String[] { "public-read" }));
headers.put("Content-Type", Arrays.asList(new String[] { "text/plain" }));
System.out.println(
conn.put(bucketName, keyName + "-public", object, headers).connection.getResponseMessage()
);
System.out.println("----- anonymous read test -----");
System.out.println("\nYou should be able to try this in your browser\n");
System.out.println(generator.makeBareURL(bucketName, keyName + "-public"));
System.out.print("\npress enter> ");
System.in.read();
System.out.println("----- path style url example -----");
System.out.println("\nNon-location-constrained buckets can also be specified as part of the url path. (This was the original url style supported by S3.)");
System.out.println("\nTry this url out in your browser (it will only be valid for 60 seconds)\n");
generator.setCallingFormat(CallingFormat.getPathCallingFormat());
// could also have been done like this:
// generator = new QueryStringAuthGenerator(awsAccessKeyId, awsSecretAccessKey, true, Utils.DEFAULT_HOST, CallingFormat.getPathCallingFormat());
generator.setExpiresIn(60 * 1000);
System.out.println(generator.get(bucketName, keyName, null));
System.out.print("\npress enter> ");
System.in.read();
System.out.println("----- getting object's acl -----");
System.out.println(new String(conn.getACL(bucketName, keyName, null).object.data));
System.out.println("----- deleting objects -----");
System.out.println(
conn.delete(bucketName, copiedKeyName, null).connection.getResponseMessage()
);
System.out.println(
conn.delete(bucketName, keyName, null).connection.getResponseMessage()
);
System.out.println(
conn.delete(bucketName, keyName + "-public", null).connection.getResponseMessage()
);
System.out.println("----- listing bucket -----");
System.out.println(conn.listBucket(bucketName, null, null, null, null).entries);
System.out.println("----- listing all my buckets -----");
System.out.println(conn.listAllMyBuckets(null).entries);
System.out.println("----- deleting bucket -----");
System.out.println(
conn.deleteBucket(bucketName, null).connection.getResponseMessage()
);
}
}

View File

@ -0,0 +1,633 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.
* ====================================================================
*/
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006-2007 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
/**
* An interface into the S3 system. It is initially configured with
* authentication and connection parameters and exposes methods to access and
* manipulate S3 data.
*/
public class AWSAuthConnection {
public static final String LOCATION_DEFAULT = null;
public static final String LOCATION_EU = "EU";
private String awsAccessKeyId;
private String awsSecretAccessKey;
private boolean isSecure;
private String server;
private int port;
private CallingFormat callingFormat;
public AWSAuthConnection(String awsAccessKeyId, String awsSecretAccessKey) {
this(awsAccessKeyId, awsSecretAccessKey, true);
}
public AWSAuthConnection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure) {
this(awsAccessKeyId, awsSecretAccessKey, isSecure, Utils.DEFAULT_HOST);
}
public AWSAuthConnection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure,
String server)
{
this(awsAccessKeyId, awsSecretAccessKey, isSecure, server,
isSecure ? Utils.SECURE_PORT : Utils.INSECURE_PORT);
}
public AWSAuthConnection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure,
String server, int port) {
this(awsAccessKeyId, awsSecretAccessKey, isSecure, server, port, CallingFormat.getSubdomainCallingFormat());
}
public AWSAuthConnection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure,
String server, CallingFormat format) {
this(awsAccessKeyId, awsSecretAccessKey, isSecure, server,
isSecure ? Utils.SECURE_PORT : Utils.INSECURE_PORT,
format);
}
/**
* Create a new interface to interact with S3 with the given credential and connection
* parameters
*
* @param awsAccessKeyId Your user key into AWS
* @param awsSecretAccessKey The secret string used to generate signatures for authentication.
* @param isSecure use SSL encryption
* @param server Which host to connect to. Usually, this will be s3.amazonaws.com
* @param port Which port to use.
* @param callingFormat Type of request Regular/Vanity or Pure Vanity domain
*/
public AWSAuthConnection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure,
String server, int port, CallingFormat format)
{
this.awsAccessKeyId = awsAccessKeyId;
this.awsSecretAccessKey = awsSecretAccessKey;
this.isSecure = isSecure;
this.server = server;
this.port = port;
this.callingFormat = format;
}
/**
* Creates a new bucket.
* @param bucket The name of the bucket to create.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
* @param metadata A Map of String to List of Strings representing the s3
* metadata for this bucket (can be null).
* @deprecated use version that specifies location
*/
public Response createBucket(String bucket, Map headers)
throws MalformedURLException, IOException
{
return createBucket(bucket, null, headers);
}
/**
* Creates a new bucket.
* @param bucket The name of the bucket to create.
* @param location Desired location ("EU") (or null for default).
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
* @param metadata A Map of String to List of Strings representing the s3
* metadata for this bucket (can be null).
* @throws IllegalArgumentException on invalid location
*/
public Response createBucket(String bucket, String location, Map headers)
throws MalformedURLException, IOException
{
String body;
if (location == null) {
body = null;
} else if (LOCATION_EU.equals(location)) {
if (!callingFormat.supportsLocatedBuckets())
throw new IllegalArgumentException("Creating location-constrained bucket with unsupported calling-format");
body = "<CreateBucketConstraint><LocationConstraint>" + location + "</LocationConstraint></CreateBucketConstraint>";
} else
throw new IllegalArgumentException("Invalid Location: "+location);
// validate bucket name
if (!Utils.validateBucketName(bucket, callingFormat, location != null))
throw new IllegalArgumentException("Invalid S3Bucket Name: "+bucket);
HttpURLConnection request = makeRequest("PUT", bucket, "", null, headers);
if (body != null)
{
request.setDoOutput(true);
request.getOutputStream().write(body.getBytes("UTF-8"));
}
return new Response(request);
}
/**
* Check if the specified bucket exists (via a HEAD request)
* @param bucket The name of the bucket to check
* @return true if HEAD access returned success
*/
public boolean checkBucketExists(String bucket) throws MalformedURLException, IOException
{
HttpURLConnection response = makeRequest("HEAD", bucket, "", null, null);
int httpCode = response.getResponseCode();
return httpCode >= 200 && httpCode < 300;
}
/**
* Lists the contents of a bucket.
* @param bucket The name of the bucket to create.
* @param prefix All returned keys will start with this string (can be null).
* @param marker All returned keys will be lexographically greater than
* this string (can be null).
* @param maxKeys The maximum number of keys to return (can be null).
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public ListBucketResponse listBucket(String bucket, String prefix, String marker,
Integer maxKeys, Map headers)
throws MalformedURLException, IOException
{
return listBucket(bucket, prefix, marker, maxKeys, null, headers);
}
/**
* Lists the contents of a bucket.
* @param bucket The name of the bucket to list.
* @param prefix All returned keys will start with this string (can be null).
* @param marker All returned keys will be lexographically greater than
* this string (can be null).
* @param maxKeys The maximum number of keys to return (can be null).
* @param delimiter Keys that contain a string between the prefix and the first
* occurrence of the delimiter will be rolled up into a single element.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public ListBucketResponse listBucket(String bucket, String prefix, String marker,
Integer maxKeys, String delimiter, Map headers)
throws MalformedURLException, IOException
{
Map pathArgs = Utils.paramsForListOptions(prefix, marker, maxKeys, delimiter);
return new ListBucketResponse(makeRequest("GET", bucket, "", pathArgs, headers));
}
/**
* Deletes a bucket.
* @param bucket The name of the bucket to delete.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public Response deleteBucket(String bucket, Map headers)
throws MalformedURLException, IOException
{
return new Response(makeRequest("DELETE", bucket, "", null, headers));
}
/**
* Writes an object to S3.
* @param bucket The name of the bucket to which the object will be added.
* @param key The name of the key to use.
* @param object An S3Object containing the data to write.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public Response put(String bucket, String key, S3Object object, Map headers)
throws MalformedURLException, IOException
{
HttpURLConnection request =
makeRequest("PUT", bucket, Utils.urlencode(key), null, headers, object);
request.setDoOutput(true);
request.getOutputStream().write(object.data == null ? new byte[] {} : object.data);
return new Response(request);
}
/**
* Creates a copy of an existing S3 Object. In this signature, we will copy the
* existing metadata. The default access control policy is private; if you want
* to override it, please use x-amz-acl in the headers.
* @param sourceBucket The name of the bucket where the source object lives.
* @param sourceKey The name of the key to copy.
* @param destinationBucket The name of the bucket to which the object will be added.
* @param destinationKey The name of the key to use.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null). You may wish to set the x-amz-acl header appropriately.
*/
public Response copy( String sourceBucket, String sourceKey, String destinationBucket, String destinationKey, Map headers )
throws MalformedURLException, IOException
{
S3Object object = new S3Object(new byte[] {}, new HashMap());
headers = headers == null ? new HashMap() : new HashMap(headers);
headers.put("x-amz-copy-source", Arrays.asList( new String[] { sourceBucket + "/" + sourceKey } ) );
headers.put("x-amz-metadata-directive", Arrays.asList( new String[] { "COPY" } ) );
return verifyCopy( put( destinationBucket, destinationKey, object, headers ) );
}
/**
* Creates a copy of an existing S3 Object. In this signature, we will replace the
* existing metadata. The default access control policy is private; if you want
* to override it, please use x-amz-acl in the headers.
* @param sourceBucket The name of the bucket where the source object lives.
* @param sourceKey The name of the key to copy.
* @param destinationBucket The name of the bucket to which the object will be added.
* @param destinationKey The name of the key to use.
* @param metadata A Map of String to List of Strings representing the S3 metadata
* for the new object.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null). You may wish to set the x-amz-acl header appropriately.
*/
public Response copy( String sourceBucket, String sourceKey, String destinationBucket, String destinationKey, Map metadata, Map headers )
throws MalformedURLException, IOException
{
S3Object object = new S3Object(new byte[] {}, metadata);
headers = headers == null ? new HashMap() : new HashMap(headers);
headers.put("x-amz-copy-source", Arrays.asList( new String[] { sourceBucket + "/" + sourceKey } ) );
headers.put("x-amz-metadata-directive", Arrays.asList( new String[] { "REPLACE" } ) );
return verifyCopy( put( destinationBucket, destinationKey, object, headers ) );
}
/**
* Copy sometimes returns a successful response and starts to send whitespace
* characters to us. This method processes those whitespace characters and
* will throw an exception if the response is either unknown or an error.
* @param response Response object from the PUT request.
* @return The response with the input stream drained.
* @throws IOException If anything goes wrong.
*/
private Response verifyCopy( Response response ) throws IOException {
if (response.connection.getResponseCode() < 400) {
byte[] body = GetResponse.slurpInputStream(response.connection.getInputStream());
String message = new String( body );
if ( message.indexOf( "<Error" ) != -1 ) {
throw new IOException( message.substring( message.indexOf( "<Error" ) ) );
} else if ( message.indexOf( "</CopyObjectResult>" ) != -1 ) {
// It worked!
} else {
throw new IOException( "Unexpected response: " + message );
}
}
return response;
}
/**
* Reads an object from S3.
* @param bucket The name of the bucket where the object lives.
* @param key The name of the key to use.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public GetResponse get(String bucket, String key, Map headers)
throws MalformedURLException, IOException
{
return new GetResponse(makeRequest("GET", bucket, Utils.urlencode(key), null, headers));
}
/**
* Deletes an object from S3.
* @param bucket The name of the bucket where the object lives.
* @param key The name of the key to use.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public Response delete(String bucket, String key, Map headers)
throws MalformedURLException, IOException
{
return new Response(makeRequest("DELETE", bucket, Utils.urlencode(key), null, headers));
}
/**
* Get the requestPayment xml document for a given bucket
* @param bucket The name of the bucket
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public GetResponse getBucketRequestPayment(String bucket, Map headers)
throws MalformedURLException, IOException
{
Map pathArgs = new HashMap();
pathArgs.put("requestPayment", null);
return new GetResponse(makeRequest("GET", bucket, "", pathArgs, headers));
}
/**
* Write a new requestPayment xml document for a given bucket
* @param loggingXMLDoc The xml representation of the requestPayment configuration as a String
* @param bucket The name of the bucket
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public Response putBucketRequestPayment(String bucket, String requestPaymentXMLDoc, Map headers)
throws MalformedURLException, IOException
{
Map pathArgs = new HashMap();
pathArgs.put("requestPayment", null);
S3Object object = new S3Object(requestPaymentXMLDoc.getBytes(), null);
HttpURLConnection request = makeRequest("PUT", bucket, "", pathArgs, headers, object);
request.setDoOutput(true);
request.getOutputStream().write(object.data == null ? new byte[] {} : object.data);
return new Response(request);
}
/**
* Get the logging xml document for a given bucket
* @param bucket The name of the bucket
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public GetResponse getBucketLogging(String bucket, Map headers)
throws MalformedURLException, IOException
{
Map pathArgs = new HashMap();
pathArgs.put("logging", null);
return new GetResponse(makeRequest("GET", bucket, "", pathArgs, headers));
}
/**
* Write a new logging xml document for a given bucket
* @param loggingXMLDoc The xml representation of the logging configuration as a String
* @param bucket The name of the bucket
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public Response putBucketLogging(String bucket, String loggingXMLDoc, Map headers)
throws MalformedURLException, IOException
{
Map pathArgs = new HashMap();
pathArgs.put("logging", null);
S3Object object = new S3Object(loggingXMLDoc.getBytes(), null);
HttpURLConnection request = makeRequest("PUT", bucket, "", pathArgs, headers, object);
request.setDoOutput(true);
request.getOutputStream().write(object.data == null ? new byte[] {} : object.data);
return new Response(request);
}
/**
* Get the ACL for a given bucket
* @param bucket The name of the bucket where the object lives.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public GetResponse getBucketACL(String bucket, Map headers)
throws MalformedURLException, IOException
{
return getACL(bucket, "", headers);
}
/**
* Get the ACL for a given object (or bucket, if key is null).
* @param bucket The name of the bucket where the object lives.
* @param key The name of the key to use.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public GetResponse getACL(String bucket, String key, Map headers)
throws MalformedURLException, IOException
{
if (key == null) key = "";
Map pathArgs = new HashMap();
pathArgs.put("acl", null);
return new GetResponse(
makeRequest("GET", bucket, Utils.urlencode(key), pathArgs, headers)
);
}
/**
* Write a new ACL for a given bucket
* @param aclXMLDoc The xml representation of the ACL as a String
* @param bucket The name of the bucket where the object lives.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public Response putBucketACL(String bucket, String aclXMLDoc, Map headers)
throws MalformedURLException, IOException
{
return putACL(bucket, "", aclXMLDoc, headers);
}
/**
* Write a new ACL for a given object
* @param aclXMLDoc The xml representation of the ACL as a String
* @param bucket The name of the bucket where the object lives.
* @param key The name of the key to use.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public Response putACL(String bucket, String key, String aclXMLDoc, Map headers)
throws MalformedURLException, IOException
{
S3Object object = new S3Object(aclXMLDoc.getBytes(), null);
Map pathArgs = new HashMap();
pathArgs.put("acl", null);
HttpURLConnection request =
makeRequest("PUT", bucket, Utils.urlencode(key), pathArgs, headers, object);
request.setDoOutput(true);
request.getOutputStream().write(object.data == null ? new byte[] {} : object.data);
return new Response(request);
}
public LocationResponse getBucketLocation(String bucket)
throws MalformedURLException, IOException
{
Map pathArgs = new HashMap();
pathArgs.put("location", null);
return new LocationResponse(makeRequest("GET", bucket, "", pathArgs, null));
}
/**
* List all the buckets created by this account.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
public ListAllMyBucketsResponse listAllMyBuckets(Map headers)
throws MalformedURLException, IOException
{
return new ListAllMyBucketsResponse(makeRequest("GET", "", "", null, headers));
}
/**
* Make a new HttpURLConnection without passing an S3Object parameter.
* Use this method for key operations that do require arguments
* @param method The method to invoke
* @param bucketName the bucket this request is for
* @param key the key this request is for
* @param pathArgs the
* @param headers
* @return
* @throws MalformedURLException
* @throws IOException
*/
private HttpURLConnection makeRequest(String method, String bucketName, String key, Map pathArgs, Map headers)
throws MalformedURLException, IOException
{
return makeRequest(method, bucketName, key, pathArgs, headers, null);
}
/**
* Make a new HttpURLConnection.
* @param method The HTTP method to use (GET, PUT, DELETE)
* @param bucketName The bucket name this request affects
* @param key The key this request is for
* @param pathArgs parameters if any to be sent along this request
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
* @param object The S3Object that is to be written (can be null).
*/
private HttpURLConnection makeRequest(String method, String bucket, String key, Map pathArgs, Map headers,
S3Object object)
throws MalformedURLException, IOException
{
CallingFormat callingFormat = Utils.getCallingFormatForBucket( this.callingFormat, bucket );
if ( isSecure && callingFormat != CallingFormat.getPathCallingFormat() && bucket.indexOf( "." ) != -1 ) {
System.err.println( "You are making an SSL connection, however, the bucket contains periods and the wildcard certificate will not match by default. Please consider using HTTP." );
}
// build the domain based on the calling format
URL url = callingFormat.getURL(isSecure, server, this.port, bucket, key, pathArgs);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod(method);
// subdomain-style urls may encounter http redirects.
// Ensure that redirects are supported.
if (!connection.getInstanceFollowRedirects()
&& callingFormat.supportsLocatedBuckets())
throw new RuntimeException("HTTP redirect support required.");
addHeaders(connection, headers);
if (object != null) addMetadataHeaders(connection, object.metadata);
addAuthHeader(connection, method, bucket, key, pathArgs);
return connection;
}
/**
* Add the given headers to the HttpURLConnection.
* @param connection The HttpURLConnection to which the headers will be added.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
*/
private void addHeaders(HttpURLConnection connection, Map headers) {
addHeaders(connection, headers, "");
}
/**
* Add the given metadata fields to the HttpURLConnection.
* @param connection The HttpURLConnection to which the headers will be added.
* @param metadata A Map of String to List of Strings representing the s3
* metadata for this resource.
*/
private void addMetadataHeaders(HttpURLConnection connection, Map metadata) {
addHeaders(connection, metadata, Utils.METADATA_PREFIX);
}
/**
* Add the given headers to the HttpURLConnection with a prefix before the keys.
* @param connection The HttpURLConnection to which the headers will be added.
* @param headers A Map of String to List of Strings representing the http
* headers to pass (can be null).
* @param prefix The string to prepend to each key before adding it to the connection.
*/
private void addHeaders(HttpURLConnection connection, Map headers, String prefix) {
if (headers != null) {
for (Iterator i = headers.keySet().iterator(); i.hasNext(); ) {
String key = (String)i.next();
for (Iterator j = ((List)headers.get(key)).iterator(); j.hasNext(); ) {
String value = (String)j.next();
connection.addRequestProperty(prefix + key, value);
}
}
}
}
/**
* Add the appropriate Authorization header to the HttpURLConnection.
* @param connection The HttpURLConnection to which the header will be added.
* @param method The HTTP method to use (GET, PUT, DELETE)
* @param bucket the bucket name this request is for
* @param key the key this request is for
* @param pathArgs path arguments which are part of this request
*/
private void addAuthHeader(HttpURLConnection connection, String method, String bucket, String key, Map pathArgs) {
if (connection.getRequestProperty("Date") == null) {
connection.setRequestProperty("Date", httpDate());
}
if (connection.getRequestProperty("Content-Type") == null) {
connection.setRequestProperty("Content-Type", "");
}
String canonicalString =
Utils.makeCanonicalString(method, bucket, key, pathArgs, connection.getRequestProperties());
String encodedCanonical = Utils.encode(this.awsSecretAccessKey, canonicalString, false);
connection.setRequestProperty("Authorization",
"AWS " + this.awsAccessKeyId + ":" + encodedCanonical);
}
/**
* Generate an rfc822 date for use in the Date HTTP header.
*/
public static String httpDate() {
final String DateFormat = "EEE, dd MMM yyyy HH:mm:ss ";
SimpleDateFormat format = new SimpleDateFormat( DateFormat, Locale.US );
format.setTimeZone( TimeZone.getTimeZone( "GMT" ) );
return format.format( new Date() ) + "GMT";
}
}

View File

@ -0,0 +1,41 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
import java.util.Date;
/**
* A class representing a single bucket. Returned as a component of ListAllMyBucketsResponse.
*/
public class Bucket {
/**
* The name of the bucket.
*/
public String name;
/**
* The bucket's creation date.
*/
public Date creationDate;
public Bucket() {
this.name = null;
this.creationDate = null;
}
public Bucket(String name, Date creationDate) {
this.name = name;
this.creationDate = creationDate;
}
public String toString() {
return this.name;
}
}

View File

@ -0,0 +1,103 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006-2007 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
public abstract class CallingFormat {
protected static CallingFormat pathCallingFormat = new PathCallingFormat();
protected static CallingFormat subdomainCallingFormat = new SubdomainCallingFormat();
protected static CallingFormat vanityCallingFormat = new VanityCallingFormat();
public abstract boolean supportsLocatedBuckets();
public abstract String getEndpoint(String server, int port, String bucket);
public abstract String getPathBase(String bucket, String key);
public abstract URL getURL (boolean isSecure, String server, int port, String bucket, String key, Map pathArgs)
throws MalformedURLException;
public static CallingFormat getPathCallingFormat() {
return pathCallingFormat;
}
public static CallingFormat getSubdomainCallingFormat() {
return subdomainCallingFormat;
}
public static CallingFormat getVanityCallingFormat() {
return vanityCallingFormat;
}
static private class PathCallingFormat extends CallingFormat {
public boolean supportsLocatedBuckets() {
return false;
}
public String getPathBase(String bucket, String key) {
return isBucketSpecified(bucket) ? "/" + bucket + "/" + key : "/";
}
public String getEndpoint(String server, int port, String bucket) {
return server + ":" + port;
}
public URL getURL(boolean isSecure, String server, int port, String bucket, String key, Map pathArgs)
throws MalformedURLException {
String pathBase = isBucketSpecified(bucket) ? "/" + bucket + "/" + key : "/";
String pathArguments = Utils.convertPathArgsHashToString(pathArgs);
return new URL(isSecure ? "https": "http", server, port, pathBase + pathArguments);
}
private boolean isBucketSpecified(String bucket) {
if(bucket == null) return false;
if(bucket.length() == 0) return false;
return true;
}
}
static private class SubdomainCallingFormat extends CallingFormat {
public boolean supportsLocatedBuckets() {
return true;
}
public String getServer(String server, String bucket) {
return bucket + "." + server;
}
public String getEndpoint(String server, int port, String bucket) {
return getServer(server, bucket) + ":" + port ;
}
public String getPathBase(String bucket, String key) {
return "/" + key;
}
public URL getURL(boolean isSecure, String server, int port, String bucket, String key, Map pathArgs)
throws MalformedURLException {
if (bucket == null || bucket.length() == 0)
{
//The bucket is null, this is listAllBuckets request
String pathArguments = Utils.convertPathArgsHashToString(pathArgs);
return new URL(isSecure ? "https": "http", server, port, "/" + pathArguments);
} else {
String serverToUse = getServer(server, bucket);
String pathBase = getPathBase(bucket, key);
String pathArguments = Utils.convertPathArgsHashToString(pathArgs);
return new URL(isSecure ? "https": "http", serverToUse, port, pathBase + pathArguments);
}
}
}
static private class VanityCallingFormat extends SubdomainCallingFormat {
public String getServer(String server, String bucket) {
return bucket;
}
}
}

View File

@ -0,0 +1,17 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
public class CommonPrefixEntry {
/**
* The prefix common to the delimited keys it represents
*/
public String prefix;
}

View File

@ -0,0 +1,70 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
import java.net.HttpURLConnection;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
/**
* A Response object returned from AWSAuthConnection.get(). Exposes the attribute object, which
* represents the retrieved object.
*/
public class GetResponse extends Response {
public S3Object object;
/**
* Pulls a representation of an S3Object out of the HttpURLConnection response.
*/
public GetResponse(HttpURLConnection connection) throws IOException {
super(connection);
if (connection.getResponseCode() < 400) {
Map metadata = extractMetadata(connection);
byte[] body = slurpInputStream(connection.getInputStream());
this.object = new S3Object(body, metadata);
}
}
/**
* Examines the response's header fields and returns a Map from String to List of Strings
* representing the object's metadata.
*/
private Map extractMetadata(HttpURLConnection connection) {
TreeMap metadata = new TreeMap();
Map headers = connection.getHeaderFields();
for (Iterator i = headers.keySet().iterator(); i.hasNext(); ) {
String key = (String)i.next();
if (key == null) continue;
if (key.startsWith(Utils.METADATA_PREFIX)) {
metadata.put(key.substring(Utils.METADATA_PREFIX.length()), headers.get(key));
}
}
return metadata;
}
/**
* Read the input stream and dump it all into a big byte array
*/
static byte[] slurpInputStream(InputStream stream) throws IOException {
final int chunkSize = 2048;
byte[] buf = new byte[chunkSize];
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(chunkSize);
int count;
while ((count=stream.read(buf)) != -1) byteStream.write(buf, 0, count);
return byteStream.toByteArray();
}
}

View File

@ -0,0 +1,106 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.SimpleTimeZone;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
/**
* Returned by AWSAuthConnection.listAllMyBuckets().
*/
public class ListAllMyBucketsResponse extends Response {
/**
* A list of Bucket objects, one for each of this account's buckets. Will be null if
* the request fails.
*/
public List entries;
public ListAllMyBucketsResponse(HttpURLConnection connection) throws IOException {
super(connection);
if (connection.getResponseCode() < 400) {
try {
XMLReader xr = Utils.createXMLReader();;
ListAllMyBucketsHandler handler = new ListAllMyBucketsHandler();
xr.setContentHandler(handler);
xr.setErrorHandler(handler);
xr.parse(new InputSource(connection.getInputStream()));
this.entries = handler.getEntries();
} catch (SAXException e) {
throw new RuntimeException("Unexpected error parsing ListAllMyBuckets xml", e);
}
}
}
static class ListAllMyBucketsHandler extends DefaultHandler {
private List entries = null;
private Bucket currBucket = null;
private StringBuffer currText = null;
private SimpleDateFormat iso8601Parser = null;
public ListAllMyBucketsHandler() {
super();
entries = new ArrayList();
this.iso8601Parser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
this.iso8601Parser.setTimeZone(new SimpleTimeZone(0, "GMT"));
this.currText = new StringBuffer();
}
public void startDocument() {
// ignore
}
public void endDocument() {
// ignore
}
public void startElement(String uri, String name, String qName, Attributes attrs) {
if (name.equals("Bucket")) {
this.currBucket = new Bucket();
}
}
public void endElement(String uri, String name, String qName) {
if (name.equals("Bucket")) {
this.entries.add(this.currBucket);
} else if (name.equals("Name")) {
this.currBucket.name = this.currText.toString();
} else if (name.equals("CreationDate")) {
try {
this.currBucket.creationDate = this.iso8601Parser.parse(this.currText.toString());
} catch (ParseException e) {
throw new RuntimeException("Unexpected date format in list bucket output", e);
}
}
this.currText = new StringBuffer();
}
public void characters(char ch[], int start, int length) {
this.currText.append(ch, start, length);
}
public List getEntries() {
return this.entries;
}
}
}

View File

@ -0,0 +1,243 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.SimpleTimeZone;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.SAXException;
/**
* Returned by AWSAuthConnection.listBucket()
*/
public class ListBucketResponse extends Response {
/**
* The name of the bucket being listed. Null if request fails.
*/
public String name = null;
/**
* The prefix echoed back from the request. Null if request fails.
*/
public String prefix = null;
/**
* The marker echoed back from the request. Null if request fails.
*/
public String marker = null;
/**
* The delimiter echoed back from the request. Null if not specified in
* the request, or if it fails.
*/
public String delimiter = null;
/**
* The maxKeys echoed back from the request if specified. 0 if request fails.
*/
public int maxKeys = 0;
/**
* Indicates if there are more results to the list. True if the current
* list results have been truncated. false if request fails.
*/
public boolean isTruncated = false;
/**
* Indicates what to use as a marker for subsequent list requests in the event
* that the results are truncated. Present only when a delimiter is specified.
* Null if request fails.
*/
public String nextMarker = null;
/**
* A List of ListEntry objects representing the objects in the given bucket.
* Null if the request fails.
*/
public List entries = null;
/**
* A List of CommonPrefixEntry objects representing the common prefixes of the
* keys that matched up to the delimiter. Null if the request fails.
*/
public List commonPrefixEntries = null;
public ListBucketResponse(HttpURLConnection connection) throws IOException {
super(connection);
if (connection.getResponseCode() < 400) {
try {
XMLReader xr = Utils.createXMLReader();
ListBucketHandler handler = new ListBucketHandler();
xr.setContentHandler(handler);
xr.setErrorHandler(handler);
xr.parse(new InputSource(connection.getInputStream()));
this.name = handler.getName();
this.prefix = handler.getPrefix();
this.marker = handler.getMarker();
this.delimiter = handler.getDelimiter();
this.maxKeys = handler.getMaxKeys();
this.isTruncated = handler.getIsTruncated();
this.nextMarker = handler.getNextMarker();
this.entries = handler.getKeyEntries();
this.commonPrefixEntries = handler.getCommonPrefixEntries();
} catch (SAXException e) {
throw new RuntimeException("Unexpected error parsing ListBucket xml", e);
}
}
}
class ListBucketHandler extends DefaultHandler {
private String name = null;
private String prefix = null;
private String marker = null;
private String delimiter = null;
private int maxKeys = 0;
private boolean isTruncated = false;
private String nextMarker = null;
private boolean isEchoedPrefix = false;
private List keyEntries = null;
private ListEntry keyEntry = null;
private List commonPrefixEntries = null;
private CommonPrefixEntry commonPrefixEntry = null;
private StringBuffer currText = null;
private SimpleDateFormat iso8601Parser = null;
public ListBucketHandler() {
super();
keyEntries = new ArrayList();
commonPrefixEntries = new ArrayList();
this.iso8601Parser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
this.iso8601Parser.setTimeZone(new SimpleTimeZone(0, "GMT"));
this.currText = new StringBuffer();
}
public void startDocument() {
this.isEchoedPrefix = true;
}
public void endDocument() {
// ignore
}
public void startElement(String uri, String name, String qName, Attributes attrs) {
if (name.equals("Contents")) {
this.keyEntry = new ListEntry();
} else if (name.equals("Owner")) {
this.keyEntry.owner = new Owner();
} else if (name.equals("CommonPrefixes")){
this.commonPrefixEntry = new CommonPrefixEntry();
}
}
public void endElement(String uri, String name, String qName) {
if (name.equals("Name")) {
this.name = this.currText.toString();
}
// this prefix is the one we echo back from the request
else if (name.equals("Prefix") && this.isEchoedPrefix) {
this.prefix = this.currText.toString();
this.isEchoedPrefix = false;
} else if (name.equals("Marker")) {
this.marker = this.currText.toString();
} else if (name.equals("MaxKeys")) {
this.maxKeys = Integer.parseInt(this.currText.toString());
} else if (name.equals("Delimiter")) {
this.delimiter = this.currText.toString();
} else if (name.equals("IsTruncated")) {
this.isTruncated = Boolean.valueOf(this.currText.toString());
} else if (name.equals("NextMarker")) {
this.nextMarker = this.currText.toString();
} else if (name.equals("Contents")) {
this.keyEntries.add(this.keyEntry);
} else if (name.equals("Key")) {
this.keyEntry.key = this.currText.toString();
} else if (name.equals("LastModified")) {
try {
this.keyEntry.lastModified = this.iso8601Parser.parse(this.currText.toString());
} catch (ParseException e) {
throw new RuntimeException("Unexpected date format in list bucket output", e);
}
} else if (name.equals("ETag")) {
this.keyEntry.eTag = this.currText.toString();
} else if (name.equals("Size")) {
this.keyEntry.size = Long.parseLong(this.currText.toString());
} else if (name.equals("StorageClass")) {
this.keyEntry.storageClass = this.currText.toString();
} else if (name.equals("ID")) {
this.keyEntry.owner.id = this.currText.toString();
} else if (name.equals("DisplayName")) {
this.keyEntry.owner.displayName = this.currText.toString();
} else if (name.equals("CommonPrefixes")) {
this.commonPrefixEntries.add(this.commonPrefixEntry);
}
// this is the common prefix for keys that match up to the delimiter
else if (name.equals("Prefix")) {
this.commonPrefixEntry.prefix = this.currText.toString();
}
if(this.currText.length() != 0)
this.currText = new StringBuffer();
}
public void characters(char ch[], int start, int length) {
this.currText.append(ch, start, length);
}
public String getName() {
return this.name;
}
public String getPrefix() {
return this.prefix;
}
public String getMarker() {
return this.marker;
}
public String getDelimiter() {
return this.delimiter;
}
public int getMaxKeys(){
return this.maxKeys;
}
public boolean getIsTruncated() {
return this.isTruncated;
}
public String getNextMarker() {
return this.nextMarker;
}
public List getKeyEntries() {
return this.keyEntries;
}
public List getCommonPrefixEntries() {
return this.commonPrefixEntries;
}
}
}

View File

@ -0,0 +1,51 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
import java.util.Date;
/**
* A structure representing a single object stored in S3. Returned as a part of ListBucketResponse.
*/
public class ListEntry {
/**
* The name of the object
*/
public String key;
/**
* The date at which the object was last modified.
*/
public Date lastModified;
/**
* The object's ETag, which can be used for conditional GETs.
*/
public String eTag;
/**
* The size of the object in bytes.
*/
public long size;
/**
* The object's storage class
*/
public String storageClass;
/**
* The object's owner
*/
public Owner owner;
public String toString() {
return key;
}
}

View File

@ -0,0 +1,89 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006-2007 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
import java.io.IOException;
import java.net.HttpURLConnection;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
/**
* A Response object returned from AWSAuthConnection.getBucketLocation().
* Parses the response XML and exposes the location constraint
* via the geteLocation() method.
*/
public class LocationResponse extends Response {
String location;
/**
* Parse the response to a ?location query.
*/
public LocationResponse(HttpURLConnection connection) throws IOException {
super(connection);
if (connection.getResponseCode() < 400) {
try {
XMLReader xr = Utils.createXMLReader();;
LocationResponseHandler handler = new LocationResponseHandler();
xr.setContentHandler(handler);
xr.setErrorHandler(handler);
xr.parse(new InputSource(connection.getInputStream()));
this.location = handler.location;
} catch (SAXException e) {
throw new RuntimeException("Unexpected error parsing ListAllMyBuckets xml", e);
}
} else {
this.location = "<error>";
}
}
/**
* Report the location-constraint for a bucket.
* A value of null indicates an error;
* the empty string indicates no constraint;
* and any other value is an actual location constraint value.
*/
public String getLocation() {
return location;
}
/**
* Helper class to parse LocationConstraint response XML
*/
static class LocationResponseHandler extends DefaultHandler {
String location = null;
private StringBuffer currText = null;
public void startDocument() {
}
public void startElement(String uri, String name, String qName, Attributes attrs) {
if (name.equals("LocationConstraint")) {
this.currText = new StringBuffer();
}
}
public void endElement(String uri, String name, String qName) {
if (name.equals("LocationConstraint")) {
location = this.currText.toString();
this.currText = null;
}
}
public void characters(char ch[], int start, int length) {
if (currText != null)
this.currText.append(ch, start, length);
}
}
}

View File

@ -0,0 +1,18 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
/**
* A structure representing the owner of an object, used as a part of ListEntry.
*/
public class Owner {
public String id;
public String displayName;
}

View File

@ -0,0 +1,264 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006-2007 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* This class mimics the behavior of AWSAuthConnection, except instead of actually performing
* the operation, QueryStringAuthGenerator will return URLs with query string parameters that
* can be used to do the same thing. These parameters include an expiration date, so that
* if you hand them off to someone else, they will only work for a limited amount of time.
*/
public class QueryStringAuthGenerator {
private String awsAccessKeyId;
private String awsSecretAccessKey;
private boolean isSecure;
private String server;
private int port;
private CallingFormat callingFormat;
private Long expiresIn = null;
private Long expires = null;
// by default, expire in 1 minute.
private static final Long DEFAULT_EXPIRES_IN = new Long(60 * 1000);
public QueryStringAuthGenerator(String awsAccessKeyId, String awsSecretAccessKey) {
this(awsAccessKeyId, awsSecretAccessKey, true);
}
public QueryStringAuthGenerator(String awsAccessKeyId, String awsSecretAccessKey,
boolean isSecure)
{
this(awsAccessKeyId, awsSecretAccessKey, isSecure, Utils.DEFAULT_HOST);
}
public QueryStringAuthGenerator(String awsAccessKeyId, String awsSecretAccessKey,
boolean isSecure, String server)
{
this(awsAccessKeyId, awsSecretAccessKey, isSecure, server,
isSecure ? Utils.SECURE_PORT : Utils.INSECURE_PORT);
}
public QueryStringAuthGenerator(String awsAccessKeyId, String awsSecretAccessKey,
boolean isSecure, String server, int port)
{
this(awsAccessKeyId, awsSecretAccessKey, isSecure, server,
port, CallingFormat.getSubdomainCallingFormat());
}
public QueryStringAuthGenerator(String awsAccessKeyId, String awsSecretAccessKey,
boolean isSecure, String server, CallingFormat callingFormat)
{
this(awsAccessKeyId, awsSecretAccessKey, isSecure, server,
isSecure ? Utils.SECURE_PORT : Utils.INSECURE_PORT,
callingFormat);
}
public QueryStringAuthGenerator(String awsAccessKeyId, String awsSecretAccessKey,
boolean isSecure, String server, int port, CallingFormat callingFormat)
{
this.awsAccessKeyId = awsAccessKeyId;
this.awsSecretAccessKey = awsSecretAccessKey;
this.isSecure = isSecure;
this.server = server;
this.port = port;
this.callingFormat = callingFormat;
this.expiresIn = DEFAULT_EXPIRES_IN;
this.expires = null;
}
public void setCallingFormat(CallingFormat format) {
this.callingFormat = format;
}
public void setExpires(long millisSinceEpoch) {
expires = new Long(millisSinceEpoch);
expiresIn = null;
}
public void setExpiresIn(long millis) {
expiresIn = new Long(millis);
expires = null;
}
public String createBucket(String bucket, Map headers)
{
// validate bucket name
if (!Utils.validateBucketName(bucket, callingFormat, false))
throw new IllegalArgumentException("Invalid S3Bucket Name: "+bucket);
Map pathArgs = new HashMap();
return generateURL("PUT", bucket, "", pathArgs, headers);
}
public String listBucket(String bucket, String prefix, String marker,
Integer maxKeys, Map headers){
return listBucket(bucket, prefix, marker, maxKeys, null, headers);
}
public String listBucket(String bucket, String prefix, String marker,
Integer maxKeys, String delimiter, Map headers)
{
Map pathArgs = Utils.paramsForListOptions(prefix, marker, maxKeys, delimiter);
return generateURL("GET", bucket, "", pathArgs, headers);
}
public String deleteBucket(String bucket, Map headers)
{
Map pathArgs = new HashMap();
return generateURL("DELETE", bucket, "", pathArgs, headers);
}
public String put(String bucket, String key, S3Object object, Map headers) {
Map metadata = null;
Map pathArgs = new HashMap();
if (object != null) {
metadata = object.metadata;
}
return generateURL("PUT", bucket, Utils.urlencode(key), pathArgs, mergeMeta(headers, metadata));
}
public String get(String bucket, String key, Map headers)
{
Map pathArgs = new HashMap();
return generateURL("GET", bucket, Utils.urlencode(key), pathArgs, headers);
}
public String delete(String bucket, String key, Map headers)
{
Map pathArgs = new HashMap();
return generateURL("DELETE", bucket, Utils.urlencode(key), pathArgs, headers);
}
public String getBucketLogging(String bucket, Map headers) {
Map pathArgs = new HashMap();
pathArgs.put("logging", null);
return generateURL("GET", bucket, "", pathArgs, headers);
}
public String putBucketLogging(String bucket, String loggingXMLDoc, Map headers) {
Map pathArgs = new HashMap();
pathArgs.put("logging", null);
return generateURL("PUT", bucket, "", pathArgs, headers);
}
public String getBucketACL(String bucket, Map headers) {
return getACL(bucket, "", headers);
}
public String getACL(String bucket, String key, Map headers)
{
Map pathArgs = new HashMap();
pathArgs.put("acl", null);
return generateURL("GET", bucket, Utils.urlencode(key), pathArgs, headers);
}
public String putBucketACL(String bucket, String aclXMLDoc, Map headers) {
return putACL(bucket, "", aclXMLDoc, headers);
}
public String putACL(String bucket, String key, String aclXMLDoc, Map headers)
{
Map pathArgs = new HashMap();
pathArgs.put("acl", null);
return generateURL("PUT", bucket, Utils.urlencode(key), pathArgs, headers);
}
public String listAllMyBuckets(Map headers)
{
Map pathArgs = new HashMap();
return generateURL("GET", "", "", pathArgs, headers);
}
public String makeBareURL(String bucket, String key) {
StringBuffer buffer = new StringBuffer();
if (this.isSecure) {
buffer.append("https://");
} else {
buffer.append("http://");
}
buffer.append(this.server).append(":").append(this.port).append("/").append(bucket);
buffer.append("/").append(Utils.urlencode(key));
return buffer.toString();
}
private String generateURL(String method, String bucketName, String key, Map pathArgs, Map headers)
{
long expires = 0L;
if (this.expiresIn != null) {
expires = System.currentTimeMillis() + this.expiresIn.longValue();
} else if (this.expires != null) {
expires = this.expires.longValue();
} else {
throw new RuntimeException("Illegal expires state");
}
// convert to seconds
expires /= 1000;
String canonicalString = Utils.makeCanonicalString(method, bucketName, key, pathArgs, headers, ""+expires);
String encodedCanonical = Utils.encode(this.awsSecretAccessKey, canonicalString, true);
pathArgs.put("Signature", encodedCanonical);
pathArgs.put("Expires", Long.toString(expires));
pathArgs.put("AWSAccessKeyId", this.awsAccessKeyId);
CallingFormat callingFormat = Utils.getCallingFormatForBucket( this.callingFormat, bucketName );
if ( isSecure && callingFormat != CallingFormat.getPathCallingFormat() && bucketName.indexOf( "." ) != -1 ) {
System.err.println( "You are making an SSL connection, however, the bucket contains periods and the wildcard certificate will not match by default. Please consider using HTTP." );
}
String returnString;
try {
URL url = callingFormat.getURL(isSecure, server, port, bucketName, key, pathArgs);
returnString = url.toString();
} catch (MalformedURLException e) {
returnString = "Exception generating url " + e;
}
return returnString;
}
private Map mergeMeta(Map headers, Map metadata) {
Map merged = new TreeMap();
if (headers != null) {
for (Iterator i = headers.keySet().iterator(); i.hasNext(); ) {
String key = (String)i.next();
merged.put(key, headers.get(key));
}
}
if (metadata != null) {
for (Iterator i = metadata.keySet().iterator(); i.hasNext(); ) {
String key = (String)i.next();
String metadataKey = Utils.METADATA_PREFIX + key;
if (merged.containsKey(metadataKey)) {
((List)merged.get(metadataKey)).addAll((List)metadata.get(key));
} else {
merged.put(metadataKey, metadata.get(key));
}
}
}
return merged;
}
}

View File

@ -0,0 +1,25 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
import java.net.HttpURLConnection;
import java.io.IOException;
/**
* The parent class of all other Responses. This class keeps track of the
* HttpURLConnection response.
*/
public class Response {
public HttpURLConnection connection;
public Response(HttpURLConnection connection) throws IOException {
this.connection = connection;
}
}

View File

@ -0,0 +1,30 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
import java.util.Map;
/**
* A representation of a single object stored in S3.
*/
public class S3Object {
public byte[] data;
/**
* A Map from String to List of Strings representing the object's metadata
*/
public Map metadata;
public S3Object(byte[] data, Map metadata) {
this.data = data;
this.metadata = metadata;
}
}

View File

@ -0,0 +1,324 @@
// This software code is made available "AS IS" without warranties of any
// kind. You may copy, display, modify and redistribute the software
// code either by itself or as incorporated into your code; provided that
// you do not remove any proprietary notices. Your use of this software
// code is at your own risk and you waive any claim against Amazon
// Digital Services, Inc. or its affiliates with respect to your use of
// this software code. (c) 2006-2007 Amazon Digital Services, Inc. or its
// affiliates.
package com.amazon.s3;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.SAXException;
import com.amazon.thirdparty.Base64;
public class Utils {
static final String METADATA_PREFIX = "x-amz-meta-";
static final String AMAZON_HEADER_PREFIX = "x-amz-";
static final String ALTERNATIVE_DATE_HEADER = "x-amz-date";
public static final String DEFAULT_HOST = "s3.amazonaws.com";
public static final int SECURE_PORT = 443;
public static final int INSECURE_PORT = 80;
/**
* HMAC/SHA1 Algorithm per RFC 2104.
*/
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
static String makeCanonicalString(String method, String bucket, String key, Map pathArgs, Map headers) {
return makeCanonicalString(method, bucket, key, pathArgs, headers, null);
}
/**
* Calculate the canonical string. When expires is non-null, it will be
* used instead of the Date header.
*/
static String makeCanonicalString(String method, String bucketName, String key, Map pathArgs,
Map headers, String expires)
{
StringBuffer buf = new StringBuffer();
buf.append(method + "\n");
// Add all interesting headers to a list, then sort them. "Interesting"
// is defined as Content-MD5, Content-Type, Date, and x-amz-
SortedMap interestingHeaders = new TreeMap();
if (headers != null) {
for (Iterator i = headers.keySet().iterator(); i.hasNext(); ) {
String hashKey = (String)i.next();
if (hashKey == null) continue;
String lk = hashKey.toLowerCase();
// Ignore any headers that are not particularly interesting.
if (lk.equals("content-type") || lk.equals("content-md5") || lk.equals("date") ||
lk.startsWith(AMAZON_HEADER_PREFIX))
{
List s = (List)headers.get(hashKey);
interestingHeaders.put(lk, concatenateList(s));
}
}
}
if (interestingHeaders.containsKey(ALTERNATIVE_DATE_HEADER)) {
interestingHeaders.put("date", "");
}
// if the expires is non-null, use that for the date field. this
// trumps the x-amz-date behavior.
if (expires != null) {
interestingHeaders.put("date", expires);
}
// these headers require that we still put a new line in after them,
// even if they don't exist.
if (! interestingHeaders.containsKey("content-type")) {
interestingHeaders.put("content-type", "");
}
if (! interestingHeaders.containsKey("content-md5")) {
interestingHeaders.put("content-md5", "");
}
// Finally, add all the interesting headers (i.e.: all that startwith x-amz- ;-))
for (Iterator i = interestingHeaders.keySet().iterator(); i.hasNext(); ) {
String headerKey = (String)i.next();
if (headerKey.startsWith(AMAZON_HEADER_PREFIX)) {
buf.append(headerKey).append(':').append(interestingHeaders.get(headerKey));
} else {
buf.append(interestingHeaders.get(headerKey));
}
buf.append("\n");
}
// build the path using the bucket and key
if (bucketName != null && !bucketName.equals("")) {
buf.append("/" + bucketName );
}
// append the key (it might be an empty string)
// append a slash regardless
buf.append("/");
if(key != null) {
buf.append(key);
}
// if there is an acl, logging or torrent parameter
// add them to the string
if (pathArgs != null ) {
if (pathArgs.containsKey("acl")) {
buf.append("?acl");
} else if (pathArgs.containsKey("torrent")) {
buf.append("?torrent");
} else if (pathArgs.containsKey("logging")) {
buf.append("?logging");
} else if (pathArgs.containsKey("location")) {
buf.append("?location");
}
}
return buf.toString();
}
/**
* Calculate the HMAC/SHA1 on a string.
* @param data Data to sign
* @param passcode Passcode to sign it with
* @return Signature
* @throws NoSuchAlgorithmException If the algorithm does not exist. Unlikely
* @throws InvalidKeyException If the key is invalid.
*/
static String encode(String awsSecretAccessKey, String canonicalString,
boolean urlencode)
{
// The following HMAC/SHA1 code for the signature is taken from the
// AWS Platform's implementation of RFC2104 (amazon.webservices.common.Signature)
//
// Acquire an HMAC/SHA1 from the raw key bytes.
SecretKeySpec signingKey =
new SecretKeySpec(awsSecretAccessKey.getBytes(), HMAC_SHA1_ALGORITHM);
// Acquire the MAC instance and initialize with the signing key.
Mac mac = null;
try {
mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
// should not happen
throw new RuntimeException("Could not find sha1 algorithm", e);
}
try {
mac.init(signingKey);
} catch (InvalidKeyException e) {
// also should not happen
throw new RuntimeException("Could not initialize the MAC algorithm", e);
}
// Compute the HMAC on the digest, and set it.
String b64 = Base64.encodeBytes(mac.doFinal(canonicalString.getBytes()));
if (urlencode) {
return urlencode(b64);
} else {
return b64;
}
}
static Map paramsForListOptions(String prefix, String marker, Integer maxKeys) {
return paramsForListOptions(prefix, marker, maxKeys, null);
}
static Map paramsForListOptions(String prefix, String marker, Integer maxKeys, String delimiter) {
Map argParams = new HashMap();
// these three params must be url encoded
if (prefix != null)
argParams.put("prefix", urlencode(prefix));
if (marker != null)
argParams.put("marker", urlencode(marker));
if (delimiter != null)
argParams.put("delimiter", urlencode(delimiter));
if (maxKeys != null)
argParams.put("max-keys", Integer.toString(maxKeys.intValue()));
return argParams;
}
/**
* Converts the Path Arguments from a map to String which can be used in url construction
* @param pathArgs a map of arguments
* @return a string representation of pathArgs
*/
public static String convertPathArgsHashToString(Map pathArgs) {
StringBuffer pathArgsString = new StringBuffer();
String argumentValue;
boolean firstRun = true;
if (pathArgs != null) {
for (Iterator argumentIterator = pathArgs.keySet().iterator(); argumentIterator.hasNext(); ) {
String argument = (String)argumentIterator.next();
if (firstRun) {
firstRun = false;
pathArgsString.append("?");
} else {
pathArgsString.append("&");
}
argumentValue = (String)pathArgs.get(argument);
pathArgsString.append(argument);
if (argumentValue != null) {
pathArgsString.append("=");
pathArgsString.append(argumentValue);
}
}
}
return pathArgsString.toString();
}
static String urlencode(String unencoded) {
try {
return URLEncoder.encode(unencoded, "UTF-8");
} catch (UnsupportedEncodingException e) {
// should never happen
throw new RuntimeException("Could not url encode to UTF-8", e);
}
}
static XMLReader createXMLReader() {
try {
return XMLReaderFactory.createXMLReader();
} catch (SAXException e) {
// oops, lets try doing this (needed in 1.4)
System.setProperty("org.xml.sax.driver", "org.apache.crimson.parser.XMLReaderImpl");
}
try {
// try once more
return XMLReaderFactory.createXMLReader();
} catch (SAXException e) {
throw new RuntimeException("Couldn't initialize a sax driver for the XMLReader");
}
}
/**
* Concatenates a bunch of header values, seperating them with a comma.
* @param values List of header values.
* @return String of all headers, with commas.
*/
private static String concatenateList(List values) {
StringBuffer buf = new StringBuffer();
for (int i = 0, size = values.size(); i < size; ++ i) {
buf.append(((String)values.get(i)).replaceAll("\n", "").trim());
if (i != (size - 1)) {
buf.append(",");
}
}
return buf.toString();
}
/**
* Validate bucket-name
*/
static boolean validateBucketName(String bucketName, CallingFormat callingFormat, boolean located) {
if (callingFormat == CallingFormat.getPathCallingFormat())
{
final int MIN_BUCKET_LENGTH = 3;
final int MAX_BUCKET_LENGTH = 255;
final String BUCKET_NAME_REGEX = "^[0-9A-Za-z\\.\\-_]*$";
return null != bucketName &&
bucketName.length() >= MIN_BUCKET_LENGTH &&
bucketName.length() <= MAX_BUCKET_LENGTH &&
bucketName.matches(BUCKET_NAME_REGEX);
} else {
return isValidSubdomainBucketName( bucketName );
}
}
static boolean isValidSubdomainBucketName( String bucketName ) {
final int MIN_BUCKET_LENGTH = 3;
final int MAX_BUCKET_LENGTH = 63;
// don't allow names that look like 127.0.0.1
final String IPv4_REGEX = "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$";
// dns sub-name restrictions
final String BUCKET_NAME_REGEX = "^[a-z0-9]([a-z0-9\\-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9\\-]*[a-z0-9])?)*$";
// If there wasn't a location-constraint, then the current actual
// restriction is just that no 'part' of the name (i.e. sequence
// of characters between any 2 '.'s has to be 63) but the recommendation
// is to keep the entire bucket name under 63.
return null != bucketName &&
bucketName.length() >= MIN_BUCKET_LENGTH &&
bucketName.length() <= MAX_BUCKET_LENGTH &&
!bucketName.matches(IPv4_REGEX) &&
bucketName.matches(BUCKET_NAME_REGEX);
}
static CallingFormat getCallingFormatForBucket( CallingFormat desiredFormat, String bucketName ) {
CallingFormat callingFormat = desiredFormat;
if ( callingFormat == CallingFormat.getSubdomainCallingFormat() && ! Utils.isValidSubdomainBucketName( bucketName ) ) {
callingFormat = CallingFormat.getPathCallingFormat();
}
return callingFormat;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,84 @@
This is one of a collection of interface libraries that can be used to interact
with the Amazon S3 system in a number of different languages. They each expose
two main interface classes, AWSAuthConnection and QueryStringAuthGenerator.
The first actually performs all the operations using the appropriate libraries
for the language, including header signing. The second,
QueryStringAuthGenerator, has the same interface, but instead of performing
the operation, this class will return urls with the right query string
authentication parameters set.
Basic Operations:
object requests:
GetResponse get(bucketName, keyName) - retrieves an object
GetResponse getACL(bucketName, keyName) - returns the xml acl doc
Response put(bucketName, keyName, object) - writes an object
Response putACL(bucketName, keyName, aclXMLDoc) - sets the xml acl doc
Response delete(bucketName, keyName) - deletes an object
bucket requests:
Response createBucket(bucketName, location) - creates a bucket
ListResponse listBucket(bucketName) - lists a bucket's contents
LocationResponse getBucketLocation(bucketName) - return the location-constraint of this bucket
GetResponse getBucketACL(bucketName) - returns the xml representation of this bucket's access control list
Response putBucketAcl(bucketName, aclXMLDoc) - sets xml representation of the bucket acl
Response deleteBucket(bucketName) - delete an empty bucket
GetResponse getBucketLogging(bucketName) - returns the xml representation of this bucket's access logging configuration
Response putBucketLogging(bucketName, loggingXMLDoc) - sets the xml representation of the bucket logging configuration
ListAllMyBucketsResponse listAllMyBuckets() - returns a list of all buckets owned by this AWS Access Key Id
Dependencies:
None, beyond the standard libraries.
Notes:
Please note that this uses the public domain iHarder.net Base64 library. For updates to that library,
see http://iharder.sourceforge.net/current/java/base64/ .
If your bucket name contains periods, you will need to use a non-HTTPS connection as the SSL certificate
presented by s3.amazonaws.com will not match if you do.
Limitations:
One of the main limitations of these sample AWSAuthConnection implementations
is that the interfaces are not streaming. This means that you have to pass the
data in as a string or as a byte array and the operation returns a string or a
byte array back. This is conceptually simpler, and fine for smaller objects,
but large objects, say a couple megabytes or greater, will show poor
performance, since everything is being passed around in memory. More
sophisticated libraries would pass streams in and out, and would only read the
data on-demand, rather than storing it all in memory (S3 itself would have no
problem with such streaming applications). Another upshot of this is that the
interfaces are all blocking---that is, you cannot look at the data until all of
it has downloaded. Again, this is fine for smaller objects, but unacceptable
for larger ones.
These libraries have nearly non-existent error handling. All errors from lower
libraries are simply passed up. The response code in the connection object needs
to be checked after each request to verify whether the request succeeded.
Only the java library has proper handling for repeated headers. The others
assume that each header will have only one value.
It is our intention that these libraries act as a starting point for future
development. They are meant to show off the various operations and provide an
example of how to negotiate the authentication process.
This software code is made available "AS IS" without warranties of any
kind. You may copy, display, modify and redistribute the software
code either by itself or as incorporated into your code; provided that
you do not remove any proprietary notices. Your use of this software
code is at your own risk and you waive any claim against Amazon
Digital Services, Inc. or its affiliates with respect to your use of
this software code. (c) 2006 Amazon Digital Services, Inc. or its
affiliates.

View File

@ -0,0 +1,119 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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 com.amazon.s3;
import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ExecutionException;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "s3.AmazonPerformance", groups = "performance")
public class AmazonPerformance extends BasePerformance {
private AWSAuthConnection amzClient;
@Override
@BeforeTest
@Parameters( { "jclouds.aws.accesskeyid", "jclouds.aws.secretaccesskey" })
protected void setUpClient(@Optional String AWSAccessKeyId, @Optional String AWSSecretAccessKey) throws Exception {
super.setUpClient(AWSAccessKeyId, AWSSecretAccessKey);
amzClient = new AWSAuthConnection(AWSAccessKeyId, AWSSecretAccessKey,
false);
}
@Override
protected void testPutFileSerial() throws Exception {
throw new UnsupportedOperationException();
}
@Override
protected void testPutFileParallel() throws InterruptedException,
ExecutionException {
throw new UnsupportedOperationException();
}
@Override
protected void testPutInputStreamSerial() throws Exception {
throw new UnsupportedOperationException();
}
@Override
protected void testPutInputStreamParallel() throws InterruptedException,
ExecutionException {
throw new UnsupportedOperationException();
}
@Override
protected void testPutStringSerial() throws Exception {
throw new UnsupportedOperationException();
}
@Override
protected void testPutStringParallel() throws InterruptedException,
ExecutionException {
throw new UnsupportedOperationException();
}
@Override
protected boolean putByteArray(String bucket, String key, byte[] data,
String contentType) throws Exception {
com.amazon.s3.S3Object object = new com.amazon.s3.S3Object(data, null);
Map headers = new TreeMap();
headers
.put("Content-Type", Arrays
.asList(new String[] { contentType }));
return amzClient.put(bucket, key, object, headers).connection
.getResponseMessage() != null;
}
@Override
protected boolean putFile(String bucket, String key, File data,
String contentType) throws Exception {
throw new UnsupportedOperationException();
}
@Override
protected boolean putInputStream(String bucket, String key,
InputStream data, String contentType) throws Exception {
throw new UnsupportedOperationException();
}
@Override
protected boolean putString(String bucket, String key, String data,
String contentType) throws Exception {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,102 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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 com.amazon.s3;
import java.io.File;
import java.io.InputStream;
import org.jclouds.aws.s3.domain.S3Bucket;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public abstract class BaseJCloudsPerformance extends BasePerformance {
// boolean get
// (
// int id) throws Exception {
// S3Bucket s3Bucket = new S3Bucket();
// s3Bucket.setName(bucketPrefix + "-jclouds-puts");
// org.jclouds.aws.s3.domain.S3Object object = new
// org.jclouds.aws.s3.domain.S3Object();
// object.setKey(id + "");
// //object.setContentType("text/plain");
// object.setContentType("application/octetstream");
// //object.setContent("this is a test");
// object.setContent(test);
// return clientProvider.getObject(s3Bucket, object.getKey()).get() !=
// org.jclouds.aws.s3.domain.S3Object.NOT_FOUND;
// }
@Override
protected boolean putByteArray(String bucket, String key, byte[] data,
String contentType) throws Exception {
S3Bucket s3Bucket = new S3Bucket();
s3Bucket.setName(bucket);
org.jclouds.aws.s3.domain.S3Object object = new org.jclouds.aws.s3.domain.S3Object();
object.setKey(key);
object.setContentType(contentType);
object.setContent(data);
return client.addObject(s3Bucket, object).get() != null;
}
@Override
protected boolean putFile(String bucket, String key, File data,
String contentType) throws Exception {
S3Bucket s3Bucket = new S3Bucket();
s3Bucket.setName(bucket);
org.jclouds.aws.s3.domain.S3Object object = new org.jclouds.aws.s3.domain.S3Object();
object.setKey(key);
object.setContentType(contentType);
object.setContent(data);
return client.addObject(s3Bucket, object).get() != null;
}
@Override
protected boolean putInputStream(String bucket, String key,
InputStream data, String contentType) throws Exception {
S3Bucket s3Bucket = new S3Bucket();
s3Bucket.setName(bucket);
org.jclouds.aws.s3.domain.S3Object object = new org.jclouds.aws.s3.domain.S3Object();
object.setKey(key);
object.setContentType(contentType);
object.setContent(data);
object.setSize(data.available());
return client.addObject(s3Bucket, object).get() != null;
}
@Override
protected boolean putString(String bucket, String key, String data,
String contentType) throws Exception {
S3Bucket s3Bucket = new S3Bucket();
s3Bucket.setName(bucket);
org.jclouds.aws.s3.domain.S3Object object = new org.jclouds.aws.s3.domain.S3Object();
object.setKey(key);
object.setContentType(contentType);
object.setContent(data);
return client.addObject(s3Bucket, object).get() != null;
}
}

View File

@ -0,0 +1,258 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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 com.amazon.s3;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import org.jclouds.aws.s3.S3IntegrationTest;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import com.google.inject.Provider;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Test(sequential = true, timeOut = 2 * 60 * 1000, groups = "performance")
public abstract class BasePerformance extends S3IntegrationTest {
protected static int LOOP_COUNT = 100;
protected ExecutorService exec;
protected final String BUCKET_BYTES = bucketPrefix + "-bytes";
protected final String BUCKET_INPUTSTREAM = bucketPrefix + "-inputstream";
protected final String BUCKET_STRING = bucketPrefix + "-string";
protected final String BUCKET_FILE = bucketPrefix + "-file";
protected final String[] BUCKETS = { BUCKET_BYTES, BUCKET_INPUTSTREAM,
BUCKET_STRING, BUCKET_FILE };
protected PutBytesCallable putBytesCallable;
protected PutFileCallable putFileCallable;
protected PutInputStreamCallable putInputStreamCallable;
protected PutStringCallable putStringCallable;
protected CompletionService<Boolean> completer;
@BeforeTest
protected void setUpCallables() {
putBytesCallable = new PutBytesCallable();
putFileCallable = new PutFileCallable();
putInputStreamCallable = new PutInputStreamCallable();
putStringCallable = new PutStringCallable();
exec = Executors.newCachedThreadPool();
completer = new ExecutorCompletionService<Boolean>(exec);
}
@Override
@BeforeTest
@Parameters( { "jclouds.aws.accesskeyid", "jclouds.aws.secretaccesskey" })
protected void setUpClient(@Optional String AWSAccessKeyId,
@Optional String AWSSecretAccessKey) throws Exception {
super.setUpClient(AWSAccessKeyId, AWSSecretAccessKey);
S3Bucket s3Bucket = new S3Bucket();
for (String bucket : BUCKETS) {
s3Bucket.setName(bucket);
client.createBucketIfNotExists(s3Bucket).get();
}
}
@AfterTest
protected void tearDownExecutor() throws Exception {
exec.shutdownNow();
exec = null;
}
@Test(enabled = true)
protected void testPutBytesSerial() throws Exception {
doSerial(putBytesCallable, LOOP_COUNT / 10);
}
@Test(enabled = true)
protected void testPutBytesParallel() throws InterruptedException,
ExecutionException {
doParallel(putBytesCallable, LOOP_COUNT);
}
@Test(enabled = true)
protected void testPutFileSerial() throws Exception {
doSerial(putFileCallable, LOOP_COUNT / 10);
}
@Test(enabled = true)
protected void testPutFileParallel() throws InterruptedException,
ExecutionException {
doParallel(putFileCallable, LOOP_COUNT);
}
@Test(enabled = true)
protected void testPutInputStreamSerial() throws Exception {
doSerial(putInputStreamCallable, LOOP_COUNT / 10);
}
@Test(enabled = true)
protected void testPutInputStreamParallel() throws InterruptedException,
ExecutionException {
doParallel(putInputStreamCallable, LOOP_COUNT);
}
@Test(enabled = true)
protected void testPutStringSerial() throws Exception {
doSerial(putStringCallable, LOOP_COUNT / 10);
}
@Test(enabled = true)
protected void testPutStringParallel() throws InterruptedException,
ExecutionException {
doParallel(putStringCallable, LOOP_COUNT);
}
private void doSerial(Provider<Callable<Boolean>> provider, int loopCount)
throws Exception, ExecutionException {
for (int i = 0; i < loopCount; i++)
assert provider.get().call();
}
private void doParallel(Provider<Callable<Boolean>> provider, int loopCount)
throws InterruptedException, ExecutionException {
for (int i = 0; i < loopCount; i++)
completer.submit(provider.get());
for (int i = 0; i < loopCount; i++)
assert completer.take().get();
}
class PutBytesCallable implements Provider<Callable<Boolean>> {
final AtomicInteger key = new AtomicInteger(0);
protected byte[] test = new byte[1024 * 2];
public Callable<Boolean> get() {
return new Callable<Boolean>() {
public Boolean call() throws Exception {
return putByteArray(BUCKET_BYTES, key.getAndIncrement()
+ "", test, "application/octetstring");
}
};
}
}
class PutFileCallable implements Provider<Callable<Boolean>> {
final AtomicInteger key = new AtomicInteger(0);
protected File file = new File(
"/Users/adriancole/Desktop/charles_ca_certificate.zip");
public Callable<Boolean> get() {
return new Callable<Boolean>() {
public Boolean call() throws Exception {
return putFile(BUCKET_FILE, key.getAndIncrement() + "",
file, "application/zip");
}
};
}
}
class PutInputStreamCallable extends PutBytesCallable {
final AtomicInteger key = new AtomicInteger(0);
@Override
public Callable<Boolean> get() {
return new Callable<Boolean>() {
public Boolean call() throws Exception {
return putInputStream(BUCKET_INPUTSTREAM, key
.getAndIncrement()
+ "", new ByteArrayInputStream(test),
"application/octetstring");
}
};
}
}
class PutStringCallable implements Provider<Callable<Boolean>> {
final AtomicInteger key = new AtomicInteger(0);
protected String testString = "hello world!";
public Callable<Boolean> get() {
return new Callable<Boolean>() {
public Boolean call() throws Exception {
return putString(BUCKET_STRING, key.getAndIncrement() + "",
testString, "text/plain");
}
};
}
}
protected abstract boolean putByteArray(String bucket, String key,
byte[] data, String contentType) throws Exception;
protected abstract boolean putFile(String bucket, String key, File data,
String contentType) throws Exception;
protected abstract boolean putInputStream(String bucket, String key,
InputStream data, String contentType) throws Exception;
protected abstract boolean putString(String bucket, String key,
String data, String contentType) throws Exception;
// private class BucketDeleter implements Callable<Boolean> {
// private BucketDeleter(S3Bucket bucket) {
// this.bucket = bucket;
// }
//
// private S3Bucket bucket;
//
// @Override
// public String toString() {
// return "BucketDeleter{" + "bucket=" + bucket + '}';
// }
//
// public Boolean call() throws Exception {
// bucket = clientProvider.get().getBucket(bucket).get();
// List<Future<Boolean>> deletes = new ArrayList<Future<Boolean>>();
// for (org.jclouds.aws.s3.domain.S3Object object : bucket
// .getContents()) {
// deletes.add(clientProvider.get().deleteObject(bucket,
// object.getKey()));
// }
// for (Future<Boolean> isdeleted : deletes)
// assert isdeleted.get() : String.format("failed to delete %1s",
// isdeleted);
// return clientProvider.get().deleteBucket(bucket).get();
// }
// }
}

View File

@ -0,0 +1,155 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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 com.amazon.s3;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.jclouds.aws.PerformanceTest;
import org.jclouds.aws.s3.DateService;
import org.joda.time.DateTime;
import org.testng.annotations.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SimpleTimeZone;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "s3.DateTest", groups = "performance")
public class DateTest extends PerformanceTest {
Injector i = Guice.createInjector();
DateService utils = i.getInstance(DateService.class);
SimpleDateFormat dateParser;
public DateTest() {
this.dateParser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
this.dateParser.setTimeZone(new SimpleTimeZone(0, "GMT"));
}
Date amazonDateFromString(String toParse) throws ParseException {
return this.dateParser.parse(toParse);
}
private static String toParse = "2009-03-12T02:00:07.000Z";
@Test
public void testParseDateSameAsAmazon() throws ParseException, ExecutionException, InterruptedException {
Date java = dateParser.parse(toParse);
DateTime joda = utils.dateTimeFromXMLFormat(toParse);
assert java.equals(joda.toDate());
}
@Test
public void testTimeStampDateSameAsAmazon() throws ExecutionException, InterruptedException {
String java = AWSAuthConnection.httpDate();
String joda = utils.timestampAsHeaderString();
assert java.equals(joda);
}
@Test
void testTimeStampSerialResponseTime() throws ExecutionException, InterruptedException {
for (int i = 0; i < LOOP_COUNT; i++)
utils.timestampAsHeaderString();
}
@Test
void testAmazonTimeStampSerialResponseTime() {
for (int i = 0; i < LOOP_COUNT; i++)
AWSAuthConnection.httpDate();
}
@Test
void testTimeStampParallelResponseTime() throws InterruptedException, ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() throws ExecutionException, InterruptedException {
utils.timestampAsHeaderString();
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++) assert completer.take().get();
}
@Test
void testAmazonTimeStampParallelResponseTime() throws InterruptedException, ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() {
AWSAuthConnection.httpDate();
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++) assert completer.take().get();
}
@Test
void testParseDateSerialResponseTime() throws ExecutionException, InterruptedException {
for (int i = 0; i < LOOP_COUNT; i++)
utils.dateTimeFromXMLFormat(toParse);
}
@Test
void testAmazonParseDateSerialResponseTime() {
for (int i = 0; i < LOOP_COUNT; i++)
AWSAuthConnection.httpDate();
}
@Test
void testParseDateParallelResponseTime() throws InterruptedException, ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() throws ExecutionException, InterruptedException {
utils.dateTimeFromXMLFormat(toParse);
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++) assert completer.take().get();
}
@Test
void testAmazonParseDateParallelResponseTime() throws InterruptedException, ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() {
AWSAuthConnection.httpDate();
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++) assert completer.take().get();
}
}

View File

@ -0,0 +1,52 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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 com.amazon.s3;
import com.google.inject.Module;
import org.jclouds.aws.s3.nio.config.S3HttpNioConnectionPoolClientModule;
import org.testng.annotations.Test;
import java.util.Properties;
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "s3.JCloudsNioPerformance", groups = "performance")
public class JCloudsNioPerformance extends BaseJCloudsPerformance {
@Override
protected Properties buildS3Properties(String AWSAccessKeyId,
String AWSSecretAccessKey) {
Properties properties = super.buildS3Properties(AWSAccessKeyId, AWSSecretAccessKey);
properties.setProperty("jclouds.http.pool.max_connection_reuse", "75");
properties.setProperty("jclouds.http.pool.max_session_failures", "2");
properties.setProperty("jclouds.http.pool.request_invoker_threads", "1");
properties.setProperty("jclouds.http.pool.io_worker_threads", "2");
properties.setProperty("jclouds.pool.max_connections", "12");
return properties;
}
@Override
protected Module createHttpModule() {
return new S3HttpNioConnectionPoolClientModule();
}
}

View File

@ -0,0 +1,32 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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 com.amazon.s3;
import org.testng.annotations.Test;
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "s3.JCloudsPerformance", groups = "performance")
public class JCloudsPerformance extends BaseJCloudsPerformance {
}

View File

@ -0,0 +1,109 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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 com.amazon.s3;
import java.io.File;
import java.io.InputStream;
import java.util.concurrent.ExecutionException;
import org.jets3t.service.S3Service;
import org.jets3t.service.impl.rest.httpclient.RestS3Service;
import org.jets3t.service.security.AWSCredentials;
import org.testng.annotations.Test;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "s3.Jets3tPerformance", groups = "performance")
public class Jets3tPerformance extends BasePerformance {
private S3Service jetClient;
@Override
protected void setUpClient(String AWSAccessKeyId, String AWSSecretAccessKey)
throws Exception {
super.setUpClient(AWSAccessKeyId, AWSSecretAccessKey);
jetClient = new RestS3Service(new AWSCredentials(AWSAccessKeyId,
AWSSecretAccessKey));
}
@Override
protected void testPutStringSerial() throws Exception {
throw new UnsupportedOperationException();
}
@Override
protected void testPutStringParallel() throws InterruptedException,
ExecutionException {
throw new UnsupportedOperationException();
}
@Override
protected void testPutBytesSerial() throws Exception {
throw new UnsupportedOperationException();
}
@Override
protected void testPutBytesParallel() throws InterruptedException,
ExecutionException {
throw new UnsupportedOperationException();
}
@Override
protected boolean putByteArray(String bucket, String key, byte[] data,
String contentType) throws Exception {
throw new UnsupportedOperationException();
}
@Override
protected boolean putFile(String bucket, String key, File data,
String contentType) throws Exception {
org.jets3t.service.model.S3Object object = new org.jets3t.service.model.S3Object(
key);
object.setContentType(contentType);
object.setDataInputFile(data);
object.setContentLength(data.length());
return jetClient.putObject(bucket, object) != null;
}
@Override
protected boolean putInputStream(String bucket, String key,
InputStream data, String contentType) throws Exception {
org.jets3t.service.model.S3Object object = new org.jets3t.service.model.S3Object(
key);
object.setContentType(contentType);
object.setDataInputStream(data);
object.setContentLength(data.available());
return jetClient.putObject(bucket, object) != null;
}
@Override
protected boolean putString(String bucket, String key, String data,
String contentType) throws Exception {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,168 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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 com.amazon.s3;
import org.apache.commons.io.IOUtils;
import org.joda.time.DateTime;
import org.testng.annotations.Test;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "s3.S3ParserTest", groups = "performance")
public class S3ParserTest extends org.jclouds.aws.s3.commands.S3ParserTest {
class MockHttpURLConnection extends HttpURLConnection {
private String content;
@Override
public InputStream getInputStream() throws IOException {
return IOUtils.toInputStream(content);
}
protected MockHttpURLConnection(String content) {
super(null);
this.content = content;
}
public void disconnect() {
}
public boolean usingProxy() {
return false;
}
@Override
public int getResponseCode() throws IOException {
return 200;
}
public void connect() throws IOException {
}
}
@Test
void testAmazonParseListAllMyBucketsSerialResponseTime() throws IOException {
for (int i = 0; i < LOOP_COUNT; i++)
runAmazonParseListAllMyBuckets();
}
@Test
void testAmazonParseListAllMyBucketsParallelResponseTime()
throws InterruptedException, ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(
exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() throws IOException {
runAmazonParseListAllMyBuckets();
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++)
assert completer.take().get();
}
@Test
public void testAmazonCanParseListAllMyBuckets() throws IOException {
ListAllMyBucketsResponse response = runAmazonParseListAllMyBuckets();
List<Bucket> buckets = response.entries;
Bucket bucket1 = (Bucket) buckets.get(0);
assert bucket1.name.equals("adrianjbosstest");
Date expectedDate1 = new DateTime("2009-03-12T02:00:07.000Z").toDate();
Date date1 = bucket1.creationDate;
assert date1.toString().equals(expectedDate1.toString());
Bucket bucket2 = (Bucket) buckets.get(1);
assert bucket2.name.equals("adrianjbosstest2");
Date expectedDate2 = new DateTime("2009-03-12T02:00:09.000Z").toDate();
Date date2 = bucket2.creationDate;
assert date2.toString().equals(expectedDate2.toString());
assert buckets.size() == 2;
}
private ListAllMyBucketsResponse runAmazonParseListAllMyBuckets()
throws IOException {
ListAllMyBucketsResponse response = new ListAllMyBucketsResponse(
new MockHttpURLConnection(listAllMyBucketsResult));
return response;
}
public void testAmazonCanParseListBucketResult() throws IOException {
ListBucketResponse response = runAmazonParseListBucketResult();
ListEntry content = (ListEntry) response.entries.get(0);
assert content.key.equals("3366");
assert content.lastModified.equals(new DateTime(
"2009-03-12T02:00:13.000Z").toDate());
assert content.eTag.equals("\"9d7bb64e8e18ee34eec06dd2cf37b766\"");
assert content.size == 136;
assert content.owner.id
.equals("e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0");
assert content.owner.displayName.equals("ferncam");
assert content.storageClass.equals("STANDARD");
}
private ListBucketResponse runAmazonParseListBucketResult()
throws IOException {
ListBucketResponse response = new ListBucketResponse(
new MockHttpURLConnection(listBucketResult));
return response;
}
@Test
void testAmazonParseListBucketResultSerialResponseTime() throws IOException {
for (int i = 0; i < LOOP_COUNT; i++)
runAmazonParseListBucketResult();
}
@Test
void testAmazonParseListBucketResultParallelResponseTime()
throws InterruptedException, ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(
exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() throws IOException {
runAmazonParseListBucketResult();
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++)
assert completer.take().get();
}
}

View File

@ -0,0 +1,67 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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 com.amazon.s3;
import org.testng.annotations.Test;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "s3.S3UtilsTest", groups = "performance")
public class S3UtilsTest extends org.jclouds.aws.s3.S3UtilsTest {
@Test(dataProvider = "hmacsha1")
void testAmazonSampleDigestSerialResponseTime(byte[] key, String message, String base64Digest) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException {
for (int i = 0; i < 10000; i++)
testAmazonSampleDigest(key, message, base64Digest);
}
@Test(dataProvider = "hmacsha1")
public void testAmazonSampleDigest(byte[] key, String message, String base64Digest) {
String encoded = Utils.encode(new String(key), message, false);
assert encoded.equals(base64Digest);
}
@Test(dataProvider = "hmacsha1")
void testAmazonSampleDigestParallelResponseTime(final byte[] key, final String message, final String base64Digest) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException, InterruptedException, ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(exec);
for (int i = 0; i < 10000; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() {
try {
testAmazonSampleDigest(key, message, base64Digest);
return true;
} catch (Exception e) {
return false;
}
}
});
for (int i = 0; i < 10000; i++) assert completer.take().get();
}
}

91
s3/pom.xml Normal file
View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
====================================================================
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.html
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">
<parent>
<groupId>org.jclouds</groupId>
<artifactId>project</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../project/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-s3</artifactId>
<name>Amazon S3 Components Core</name>
<packaging>jar</packaging>
<description>Core components to access Amazon S3</description>
<scm>
<connection>scm:svn:http://jclouds.googlecode.com/svn/trunk/s3</connection>
<developerConnection>scm:svn:https://jclouds.googlecode.com/svn/trunk/s3</developerConnection>
<url>http://jclouds.googlecode.com/svn/trunk/s3</url>
</scm>
<properties>
<jclouds.aws.accesskeyid></jclouds.aws.accesskeyid>
<jclouds.aws.secretaccesskey></jclouds.aws.secretaccesskey>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>bouncycastle</groupId>
<artifactId>bcprov-jdk15</artifactId>
<version>140</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemProperties>
<property>
<name>jclouds.aws.accesskeyid</name>
<value>${jclouds.aws.accesskeyid}</value>
</property>
<property>
<name>jclouds.aws.secretaccesskey</name>
<value>${jclouds.aws.secretaccesskey}</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,49 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
public class DateService {
private DateTimeFormatter headerDateFormat = DateTimeFormat.forPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'");
private DateTimeFormatter dataDateFormat = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
public DateTime dateTimeFromXMLFormat(String toParse) {
//return dataDateFormat.parseDateTime(toParse);
return new DateTime(toParse);
}
public DateTime dateTimeFromHeaderFormat(String toParse) {
return headerDateFormat.parseDateTime(toParse);
}
public String timestampAsHeaderString() {
return headerDateFormat.print(new DateTime(DateTimeZone.UTC));
}
}

View File

@ -0,0 +1,135 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.Future;
import org.jclouds.aws.s3.commands.CopyObject;
import org.jclouds.aws.s3.commands.DeleteBucket;
import org.jclouds.aws.s3.commands.DeleteObject;
import org.jclouds.aws.s3.commands.HeadBucket;
import org.jclouds.aws.s3.commands.ListAllMyBuckets;
import org.jclouds.aws.s3.commands.ListBucket;
import org.jclouds.aws.s3.commands.PutBucket;
import org.jclouds.aws.s3.commands.PutObject;
import org.jclouds.aws.s3.commands.RetrieveObject;
import org.jclouds.aws.s3.commands.S3CommandFactory;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.http.HttpFutureCommandClient;
import com.google.inject.Inject;
/**
* Non-blocking interface to Amazon S3.
*
* @author Adrian Cole
*/
public class LiveS3Connection implements S3Connection {
/**
* not all clients are threadsafe, but this connection needs to be.
*/
private final HttpFutureCommandClient client;
private final S3CommandFactory factory;
@Inject
public LiveS3Connection(HttpFutureCommandClient client,
S3CommandFactory factory) {
this.client = client;
this.factory = factory;
}
public Future<S3Object> getObject(S3Bucket s3Bucket, String key) {
RetrieveObject getRequestObject = factory.createRetrieveObject(
s3Bucket, key, true);
client.submit(getRequestObject);
return getRequestObject;
}
public Future<S3Object> headObject(S3Bucket s3Bucket, String key) {
RetrieveObject getRequestObject = factory.createRetrieveObject(
s3Bucket, key, false);
client.submit(getRequestObject);
return getRequestObject;
}
public Future<Boolean> deleteObject(S3Bucket s3Bucket, String key) {
DeleteObject deleteObject = factory.createDeleteObject(s3Bucket, key);
client.submit(deleteObject);
return deleteObject;
}
public Future<String> addObject(S3Bucket s3Bucket, S3Object object) {
PutObject putObject = factory.createPutObject(s3Bucket, object);
client.submit(putObject);
return putObject;
}
public Future<Boolean> createBucketIfNotExists(S3Bucket s3Bucket) {
PutBucket putBucket = factory.createPutBucket(s3Bucket);
client.submit(putBucket);
return putBucket;
}
public Future<Boolean> deleteBucket(S3Bucket s3Bucket) {
DeleteBucket deleteBucket = factory.createDeleteBucket(s3Bucket);
client.submit(deleteBucket);
return deleteBucket;
}
public Future<Boolean> copyObject(S3Bucket sourceBucket,
S3Object sourceObject, S3Bucket destinationBucket,
S3Object destinationObject) {
CopyObject copy = factory.createCopyObject(sourceBucket, sourceObject,
destinationBucket, destinationObject);
client.submit(copy);
return copy;
}
public Future<Boolean> bucketExists(S3Bucket s3Bucket) {
HeadBucket headRequestObject = factory.createHeadBucket(s3Bucket);
client.submit(headRequestObject);
return headRequestObject;
}
public Future<S3Bucket> getBucket(S3Bucket s3Bucket) {
ListBucket listRequest = factory.createListBucket(s3Bucket);
client.submit(listRequest);
return listRequest;
}
public Future<List<S3Bucket>> getBuckets() {
ListAllMyBuckets listRequest = factory.createListAllMyBuckets();
client.submit(listRequest);
return listRequest;
}
public void close() throws IOException {
client.close();
}
}

View File

@ -0,0 +1,61 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.S3Bucket;
import java.io.IOException;
import java.io.Closeable;
import java.util.List;
import java.util.concurrent.Future;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public interface S3Connection extends Closeable {
Future<S3Object> getObject(S3Bucket s3Bucket, String key);
Future<S3Object> headObject(S3Bucket s3Bucket, String key);
Future<Boolean> deleteObject(S3Bucket s3Bucket, String key);
Future<String> addObject(S3Bucket s3Bucket, S3Object object);
Future<Boolean> createBucketIfNotExists(S3Bucket s3Bucket);
Future<Boolean> deleteBucket(S3Bucket s3Bucket);
Future<Boolean> copyObject(S3Bucket sourceBucket, S3Object sourceObject, S3Bucket destinationBucket, S3Object destinationObject);
Future<Boolean> bucketExists(S3Bucket s3Bucket);
Future<S3Bucket> getBucket(S3Bucket s3Bucket);
Future<List<S3Bucket>> getBuckets();
public void close() throws IOException;
}

View File

@ -0,0 +1,116 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.name.Names;
import org.jclouds.http.config.JavaUrlHttpFutureCommandClientModule;
import java.util.Properties;
/**
* // TODO: Adrian: Document return getConnection!
*
* @author Adrian Cole
*/
public class S3ConnectionFactory {
public static final Properties DEFAULT_PROPERTIES;
static {
DEFAULT_PROPERTIES = new Properties();
DEFAULT_PROPERTIES.setProperty("jclouds.http.address", "s3.amazonaws.com");
DEFAULT_PROPERTIES.setProperty("jclouds.http.port", "443");
DEFAULT_PROPERTIES.setProperty("jclouds.http.secure", "true");
DEFAULT_PROPERTIES.setProperty("jclouds.http.pool.max_connection_reuse", "75");
DEFAULT_PROPERTIES.setProperty("jclouds.http.pool.max_session_failures", "2");
DEFAULT_PROPERTIES.setProperty("jclouds.http.pool.request_invoker_threads", "1");
DEFAULT_PROPERTIES.setProperty("jclouds.http.pool.io_worker_threads", "2");
DEFAULT_PROPERTIES.setProperty("jclouds.pool.max_connections", "12");
}
public static S3Connection getConnection(String awsAccessKeyId, String awsSecretAccessKey) {
Properties properties = new Properties(DEFAULT_PROPERTIES);
properties.setProperty("jclouds.aws.accesskeyid", awsAccessKeyId);
properties.setProperty("jclouds.aws.secretaccesskey", awsSecretAccessKey);
return getConnection(properties, new JavaUrlHttpFutureCommandClientModule());
}
public static S3Connection getConnection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure) {
Properties properties = new Properties(DEFAULT_PROPERTIES);
properties.setProperty("jclouds.aws.accesskeyid", awsAccessKeyId);
properties.setProperty("jclouds.aws.secretaccesskey", awsSecretAccessKey);
properties.setProperty("jclouds.http.secure", Boolean.toString(isSecure));
if (!isSecure)
properties.setProperty("jclouds.http.port", "80");
return getConnection(properties, new JavaUrlHttpFutureCommandClientModule());
}
public static S3Connection getConnection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure,
String server) {
Properties properties = new Properties(DEFAULT_PROPERTIES);
properties.setProperty("jclouds.aws.accesskeyid", awsAccessKeyId);
properties.setProperty("jclouds.aws.secretaccesskey", awsSecretAccessKey);
properties.setProperty("jclouds.http.secure", Boolean.toString(isSecure));
properties.setProperty("jclouds.http.address", server);
if (!isSecure)
properties.setProperty("jclouds.http.port", "80");
return getConnection(properties, new JavaUrlHttpFutureCommandClientModule());
}
public static S3Connection getConnection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure,
String server, int port) {
Properties properties = new Properties(DEFAULT_PROPERTIES);
properties.setProperty("jclouds.aws.accesskeyid", awsAccessKeyId);
properties.setProperty("jclouds.aws.secretaccesskey", awsSecretAccessKey);
properties.setProperty("jclouds.http.secure", Boolean.toString(isSecure));
properties.setProperty("jclouds.http.address", server);
properties.setProperty("jclouds.http.port", port + "");
return getConnection(properties, new JavaUrlHttpFutureCommandClientModule());
}
/**
* Create a new interface to interact with S3 with the given credential and connection
* parameters
*/
public static synchronized S3Connection getConnection(final Properties properties, Module httpModule) {
return getInjector(properties, httpModule).getInstance(S3Connection.class);
}
/**
* Create a new interface to interact with S3 with the given credential and connection
* parameters
*/
public static synchronized Injector getInjector(final Properties properties, Module httpModule) {
return Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
Names.bindProperties(binder(), properties);
}
}, httpModule, new S3ConnectionModule());
}
}

View File

@ -0,0 +1,59 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.Scopes;
import org.jclouds.aws.s3.commands.config.S3CommandsModule;
import org.jclouds.aws.s3.filters.RemoveTransferEncodingHeader;
import org.jclouds.aws.s3.filters.RequestAuthorizeSignature;
import org.jclouds.http.HttpRequestFilter;
import java.util.ArrayList;
import java.util.List;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class S3ConnectionModule extends AbstractModule {
protected void configure() {
install(new S3CommandsModule());
bind(S3Connection.class).to(LiveS3Connection.class).in(Scopes.SINGLETON);
}
@Provides
@Singleton
List<HttpRequestFilter> provideRequestFilters(RemoveTransferEncodingHeader removTransferEncodingHeader, RequestAuthorizeSignature requestAuthorizeSignature) {
List<HttpRequestFilter> filters = new ArrayList<HttpRequestFilter>();
filters.add(removTransferEncodingHeader);
filters.add(requestAuthorizeSignature);
return filters;
}
}

View File

@ -0,0 +1,35 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3;
import org.jclouds.http.HttpConstants;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class S3Constants extends HttpConstants {
public static final String AUTH = "Authorization";
}

View File

@ -0,0 +1,160 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3;
import com.google.inject.Inject;
import org.jclouds.Logger;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.S3Bucket;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Future;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
public class S3ObjectMap implements ConcurrentMap<String, Object> {
private Logger logger;
private S3Connection connection;
private S3Bucket bucket;
private S3Utils utils;
@Inject
public S3ObjectMap(java.util.logging.Logger logger, S3Connection connection, S3Bucket bucket, S3Utils utils) {
this.logger = new Logger(logger);
this.connection = connection;
this.bucket = bucket;
this.utils = utils;
}
public Object putIfAbsent(String s, Object o) {
return null; // TODO: Adrian: Customise this generated block
}
public boolean remove(Object o, Object o1) {
return false; // TODO: Adrian: Customise this generated block
}
public boolean replace(String s, Object o, Object o1) {
return false; // TODO: Adrian: Customise this generated block
}
public Object replace(String s, Object o) {
return null; // TODO: Adrian: Customise this generated block
}
public int size() {
try {
bucket = connection.getBucket(bucket).get();
return bucket.getContents().size();
} catch (Exception e) {
S3Utils.<S3RuntimeException>rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException("Error clearing bucket" + bucket, e);
}
}
public boolean isEmpty() {
return false; // TODO: Adrian: Customise this generated block
}
public boolean containsKey(Object o) {
return false; // TODO: Adrian: Customise this generated block
}
public boolean containsValue(Object o) {
return false; // TODO: Adrian: Customise this generated block
}
public Object get(Object o) {
try {
return connection.getObject(bucket, o.toString()).get();
} catch (Exception e) {
S3Utils.<S3RuntimeException>rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException(String.format("Error geting object %1s:%2s", bucket, o), e);
}
}
public Object put(String s, Object o) {
S3Object object = new S3Object();
try {
object.setKey(s);
object.setContent(o);
return connection.addObject(bucket, object).get();
} catch (Exception e) {
S3Utils.<S3RuntimeException>rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException(String.format("Error adding object %1s:%2s", bucket, object), e);
}
}
public Object remove(Object o) {
return null; // TODO: Adrian: Customise this generated block
}
public void putAll(Map<? extends String, ? extends Object> map) {
// TODO: Adrian: Customise this generated block
}
private class S3RuntimeException extends RuntimeException {
public S3RuntimeException(String s) {
super(s); // TODO: Adrian: Customise this generated block
}
public S3RuntimeException(String s, Throwable throwable) {
super(s, throwable); // TODO: Adrian: Customise this generated block
}
}
public void clear() {
try {
bucket = connection.getBucket(bucket).get();
List<Future<Boolean>> deletes = new ArrayList<Future<Boolean>>();
for (S3Object object : bucket.getContents()) {
deletes.add(connection.deleteObject(bucket, object.getKey()));
}
for (Future<Boolean> isdeleted : deletes)
if (!isdeleted.get()) {
throw new S3RuntimeException("failed to delete entry");
}
} catch (Exception e) {
S3Utils.<S3RuntimeException>rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException("Error clearing bucket" + bucket, e);
}
}
public Set<String> keySet() {
return null; // TODO: Adrian: Customise this generated block
}
public Collection<Object> values() {
return null; // TODO: Adrian: Customise this generated block
}
public Set<Entry<String, Object>> entrySet() {
return null; // TODO: Adrian: Customise this generated block
}
}

View File

@ -0,0 +1,67 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Base64;
import org.jclouds.Utils;
import org.jclouds.aws.s3.domain.S3Object;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
public class S3Utils extends Utils {
public static String digest(String toEncode, byte[] key) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException {
HMac hmac = new HMac(new SHA1Digest());
byte[] resBuf = new byte[hmac.getMacSize()];
byte[] plainBytes = toEncode.getBytes();
byte[] keyBytes = key;
hmac.init(new KeyParameter(keyBytes));
hmac.update(plainBytes, 0, plainBytes.length);
hmac.doFinal(resBuf, 0);
return new String(Base64.encode(resBuf));
}
public static String getContentAsStringAndClose(S3Object object) throws IOException {
Object o = object.getContent();
if (o instanceof InputStream) {
String returnVal = toStringAndClose((InputStream) o);
if (object.getContentType().indexOf("xml") >= 0) {
}
return returnVal;
} else {
throw new IllegalArgumentException("Object type not supported: " + o.getClass().getName());
}
}
}

View File

@ -0,0 +1,45 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3.commands;
import org.jclouds.aws.s3.commands.callables.CopyObjectCallable;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.http.HttpFutureCommand;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.name.Named;
public class CopyObject extends HttpFutureCommand<Boolean> {
@Inject
public CopyObject(@Named("jclouds.http.address") String amazonHost, CopyObjectCallable callable, @Assisted("sourceBucket") S3Bucket sourceBucket, @Assisted("sourceObject") S3Object sourceObject, @Assisted("destinationBucket") S3Bucket destinationBucket, @Assisted("destinationObject") S3Object destinationObject) {
super("PUT", "/" + destinationObject.getKey(), callable);
getRequest().getHeaders().put("Host",
destinationBucket.getName() + "." + amazonHost);
getRequest().getHeaders().put("x-amz-copy-source", String.format("/%1s/%2s", sourceBucket.getName(), sourceObject.getKey()));
}
}

View File

@ -0,0 +1,43 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3.commands;
import org.jclouds.aws.s3.commands.callables.DeleteBucketCallable;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.http.HttpFutureCommand;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.name.Named;
public class DeleteBucket extends HttpFutureCommand<Boolean> {
@Inject
public DeleteBucket(@Named("jclouds.http.address") String amazonHost,
DeleteBucketCallable callable, @Assisted S3Bucket s3Bucket) {
super("DELETE", "/", callable);
getRequest().getHeaders().put("Host",
s3Bucket.getName() + "." + amazonHost);
}
}

View File

@ -0,0 +1,44 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3.commands;
import org.jclouds.aws.s3.commands.callables.DeleteCallable;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.http.HttpFutureCommand;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.name.Named;
public class DeleteObject extends HttpFutureCommand<Boolean> {
@Inject
public DeleteObject(@Named("jclouds.http.address") String amazonHost,
DeleteCallable callable, @Assisted S3Bucket s3Bucket,
@Assisted String key) {
super("DELETE", "/" + key, callable);
getRequest().getHeaders().put("Host",
s3Bucket.getName() + "." + amazonHost);
}
}

View File

@ -0,0 +1,43 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adriancole@jclouds.org>
*
* ====================================================================
* 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.jclouds.aws.s3.commands;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.http.HttpFutureCommand;
import org.jclouds.http.commands.callables.ReturnTrueIf200;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.name.Named;
public class HeadBucket extends HttpFutureCommand<Boolean> {
@Inject
public HeadBucket(@Named("jclouds.http.address") String amazonHost,
ReturnTrueIf200 callable, @Assisted S3Bucket s3Bucket) {
super("HEAD", "/", callable);
getRequest().getHeaders().put("Host",
s3Bucket.getName() + "." + amazonHost);
}
}

Some files were not shown because too many files have changed in this diff Show More