diff --git a/README.md b/README.md index 68c9db99c743..2baf37e639ad 100644 --- a/README.md +++ b/README.md @@ -56,16 +56,16 @@ See the ``gcloud-java`` API [datastore documentation][datastore-api] to learn ho with the Cloud Datastore using this Client Library. ```java -import com.google.gcloud.datastore.DatastoreService; -import com.google.gcloud.datastore.DatastoreServiceFactory; -import com.google.gcloud.datastore.DatastoreServiceOptions; +import com.google.gcloud.datastore.Datastore; +import com.google.gcloud.datastore.DatastoreFactory; +import com.google.gcloud.datastore.DatastoreOptions; import com.google.gcloud.datastore.DateTime; import com.google.gcloud.datastore.Entity; import com.google.gcloud.datastore.Key; import com.google.gcloud.datastore.KeyFactory; -DatastoreServiceOptions options = DatastoreServiceOptions.builder().projectId(PROJECT_ID).build(); -DatastoreService datastore = DatastoreServiceFactory.instance().get(options); +DatastoreOptions options = DatastoreOptions.builder().projectId(PROJECT_ID).build(); +Datastore datastore = DatastoreFactory.instance().get(options); KeyFactory keyFactory = datastore.newKeyFactory().kind(KIND); Key key = keyFactory.newKey(keyName); Entity entity = datastore.get(key); diff --git a/gcloud-java-core/pom.xml b/gcloud-java-core/pom.xml index 78baf824c080..fa2e1c18972f 100644 --- a/gcloud-java-core/pom.xml +++ b/gcloud-java-core/pom.xml @@ -28,13 +28,13 @@ com.google.http-client google-http-client - 1.19.0 + 1.20.0 compile com.google.oauth-client google-oauth-client - 1.19.0 + 1.20.0 compile diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java b/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java index 839da54e62cf..6cdb737ddd91 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java +++ b/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java @@ -62,7 +62,7 @@ private Object readResolve() throws ObjectStreamException { } } - private static class ServiceAccountAuthCredentials extends AuthCredentials { + public static class ServiceAccountAuthCredentials extends AuthCredentials { private static final long serialVersionUID = 8007708734318445901L; private final String account; @@ -94,6 +94,14 @@ protected HttpRequestInitializer httpRequestInitializer( return builder.build(); } + public String account() { + return account; + } + + public PrivateKey privateKey() { + return privateKey; + } + @Override public int hashCode() { return Objects.hash(account, privateKey); @@ -187,7 +195,7 @@ public static AuthCredentials createApplicationDefaults() throws IOException { return new ApplicationDefaultAuthCredentials(); } - public static AuthCredentials createFor(String account, PrivateKey privateKey) { + public static ServiceAccountAuthCredentials createFor(String account, PrivateKey privateKey) { return new ServiceAccountAuthCredentials(account, privateKey); } diff --git a/gcloud-java-datastore/README.md b/gcloud-java-datastore/README.md index 96d8075706e9..7113dbdd0231 100644 --- a/gcloud-java-datastore/README.md +++ b/gcloud-java-datastore/README.md @@ -40,16 +40,16 @@ See the ``gcloud-java`` API [datastore documentation][datastore-api] to learn ho with the Cloud Datastore using this Client Library. ```java -import com.google.gcloud.datastore.DatastoreService; -import com.google.gcloud.datastore.DatastoreServiceFactory; -import com.google.gcloud.datastore.DatastoreServiceOptions; +import com.google.gcloud.datastore.Datastore; +import com.google.gcloud.datastore.DatastoreFactory; +import com.google.gcloud.datastore.DatastoreOptions; import com.google.gcloud.datastore.DateTime; import com.google.gcloud.datastore.Entity; import com.google.gcloud.datastore.Key; import com.google.gcloud.datastore.KeyFactory; -DatastoreServiceOptions options = DatastoreServiceOptions.builder().projectId(PROJECT_ID).build(); -DatastoreService datastore = DatastoreServiceFactory.instance().get(options); +DatastoreOptions options = DatastoreOptions.builder().projectId(PROJECT_ID).build(); +Datastore datastore = DatastoreFactory.instance().get(options); KeyFactory keyFactory = datastore.newKeyFactory().kind(KIND); Key key = keyFactory.newKey(keyName); Entity entity = datastore.get(key); diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseDatastoreBatchWriter.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseDatastoreBatchWriter.java index 655b1a32125c..7eaf5c535f26 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseDatastoreBatchWriter.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseDatastoreBatchWriter.java @@ -195,8 +195,8 @@ protected void validateActive() { } } - protected DatastoreServiceException newInvalidRequest(String msg, Object... params) { - return DatastoreServiceException.throwInvalidRequest(String.format(msg, params)); + protected DatastoreException newInvalidRequest(String msg, Object... params) { + return DatastoreException.throwInvalidRequest(String.format(msg, params)); } protected DatastoreV1.Mutation.Builder toMutationPb() { @@ -219,5 +219,5 @@ protected DatastoreV1.Mutation.Builder toMutationPb() { return mutationPb; } - protected abstract DatastoreService datastore(); + protected abstract Datastore datastore(); } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseEntity.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseEntity.java index 74417f9bc756..21e20b33a8ed 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseEntity.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseEntity.java @@ -242,13 +242,13 @@ public boolean contains(String name) { /** * Returns the {@link Value} for the given property {@code name}. * - * @throws DatastoreServiceException if not such property. + * @throws DatastoreException if not such property. */ public > V getValue(String name) { @SuppressWarnings("unchecked") V property = (V) properties.get(name); if (property == null) { - throw DatastoreServiceException.throwInvalidRequest("No such property %s", name); + throw DatastoreException.throwInvalidRequest("No such property %s", name); } return property; } @@ -256,7 +256,7 @@ public > V getValue(String name) { /** * Returns true if property is instanceof NullValue. * - * @throws DatastoreServiceException if not such property. + * @throws DatastoreException if not such property. */ public boolean isNull(String name) { return getValue(name) instanceof NullValue; @@ -266,7 +266,7 @@ public boolean isNull(String name) { /** * Returns the property value as a string. * - * @throws DatastoreServiceException if not such property. + * @throws DatastoreException if not such property. * @throws ClassCastException if value is not a string. */ @SuppressWarnings("unchecked") @@ -277,7 +277,7 @@ public String getString(String name) { /** * Returns the property value as long. * - * @throws DatastoreServiceException if not such property. + * @throws DatastoreException if not such property. * @throws ClassCastException if value is not a long. */ @SuppressWarnings("unchecked") @@ -288,7 +288,7 @@ public long getLong(String name) { /** * Returns the property value as a double. * - * @throws DatastoreServiceException if not such property. + * @throws DatastoreException if not such property. * @throws ClassCastException if value is not a double. */ @SuppressWarnings("unchecked") @@ -299,7 +299,7 @@ public double getDouble(String name) { /** * Returns the property value as a boolean. * - * @throws DatastoreServiceException if not such property. + * @throws DatastoreException if not such property. * @throws ClassCastException if value is not a boolean. */ @SuppressWarnings("unchecked") @@ -310,7 +310,7 @@ public boolean getBoolean(String name) { /** * Returns the property value as a DateTime. * - * @throws DatastoreServiceException if not such property. + * @throws DatastoreException if not such property. * @throws ClassCastException if value is not a DateTime. */ @SuppressWarnings("unchecked") @@ -321,7 +321,7 @@ public DateTime getDateTime(String name) { /** * Returns the property value as a Key. * - * @throws DatastoreServiceException if not such property. + * @throws DatastoreException if not such property. * @throws ClassCastException if value is not a Key. */ @SuppressWarnings("unchecked") @@ -332,7 +332,7 @@ public Key getKey(String name) { /** * Returns the property value as an entity. * - * @throws DatastoreServiceException if not such property. + * @throws DatastoreException if not such property. * @throws ClassCastException if value is not an entity. */ @SuppressWarnings("unchecked") @@ -343,7 +343,7 @@ public FullEntity getEntity(String name) { /** * Returns the property value as a list of values. * - * @throws DatastoreServiceException if not such property. + * @throws DatastoreException if not such property. * @throws ClassCastException if value is not a list of values. */ @SuppressWarnings("unchecked") @@ -354,7 +354,7 @@ public List> getList(String name) { /** * Returns the property value as a blob. * - * @throws DatastoreServiceException if not such property. + * @throws DatastoreException if not such property. * @throws ClassCastException if value is not a blob. */ @SuppressWarnings("unchecked") diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Batch.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Batch.java index f74ccc288808..75a5d1381403 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Batch.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Batch.java @@ -43,12 +43,12 @@ interface Response { /** * Submit the batch to the Datastore. * - * @throws DatastoreServiceException if there was any failure or if batch is not longer active + * @throws DatastoreException if there was any failure or if batch is not longer active */ Response submit(); /** - * Returns the batch associated {@link DatastoreService}. + * Returns the batch associated {@link Datastore}. */ - DatastoreService datastore(); + Datastore datastore(); } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BatchImpl.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BatchImpl.java index 0d7eb704d1eb..9c95949e2c8a 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BatchImpl.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BatchImpl.java @@ -27,7 +27,7 @@ class BatchImpl extends BaseDatastoreBatchWriter implements Batch { - private final DatastoreServiceImpl datastore; + private final DatastoreImpl datastore; private final boolean force; static class ResponseImpl implements Batch.Response { @@ -49,7 +49,7 @@ public List generatedKeys() { } } - BatchImpl(DatastoreServiceImpl datastore, BatchOption... options) { + BatchImpl(DatastoreImpl datastore, BatchOption... options) { super("batch"); this.datastore = datastore; Map, BatchOption> optionsMap = BatchOption.asImmutableMap(options); @@ -76,7 +76,7 @@ public Batch.Response submit() { } @Override - public DatastoreService datastore() { + public Datastore datastore() { return datastore; } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Datastore.java similarity index 80% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreService.java rename to gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Datastore.java index 7d27938fd246..fe79fdf45ff4 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Datastore.java @@ -23,12 +23,12 @@ /** * An interface for Google Cloud Datastore. */ -public interface DatastoreService extends Service, DatastoreReaderWriter { +public interface Datastore extends Service, DatastoreReaderWriter { /** * Returns a new Datastore transaction. * - * @throws DatastoreServiceException upon failure + * @throws DatastoreException upon failure */ Transaction newTransaction(TransactionOption... options); @@ -47,15 +47,15 @@ interface TransactionCallable { /** - * Invokes the callback's {@link DatastoreService.TransactionCallable#run} method with a + * Invokes the callback's {@link Datastore.TransactionCallable#run} method with a * {@link DatastoreReaderWriter} that is associated with a new transaction. * The transaction will be committed upon successful invocation. * Any thrown exception will cause the transaction to rollback and will be propagated - * as a {@link DatastoreServiceException} with the original exception as its root cause. + * as a {@link DatastoreException} with the original exception as its root cause. * * @param callable the callback to call with a newly created transactional readerWriter * @param options the options for the created transaction - * @throws DatastoreServiceException upon failure + * @throws DatastoreException upon failure */ T runInTransaction(TransactionCallable callable, TransactionOption... options); @@ -69,35 +69,35 @@ interface TransactionCallable { * The returned key will have the same information (projectId, kind, namespace and ancestors) * as the given key and will have a newly assigned id. * - * @throws DatastoreServiceException upon failure + * @throws DatastoreException upon failure */ Key allocateId(IncompleteKey key); /** * Returns a list of keys using the allocated ids ordered by the input. * - * @throws DatastoreServiceException upon failure + * @throws DatastoreException upon failure * @see #allocateId(IncompleteKey) */ List allocateId(IncompleteKey... key); /** * {@inheritDoc} - * @throws DatastoreServiceException upon failure + * @throws DatastoreException upon failure */ @Override void update(Entity... entity); /** * {@inheritDoc} - * @throws DatastoreServiceException upon failure + * @throws DatastoreException upon failure */ @Override void put(Entity... entity); /** * {@inheritDoc} - * @throws DatastoreServiceException upon failure + * @throws DatastoreException upon failure */ @Override void delete(Key... key); diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java index 7e1ee6d57128..3a80452349dc 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java @@ -31,7 +31,7 @@ interface DatastoreBatchWriter extends DatastoreWriter { * to submit time. * * @throws IllegalArgumentException if any of the given entities is missing a key - * @throws com.google.gcloud.datastore.DatastoreServiceException if a given entity with a + * @throws DatastoreException if a given entity with a * complete key was already added to this writer or if not active */ void addWithDeferredIdAllocation(FullEntity... entity); @@ -40,7 +40,7 @@ interface DatastoreBatchWriter extends DatastoreWriter { * {@inheritDoc} * For entities with complete keys that were marked for deletion in this writer the operation * will be changed to {@link #put}. - * @throws com.google.gcloud.datastore.DatastoreServiceException if a given entity with the + * @throws DatastoreException if a given entity with the * same complete key was already added to this writer, if writer is not active or * if id allocation for an entity with an incomplete key failed. */ @@ -51,7 +51,7 @@ interface DatastoreBatchWriter extends DatastoreWriter { * {@inheritDoc} * This operation will be converted to {@link #put} operation for entities that were already * added or put in this writer - * @throws com.google.gcloud.datastore.DatastoreServiceException if an entity is marked for + * @throws DatastoreException if an entity is marked for * deletion in this writer or if not active */ @Override @@ -61,7 +61,7 @@ interface DatastoreBatchWriter extends DatastoreWriter { * {@inheritDoc} * This operation will also remove from this batch any prior writes for entities with the same * keys - * @throws com.google.gcloud.datastore.DatastoreServiceException if not active + * @throws DatastoreException if not active */ @Override void delete(Key... key); @@ -69,7 +69,7 @@ interface DatastoreBatchWriter extends DatastoreWriter { /** * {@inheritDoc} * This operation will also remove from this writer any prior writes for the same entities. - * @throws com.google.gcloud.datastore.DatastoreServiceException if not active + * @throws DatastoreException if not active */ @Override void put(Entity... entity); diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreException.java similarity index 76% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java rename to gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreException.java index 0d958005c22a..d91cc2ccd98b 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreException.java @@ -26,7 +26,7 @@ import java.util.HashMap; import java.util.Map; -public class DatastoreServiceException extends RuntimeException { +public class DatastoreException extends RuntimeException { private static final long serialVersionUID = 8170357898917041899L; private static final ImmutableMap REASON_TO_CODE; @@ -82,8 +82,8 @@ public boolean retryable() { return retryable; } - DatastoreServiceException translate(DatastoreRpcException exception, String message) { - return new DatastoreServiceException(this, message, exception); + DatastoreException translate(DatastoreRpcException exception, String message) { + return new DatastoreException(this, message, exception); } } @@ -98,12 +98,12 @@ DatastoreServiceException translate(DatastoreRpcException exception, String mess HTTP_TO_CODE = ImmutableMap.copyOf(httpCodes); } - public DatastoreServiceException(Code code, String message, Exception cause) { + public DatastoreException(Code code, String message, Exception cause) { super(MoreObjects.firstNonNull(message, code.description), cause); this.code = code; } - public DatastoreServiceException(Code code, String message) { + public DatastoreException(Code code, String message) { this(code, message, null); } @@ -114,23 +114,23 @@ public Code code() { return code; } - static DatastoreServiceException translateAndThrow(RetryHelperException ex) { + static DatastoreException translateAndThrow(RetryHelperException ex) { if (ex.getCause() instanceof DatastoreRpcException) { return translateAndThrow((DatastoreRpcException) ex.getCause()); } if (ex instanceof RetryHelper.RetryInterruptedException) { RetryHelper.RetryInterruptedException.propagate(); } - throw new DatastoreServiceException(Code.UNKNOWN, ex.getMessage(), ex); + throw new DatastoreException(Code.UNKNOWN, ex.getMessage(), ex); } /** - * Translate DatastoreException to DatastoreServiceException based on their - * HTTP error codes. This method will always throw a new DatastoreServiceException. + * Translate DatastoreException to DatastoreException based on their + * HTTP error codes. This method will always throw a new DatastoreException. * - * @throws DatastoreServiceException every time + * @throws DatastoreException every time */ - static DatastoreServiceException translateAndThrow(DatastoreRpcException exception) { + static DatastoreException translateAndThrow(DatastoreRpcException exception) { String message = exception.getMessage(); Code code = REASON_TO_CODE.get(exception.reason()); if (code == null) { @@ -140,16 +140,16 @@ static DatastoreServiceException translateAndThrow(DatastoreRpcException excepti } /** - * Throw a DatastoreServiceException with {@code FAILED_PRECONDITION} code and the {@code message} + * Throw a DatastoreException with {@code FAILED_PRECONDITION} code and the {@code message} * in a nested exception. * - * @throws DatastoreServiceException every time + * @throws DatastoreException every time */ - static DatastoreServiceException throwInvalidRequest(String massage, Object... params) { - throw new DatastoreServiceException(Code.FAILED_PRECONDITION, String.format(massage, params)); + static DatastoreException throwInvalidRequest(String massage, Object... params) { + throw new DatastoreException(Code.FAILED_PRECONDITION, String.format(massage, params)); } - static DatastoreServiceException propagateUserException(Exception ex) { - throw new DatastoreServiceException(Code.UNKNOWN, ex.getMessage(), ex); + static DatastoreException propagateUserException(Exception ex) { + throw new DatastoreException(Code.UNKNOWN, ex.getMessage(), ex); } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreFactory.java similarity index 61% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java rename to gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreFactory.java index ce0887792f98..a64fab3715f1 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreFactory.java @@ -18,26 +18,26 @@ /** - * A base class for DatasoreService factories. + * A base class for Datastore factories. */ -public abstract class DatastoreServiceFactory { +public abstract class DatastoreFactory { - private static final DatastoreServiceFactory INSTANCE = new DatastoreServiceFactory() { + private static final DatastoreFactory INSTANCE = new DatastoreFactory() { @Override - public DatastoreService get(DatastoreServiceOptions options) { - return new DatastoreServiceImpl(options); + public Datastore get(DatastoreOptions options) { + return new DatastoreImpl(options); } }; /** * Returns the default factory instance. */ - public static DatastoreServiceFactory instance() { + public static DatastoreFactory instance() { return INSTANCE; } /** - * Returns a {@code DatastoreService} for the given options. + * Returns a {@code Datastore} service for the given options. */ - public abstract DatastoreService get(DatastoreServiceOptions options); + public abstract Datastore get(DatastoreOptions options); } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java index d07db9ece9b2..a74d06642740 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java @@ -34,7 +34,7 @@ private DatastoreHelper() { } - static Key allocateId(DatastoreService service, IncompleteKey key) { + static Key allocateId(Datastore service, IncompleteKey key) { return service.allocateId(new IncompleteKey[]{key}).get(0); } @@ -46,7 +46,7 @@ static Entity add(DatastoreWriter writer, FullEntity entity) { return writer.add(new FullEntity[] {entity}).get(0); } - static KeyFactory newKeyFactory(DatastoreServiceOptions options) { + static KeyFactory newKeyFactory(DatastoreOptions options) { return new KeyFactory(options.projectId(), options.namespace()); } @@ -69,16 +69,16 @@ static List fetch(DatastoreReader reader, Key... keys) { return list; } - static T runInTransaction(DatastoreService datastoreService, - DatastoreService.TransactionCallable callable, TransactionOption... options) { - Transaction transaction = datastoreService.newTransaction(options); + static T runInTransaction(Datastore datastore, + Datastore.TransactionCallable callable, TransactionOption... options) { + Transaction transaction = datastore.newTransaction(options); try { T value = callable.run(transaction); transaction.commit(); return value; } catch (Exception ex) { transaction.rollback(); - throw DatastoreServiceException.propagateUserException(ex); + throw DatastoreException.propagateUserException(ex); } finally { if (transaction.active()) { transaction.rollback(); diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreImpl.java similarity index 95% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java rename to gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreImpl.java index d7712dd0bf15..e848dd5e56c8 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreImpl.java @@ -45,8 +45,8 @@ import java.util.concurrent.Callable; -final class DatastoreServiceImpl extends BaseService - implements DatastoreService { +final class DatastoreImpl extends BaseService + implements Datastore { private static final Interceptor EXCEPTION_HANDLER_INTERCEPTOR = new Interceptor() { @@ -74,7 +74,7 @@ public RetryResult beforeEval(Exception exception) { private final DatastoreRpc datastoreRpc; private final RetryParams retryParams; - DatastoreServiceImpl(DatastoreServiceOptions options) { + DatastoreImpl(DatastoreOptions options) { super(options); this.datastoreRpc = options.datastoreRpc(); retryParams = MoreObjects.firstNonNull(options.retryParams(), RetryParams.noRetries()); @@ -112,7 +112,7 @@ DatastoreV1.RunQueryResponse runQuery(final DatastoreV1.RunQueryRequest requestP } }, retryParams, EXCEPTION_HANDLER); } catch (RetryHelperException e) { - throw DatastoreServiceException.translateAndThrow(e); + throw DatastoreException.translateAndThrow(e); } } @@ -150,7 +150,7 @@ DatastoreV1.AllocateIdsResponse allocateIds(final DatastoreV1.AllocateIdsRequest } }, retryParams, EXCEPTION_HANDLER); } catch (RetryHelperException e) { - throw DatastoreServiceException.translateAndThrow(e); + throw DatastoreException.translateAndThrow(e); } } @@ -181,7 +181,7 @@ public List add(FullEntity... entities) { } if (completeEntity != null) { if (completeEntities.put(completeEntity.key(), completeEntity) != null) { - throw DatastoreServiceException.throwInvalidRequest( + throw DatastoreException.throwInvalidRequest( "Duplicate entity with the key %s", entity.key()); } mutationPb.addInsert(completeEntity.toPb()); @@ -277,7 +277,7 @@ DatastoreV1.LookupResponse lookup(final DatastoreV1.LookupRequest requestPb) { } }, retryParams, EXCEPTION_HANDLER); } catch (RetryHelperException e) { - throw DatastoreServiceException.translateAndThrow(e); + throw DatastoreException.translateAndThrow(e); } } @@ -348,7 +348,7 @@ DatastoreV1.CommitResponse commit(final DatastoreV1.CommitRequest requestPb) { } }, retryParams, EXCEPTION_HANDLER); } catch (RetryHelperException e) { - throw DatastoreServiceException.translateAndThrow(e); + throw DatastoreException.translateAndThrow(e); } } @@ -366,7 +366,7 @@ public DatastoreV1.BeginTransactionResponse call() throws DatastoreRpcException } }, retryParams, EXCEPTION_HANDLER); } catch (RetryHelperException e) { - throw DatastoreServiceException.translateAndThrow(e); + throw DatastoreException.translateAndThrow(e); } } @@ -385,7 +385,7 @@ void rollback(final DatastoreV1.RollbackRequest requestPb) { } }, retryParams, EXCEPTION_HANDLER); } catch (RetryHelperException e) { - throw DatastoreServiceException.translateAndThrow(e); + throw DatastoreException.translateAndThrow(e); } } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreOptions.java similarity index 88% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java rename to gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreOptions.java index e0dba7b7a982..ed6b51458938 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreOptions.java @@ -34,7 +34,7 @@ import java.util.Objects; import java.util.Set; -public class DatastoreServiceOptions extends ServiceOptions { +public class DatastoreOptions extends ServiceOptions { private static final long serialVersionUID = -8636602944160689193L; private static final String DATASET_ENV_NAME = "DATASTORE_DATASET"; @@ -48,7 +48,7 @@ public class DatastoreServiceOptions extends ServiceOptions { + ServiceOptions.Builder { private String namespace; private boolean force; @@ -57,7 +57,7 @@ public static class Builder extends private Builder() { } - private Builder(DatastoreServiceOptions options) { + private Builder(DatastoreOptions options) { super(options); force = options.force; namespace = options.namespace; @@ -65,8 +65,8 @@ private Builder(DatastoreServiceOptions options) { } @Override - public DatastoreServiceOptions build() { - DatastoreServiceOptions options = new DatastoreServiceOptions(this); + public DatastoreOptions build() { + DatastoreOptions options = new DatastoreOptions(this); return normalizeDataset ? options.normalize() : options; } @@ -86,14 +86,14 @@ Builder normalizeDataset(boolean normalizeDataset) { } } - private DatastoreServiceOptions(Builder builder) { + private DatastoreOptions(Builder builder) { super(builder); normalizeDataset = builder.normalizeDataset; namespace = builder.namespace != null ? builder.namespace : defaultNamespace(); force = builder.force; } - private DatastoreServiceOptions normalize() { + private DatastoreOptions normalize() { if (!normalizeDataset) { return this; } @@ -116,9 +116,9 @@ private DatastoreServiceOptions normalize() { key = combinedIter.next().getEntity().getKey(); } builder.projectId(key.getPartitionId().getDatasetId()); - return new DatastoreServiceOptions(builder); + return new DatastoreOptions(builder); } catch (DatastoreRpcException e) { - throw DatastoreServiceException.translateAndThrow(e); + throw DatastoreException.translateAndThrow(e); } } @@ -170,10 +170,10 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (!(obj instanceof DatastoreServiceOptions)) { + if (!(obj instanceof DatastoreOptions)) { return false; } - DatastoreServiceOptions other = (DatastoreServiceOptions) obj; + DatastoreOptions other = (DatastoreOptions) obj; return isEquals(other) && Objects.equals(namespace, other.namespace) && Objects.equals(force, other.force) && Objects.equals(normalizeDataset, other.normalizeDataset); @@ -194,7 +194,7 @@ DatastoreRpc datastoreRpc() { return datastoreRpc; } - public static DatastoreServiceOptions defaultInstance() { + public static DatastoreOptions defaultInstance() { return builder().build(); } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreReader.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreReader.java index b1dc0a70d392..056895f850e3 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreReader.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreReader.java @@ -27,18 +27,18 @@ public interface DatastoreReader { /** * Returns an {@link Entity} for the given {@link Key} or {@code null} if does not exists. * - * @throws DatastoreServiceException upon failure. + * @throws DatastoreException upon failure. */ Entity get(Key key); /** * Returns an {@link Entity} for each given {@link Key} that exists in the Datastore. * The order of the result is unspecified. - * Results are loaded lazily therefore it is possible to get a {@code DatastoreServiceException} + * Results are loaded lazily therefore it is possible to get a {@code DatastoreException} * from the returned {@code Iterator}'s {@link Iterator#hasNext hasNext} or * {@link Iterator#next next} methods. * - * @throws DatastoreServiceException upon failure. + * @throws DatastoreException upon failure. * @see #get(Key) */ Iterator get(Key... key); @@ -53,7 +53,7 @@ public interface DatastoreReader { /** * Submit a {@link Query} and returns its result. * - * @throws DatastoreServiceException upon failure. + * @throws DatastoreException upon failure. */ QueryResults run(Query query); } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java index 806394552419..66ba98aed9e9 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java @@ -30,7 +30,7 @@ public interface DatastoreWriter { * @param entity the entity to add * @return an {@code Entity} with the same properties and a key that is either newly allocated * or the same one if key is already complete - * @throws DatastoreServiceException upon failure + * @throws DatastoreException upon failure * @throws IllegalArgumentException if the given entity is missing a key */ Entity add(FullEntity entity); @@ -41,7 +41,7 @@ public interface DatastoreWriter { * * @return a list of {@code Entity} ordered by input with the same properties and a key that * is either newly allocated or the same one if was already complete - * @throws DatastoreServiceException upon failure + * @throws DatastoreException upon failure * @throws IllegalArgumentException if any of the given entities is missing a key * @see #add(FullEntity) */ diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyFactory.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyFactory.java index fc288d46510f..8010468c5068 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyFactory.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyFactory.java @@ -19,7 +19,7 @@ import com.google.common.collect.ImmutableList; /** - * An helper for creating keys for a specific {@link DatastoreService}, + * An helper for creating keys for a specific {@link Datastore}, * using its associated projectId and namespace. */ public final class KeyFactory extends BaseKey.Builder { diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ListValue.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ListValue.java index a13dbb17cc69..41c7e82788b5 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ListValue.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ListValue.java @@ -88,7 +88,7 @@ public Builder addValue(Value first, Value... other) { @Override public Builder indexed(boolean indexed) { // see issue #26 - throw DatastoreServiceException.throwInvalidRequest("ListValue can't specify index"); + throw DatastoreException.throwInvalidRequest("ListValue can't specify index"); } /** diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResults.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResults.java index 6310eec93215..44360987b573 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResults.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResults.java @@ -22,7 +22,7 @@ * The result of a Google Cloud Datastore query submission. * When result is not typed it is possible to cast it to its appropriate type according to * the {@link #resultClass} value. - * Results are loaded lazily therefore it is possible to get a {@code DatastoreServiceException} + * Results are loaded lazily therefore it is possible to get a {@code DatastoreException} * upon {@link Iterator#hasNext hasNext} or {@link Iterator#next next} calls. * * @param the type of the results value. diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResultsImpl.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResultsImpl.java index f97c9483e002..8e2f294ed15d 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResultsImpl.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResultsImpl.java @@ -27,7 +27,7 @@ class QueryResultsImpl extends AbstractIterator implements QueryResults { - private final DatastoreServiceImpl datastore; + private final DatastoreImpl datastore; private final DatastoreV1.ReadOptions readOptionsPb; private final DatastoreV1.PartitionId partitionIdPb; private final ResultType queryResultType; @@ -39,7 +39,7 @@ class QueryResultsImpl extends AbstractIterator implements QueryResults //private ByteString cursor; // only available in v1beta3 - QueryResultsImpl(DatastoreServiceImpl datastore, DatastoreV1.ReadOptions readOptionsPb, + QueryResultsImpl(DatastoreImpl datastore, DatastoreV1.ReadOptions readOptionsPb, Query query) { this.datastore = datastore; this.readOptionsPb = readOptionsPb; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Transaction.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Transaction.java index 6ac1da78ab75..9d676bc68a8c 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Transaction.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Transaction.java @@ -62,7 +62,7 @@ interface Response { * to fail if entity was changed by others after it was seen by this transaction) but any * write changes in this transaction will not be reflected by the returned entity. * - * @throws DatastoreServiceException upon failure or if no longer active + * @throws DatastoreException upon failure or if no longer active */ @Override Entity get(Key key); @@ -73,7 +73,7 @@ interface Response { * to fail if any of the entities was changed by others after they were seen by this transaction) * but any write changes in this transaction will not be reflected by the returned entities. * - * @throws DatastoreServiceException upon failure or if no longer active + * @throws DatastoreException upon failure or if no longer active */ @Override Iterator get(Key... key); @@ -84,7 +84,7 @@ interface Response { * to fail if any of the entities was changed by others after they were seen by this transaction) * but any write changes in this transaction will not be reflected by the returned entities. * - * @throws DatastoreServiceException upon failure or if no longer active + * @throws DatastoreException upon failure or if no longer active */ @Override List fetch(Key... keys); @@ -96,7 +96,7 @@ interface Response { * query was performed) but any write changes in this transaction will not be reflected by * the result. * - * @throws DatastoreServiceException upon failure or if no longer active + * @throws DatastoreException upon failure or if no longer active */ @Override QueryResults run(Query query); @@ -104,14 +104,14 @@ interface Response { /** * Commit the transaction. * - * @throws DatastoreServiceException if could not commit the transaction or if no longer active + * @throws DatastoreException if could not commit the transaction or if no longer active */ Response commit(); /** * Rollback the transaction. * - * @throws DatastoreServiceException if transaction was already committed + * @throws DatastoreException if transaction was already committed */ void rollback(); @@ -122,7 +122,7 @@ interface Response { boolean active(); /** - * Returns the transaction associated {@link DatastoreService}. + * Returns the transaction associated {@link Datastore}. */ - DatastoreService datastore(); + Datastore datastore(); } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/TransactionImpl.java index 6f1090945ee1..48568650910d 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/TransactionImpl.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/TransactionImpl.java @@ -29,7 +29,7 @@ final class TransactionImpl extends BaseDatastoreBatchWriter implements Transaction { - private final DatastoreServiceImpl datastore; + private final DatastoreImpl datastore; private final ByteString transaction; private final boolean force; private boolean rolledback; @@ -53,7 +53,7 @@ public List generatedKeys() { } } - TransactionImpl(DatastoreServiceImpl datastore, TransactionOption... options) { + TransactionImpl(DatastoreImpl datastore, TransactionOption... options) { super("transaction"); this.datastore = datastore; DatastoreV1.BeginTransactionRequest.Builder requestPb = @@ -124,7 +124,7 @@ public void rollback() { } @Override - public DatastoreService datastore() { + public Datastore datastore() { return datastore; } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/package-info.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/package-info.java index cf32c5bfa2c2..3b402820e663 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/package-info.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/package-info.java @@ -19,9 +19,8 @@ * *

A simple usage example: *

 {@code
- * DatastoreServiceOptions options =
- *     DatastoreServiceOptions.builder().projectId(PROJECT_ID).build();
- * DatastoreService datastore = DatastoreServiceFactory.instance().get(options);
+ * DatastoreOptions options = DatastoreOptions.builder().projectId(PROJECT_ID).build();
+ * Datastore datastore = DatastoreFactory.instance().get(options);
  * KeyFactory keyFactory = datastore.newKeyFactory().kind(kind);
  * Key key = keyFactory.newKey(keyName);
  * Entity entity = datastore.get(key);
diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/spi/DatastoreRpcFactory.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/spi/DatastoreRpcFactory.java
index 952114788f01..1815dda30f5d 100644
--- a/gcloud-java-datastore/src/main/java/com/google/gcloud/spi/DatastoreRpcFactory.java
+++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/spi/DatastoreRpcFactory.java
@@ -16,13 +16,13 @@
 
 package com.google.gcloud.spi;
 
-import com.google.gcloud.datastore.DatastoreServiceOptions;
+import com.google.gcloud.datastore.DatastoreOptions;
 
 /**
  * An interface for Datastore RPC factory.
  * Implementation will be loaded via {@link java.util.ServiceLoader}.
  */
 public interface DatastoreRpcFactory extends
-    ServiceRpcFactory {
+    ServiceRpcFactory {
 }
 
diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/spi/DefaultDatastoreRpc.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/spi/DefaultDatastoreRpc.java
index 3d909d368bc0..2f245260b325 100644
--- a/gcloud-java-datastore/src/main/java/com/google/gcloud/spi/DefaultDatastoreRpc.java
+++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/spi/DefaultDatastoreRpc.java
@@ -33,7 +33,7 @@
 import com.google.api.services.datastore.client.DatastoreFactory;
 import com.google.api.services.datastore.client.DatastoreOptions.Builder;
 import com.google.common.collect.ImmutableMap;
-import com.google.gcloud.datastore.DatastoreServiceOptions;
+import com.google.gcloud.datastore.DatastoreOptions;
 import com.google.gcloud.spi.DatastoreRpc.DatastoreRpcException.Reason;
 
 import org.json.JSONException;
@@ -61,7 +61,7 @@ public class DefaultDatastoreRpc implements DatastoreRpc {
     HTTP_STATUS_TO_REASON = ImmutableMap.copyOf(httpCodes);
   }
 
-  public DefaultDatastoreRpc(DatastoreServiceOptions options) {
+  public DefaultDatastoreRpc(DatastoreOptions options) {
     client = DatastoreFactory.get().create(
         new Builder()
             .dataset(options.projectId())
diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseDatastoreBatchWriterTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseDatastoreBatchWriterTest.java
index 71576036fe3b..c3f1bfbd5a71 100644
--- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseDatastoreBatchWriterTest.java
+++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseDatastoreBatchWriterTest.java
@@ -1 +1 @@
-/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.gcloud.datastore;

import static org.easymock.EasyMock.*;
import static org.junit.Assert.assertEquals;

import com.google.api.services.datastore.DatastoreV1;
import com.google.common.collect.ImmutableList;
import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

public class BaseDatastoreBatchWriterTest {

  private static final Key KEY1 = Key.builder("dataset1", "kind1", "name1").build();
  private static final Key KEY2 = Key.builder(KEY1, 1).build();
  private static final Key KEY3 = Key.builder(KEY1, 2).build();
  private static final IncompleteKey INCOMPLETE_KEY = IncompleteKey.builder(KEY1).build();
  private static final Entity ENTITY1 = Entity.builder(KEY1).build();
  private static final Entity ENTITY2 = Entity.builder(KEY2).set("bak", true).build();
  private static final Entity ENTITY3 = Entity.builder(KEY3).set("bak", true).build();
  private static final FullEntity INCOMPLETE_ENTITY_1 =
      Entity.builder(INCOMPLETE_KEY).build();
  private static final FullEntity INCOMPLETE_ENTITY_2 =
      Entity.builder(INCOMPLETE_KEY).set("name", "dan").build();

  private DatastoreBatchWriter batchWriter;

  private class DatastoreBatchWriter extends BaseDatastoreBatchWriter {

    private final DatastoreService datastore;

    protected DatastoreBatchWriter() {
      super("test");
      datastore = EasyMock.createMock(DatastoreService.class);
      IncompleteKey[] expected = {INCOMPLETE_KEY, INCOMPLETE_KEY};
      List result = ImmutableList.of(KEY2, KEY3);
      expect(datastore.allocateId(expected)).andReturn(result).times(0, 1);
      replay(datastore);
    }

    @Override
    protected DatastoreService datastore() {
      return datastore;
    }

    void finish() {
      verify(datastore);
    }
  }

  @Before
  public void setUp() {
    batchWriter = new DatastoreBatchWriter();
  }

  @After
  public void tearDown() {
    batchWriter.finish();
  }

  @Test
  public void testAdd() throws Exception {
    Entity entity2 =
        Entity.builder(ENTITY2).key(Key.builder(KEY1).name("name2").build()).build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addInsert(ENTITY1.toPb())
        .addInsert(entity2.toPb())
        .addInsert(Entity.builder(KEY2, INCOMPLETE_ENTITY_1).build().toPb())
        .addInsert(Entity.builder(KEY3, INCOMPLETE_ENTITY_2).build().toPb())
        .build();
    List entities = batchWriter
        .add(ENTITY1, INCOMPLETE_ENTITY_1, INCOMPLETE_ENTITY_2, entity2);
    assertEquals(pb, batchWriter.toMutationPb().build());
    assertEquals(ENTITY1, entities.get(0));
    assertEquals(Entity.builder(KEY2, INCOMPLETE_ENTITY_1).build(), entities.get(1));
    assertEquals(Entity.builder(KEY3, INCOMPLETE_ENTITY_2).build(), entities.get(2));
    assertEquals(entity2, entities.get(3));
  }

  @Test
  public void testAddAfterDelete() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(ENTITY1.toPb())
        .build();
    batchWriter.delete(KEY1);
    batchWriter.add(ENTITY1);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test(expected = DatastoreServiceException.class)
  public void testAddDuplicate() throws Exception {
    batchWriter.add(ENTITY1);
    batchWriter.add(ENTITY1);
  }

  @Test(expected = DatastoreServiceException.class)
  public void testAddAfterPut() throws Exception {
    batchWriter.put(ENTITY1);
    batchWriter.add(ENTITY1);
  }

  @Test(expected = DatastoreServiceException.class)
  public void testAddAfterUpdate() throws Exception {
    batchWriter.update(ENTITY1);
    batchWriter.add(ENTITY1);
  }

  @Test(expected = DatastoreServiceException.class)
  public void testAddWhenNotActive() throws Exception {
    batchWriter.deactivate();
    batchWriter.add(ENTITY1);
  }

  @Test
  public void testAddWithDeferredAllocation() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addInsert(ENTITY1.toPb())
        .addInsertAutoId(INCOMPLETE_ENTITY_1.toPb())
        .addInsertAutoId(INCOMPLETE_ENTITY_2.toPb())
        .build();
    batchWriter.addWithDeferredIdAllocation(ENTITY1, INCOMPLETE_ENTITY_1);
    batchWriter.addWithDeferredIdAllocation(INCOMPLETE_ENTITY_2);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test(expected = DatastoreServiceException.class)
  public void testAddWithDeferredAllocationWhenNotActive() throws Exception {
    batchWriter.deactivate();
    batchWriter.addWithDeferredIdAllocation(INCOMPLETE_ENTITY_1);
  }

  @Test
  public void testUpdate() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpdate(ENTITY1.toPb())
        .addUpdate(ENTITY2.toPb())
        .addUpdate(ENTITY3.toPb())
        .build();
    batchWriter.update(ENTITY1, ENTITY2);
    batchWriter.update(ENTITY3);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testUpdateAfterUpdate() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpdate(entity.toPb())
        .build();
    batchWriter.update(ENTITY1);
    batchWriter.update(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testUpdateAfterAdd() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(entity.toPb())
        .build();
    batchWriter.add(ENTITY1);
    batchWriter.update(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testUpdateAfterPut() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(entity.toPb())
        .build();
    batchWriter.put(ENTITY1);
    batchWriter.update(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test(expected = DatastoreServiceException.class)
  public void testUpdateAfterDelete() throws Exception {
    batchWriter.delete(KEY1);
    batchWriter.update(ENTITY1, ENTITY2);
  }

  @Test(expected = DatastoreServiceException.class)
  public void testUpdateWhenNotActive() throws Exception {
    batchWriter.deactivate();
    batchWriter.update(ENTITY1);
  }

  @Test
  public void testPut() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(ENTITY1.toPb())
        .addUpsert(ENTITY2.toPb())
        .addUpsert(ENTITY3.toPb())
        .build();
    batchWriter.put(ENTITY1, ENTITY2);
    batchWriter.put(ENTITY3);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testPutAfterPut() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(entity.toPb())
        .build();
    batchWriter.put(ENTITY1);
    batchWriter.put(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testPutAfterAdd() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(entity.toPb())
        .build();
    batchWriter.add(ENTITY1);
    batchWriter.put(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testPutAfterUpdate() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(entity.toPb())
        .build();
    batchWriter.update(ENTITY1);
    batchWriter.put(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testPutAfterDelete() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(entity.toPb())
        .build();
    batchWriter.delete(KEY1);
    batchWriter.put(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test(expected = DatastoreServiceException.class)
  public void testPutWhenNotActive() throws Exception {
    batchWriter.deactivate();
    batchWriter.put(ENTITY1);
  }

  @Test
  public void testDelete() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addDelete(KEY1.toPb())
        .addDelete(KEY2.toPb())
        .addDelete(KEY3.toPb())
        .build();
    batchWriter.delete(KEY1, KEY2);
    batchWriter.delete(KEY3);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testDeleteAfterAdd() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addInsertAutoId(INCOMPLETE_ENTITY_1.toPb())
        .addDelete(KEY1.toPb())
        .build();
    batchWriter.add(ENTITY1);
    batchWriter.addWithDeferredIdAllocation(INCOMPLETE_ENTITY_1);
    batchWriter.delete(KEY1);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testDeleteAfterUpdate() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addDelete(KEY1.toPb())
        .build();
    batchWriter.update(ENTITY1);
    batchWriter.delete(KEY1);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testDeleteAfterPut() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addDelete(KEY1.toPb())
        .build();
    batchWriter.put(ENTITY1);
    batchWriter.delete(KEY1);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test(expected = DatastoreServiceException.class)
  public void testDeleteWhenNotActive() throws Exception {
    batchWriter.deactivate();
    batchWriter.delete(KEY1);
  }
}

\ No newline at end of file
+/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.gcloud.datastore;

import static org.easymock.EasyMock.*;
import static org.junit.Assert.assertEquals;

import com.google.api.services.datastore.DatastoreV1;
import com.google.common.collect.ImmutableList;
import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

public class BaseDatastoreBatchWriterTest {

  private static final Key KEY1 = Key.builder("dataset1", "kind1", "name1").build();
  private static final Key KEY2 = Key.builder(KEY1, 1).build();
  private static final Key KEY3 = Key.builder(KEY1, 2).build();
  private static final IncompleteKey INCOMPLETE_KEY = IncompleteKey.builder(KEY1).build();
  private static final Entity ENTITY1 = Entity.builder(KEY1).build();
  private static final Entity ENTITY2 = Entity.builder(KEY2).set("bak", true).build();
  private static final Entity ENTITY3 = Entity.builder(KEY3).set("bak", true).build();
  private static final FullEntity INCOMPLETE_ENTITY_1 =
      Entity.builder(INCOMPLETE_KEY).build();
  private static final FullEntity INCOMPLETE_ENTITY_2 =
      Entity.builder(INCOMPLETE_KEY).set("name", "dan").build();

  private DatastoreBatchWriter batchWriter;

  private class DatastoreBatchWriter extends BaseDatastoreBatchWriter {

    private final Datastore datastore;

    protected DatastoreBatchWriter() {
      super("test");
      datastore = EasyMock.createMock(Datastore.class);
      IncompleteKey[] expected = {INCOMPLETE_KEY, INCOMPLETE_KEY};
      List result = ImmutableList.of(KEY2, KEY3);
      expect(datastore.allocateId(expected)).andReturn(result).times(0, 1);
      replay(datastore);
    }

    @Override
    protected Datastore datastore() {
      return datastore;
    }

    void finish() {
      verify(datastore);
    }
  }

  @Before
  public void setUp() {
    batchWriter = new DatastoreBatchWriter();
  }

  @After
  public void tearDown() {
    batchWriter.finish();
  }

  @Test
  public void testAdd() throws Exception {
    Entity entity2 =
        Entity.builder(ENTITY2).key(Key.builder(KEY1).name("name2").build()).build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addInsert(ENTITY1.toPb())
        .addInsert(entity2.toPb())
        .addInsert(Entity.builder(KEY2, INCOMPLETE_ENTITY_1).build().toPb())
        .addInsert(Entity.builder(KEY3, INCOMPLETE_ENTITY_2).build().toPb())
        .build();
    List entities = batchWriter
        .add(ENTITY1, INCOMPLETE_ENTITY_1, INCOMPLETE_ENTITY_2, entity2);
    assertEquals(pb, batchWriter.toMutationPb().build());
    assertEquals(ENTITY1, entities.get(0));
    assertEquals(Entity.builder(KEY2, INCOMPLETE_ENTITY_1).build(), entities.get(1));
    assertEquals(Entity.builder(KEY3, INCOMPLETE_ENTITY_2).build(), entities.get(2));
    assertEquals(entity2, entities.get(3));
  }

  @Test
  public void testAddAfterDelete() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(ENTITY1.toPb())
        .build();
    batchWriter.delete(KEY1);
    batchWriter.add(ENTITY1);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test(expected = DatastoreException.class)
  public void testAddDuplicate() throws Exception {
    batchWriter.add(ENTITY1);
    batchWriter.add(ENTITY1);
  }

  @Test(expected = DatastoreException.class)
  public void testAddAfterPut() throws Exception {
    batchWriter.put(ENTITY1);
    batchWriter.add(ENTITY1);
  }

  @Test(expected = DatastoreException.class)
  public void testAddAfterUpdate() throws Exception {
    batchWriter.update(ENTITY1);
    batchWriter.add(ENTITY1);
  }

  @Test(expected = DatastoreException.class)
  public void testAddWhenNotActive() throws Exception {
    batchWriter.deactivate();
    batchWriter.add(ENTITY1);
  }

  @Test
  public void testAddWithDeferredAllocation() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addInsert(ENTITY1.toPb())
        .addInsertAutoId(INCOMPLETE_ENTITY_1.toPb())
        .addInsertAutoId(INCOMPLETE_ENTITY_2.toPb())
        .build();
    batchWriter.addWithDeferredIdAllocation(ENTITY1, INCOMPLETE_ENTITY_1);
    batchWriter.addWithDeferredIdAllocation(INCOMPLETE_ENTITY_2);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test(expected = DatastoreException.class)
  public void testAddWithDeferredAllocationWhenNotActive() throws Exception {
    batchWriter.deactivate();
    batchWriter.addWithDeferredIdAllocation(INCOMPLETE_ENTITY_1);
  }

  @Test
  public void testUpdate() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpdate(ENTITY1.toPb())
        .addUpdate(ENTITY2.toPb())
        .addUpdate(ENTITY3.toPb())
        .build();
    batchWriter.update(ENTITY1, ENTITY2);
    batchWriter.update(ENTITY3);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testUpdateAfterUpdate() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpdate(entity.toPb())
        .build();
    batchWriter.update(ENTITY1);
    batchWriter.update(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testUpdateAfterAdd() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(entity.toPb())
        .build();
    batchWriter.add(ENTITY1);
    batchWriter.update(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testUpdateAfterPut() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(entity.toPb())
        .build();
    batchWriter.put(ENTITY1);
    batchWriter.update(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test(expected = DatastoreException.class)
  public void testUpdateAfterDelete() throws Exception {
    batchWriter.delete(KEY1);
    batchWriter.update(ENTITY1, ENTITY2);
  }

  @Test(expected = DatastoreException.class)
  public void testUpdateWhenNotActive() throws Exception {
    batchWriter.deactivate();
    batchWriter.update(ENTITY1);
  }

  @Test
  public void testPut() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(ENTITY1.toPb())
        .addUpsert(ENTITY2.toPb())
        .addUpsert(ENTITY3.toPb())
        .build();
    batchWriter.put(ENTITY1, ENTITY2);
    batchWriter.put(ENTITY3);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testPutAfterPut() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(entity.toPb())
        .build();
    batchWriter.put(ENTITY1);
    batchWriter.put(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testPutAfterAdd() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(entity.toPb())
        .build();
    batchWriter.add(ENTITY1);
    batchWriter.put(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testPutAfterUpdate() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(entity.toPb())
        .build();
    batchWriter.update(ENTITY1);
    batchWriter.put(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testPutAfterDelete() throws Exception {
    Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build();
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addUpsert(entity.toPb())
        .build();
    batchWriter.delete(KEY1);
    batchWriter.put(entity);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test(expected = DatastoreException.class)
  public void testPutWhenNotActive() throws Exception {
    batchWriter.deactivate();
    batchWriter.put(ENTITY1);
  }

  @Test
  public void testDelete() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addDelete(KEY1.toPb())
        .addDelete(KEY2.toPb())
        .addDelete(KEY3.toPb())
        .build();
    batchWriter.delete(KEY1, KEY2);
    batchWriter.delete(KEY3);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testDeleteAfterAdd() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addInsertAutoId(INCOMPLETE_ENTITY_1.toPb())
        .addDelete(KEY1.toPb())
        .build();
    batchWriter.add(ENTITY1);
    batchWriter.addWithDeferredIdAllocation(INCOMPLETE_ENTITY_1);
    batchWriter.delete(KEY1);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testDeleteAfterUpdate() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addDelete(KEY1.toPb())
        .build();
    batchWriter.update(ENTITY1);
    batchWriter.delete(KEY1);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test
  public void testDeleteAfterPut() throws Exception {
    DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder()
        .addDelete(KEY1.toPb())
        .build();
    batchWriter.put(ENTITY1);
    batchWriter.delete(KEY1);
    assertEquals(pb, batchWriter.toMutationPb().build());
  }

  @Test(expected = DatastoreException.class)
  public void testDeleteWhenNotActive() throws Exception {
    batchWriter.deactivate();
    batchWriter.delete(KEY1);
  }
}

\ No newline at end of file
diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseEntityTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseEntityTest.java
index 567e795a66e7..daa0c502d4b5 100644
--- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseEntityTest.java
+++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseEntityTest.java
@@ -1 +1 @@
-/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.gcloud.datastore;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;

import org.junit.Before;
import org.junit.Test;

import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Set;

public class BaseEntityTest {

  private static final Blob BLOB = Blob.copyFrom(new byte[]{1, 2});
  private static final DateTime DATE_TIME = DateTime.now();
  private static final Key KEY = Key.builder("ds1", "k1", "n1").build();
  private static final Entity ENTITY = Entity.builder(KEY).set("name", "foo").build();
  private static final IncompleteKey INCOMPLETE_KEY = IncompleteKey.builder("ds1", "k1").build();
  private static final FullEntity PARTIAL_ENTITY =
      Entity.builder(INCOMPLETE_KEY).build();

  private Builder builder;

  private class Builder extends BaseEntity.Builder {

    @Override public BaseEntity build() {

      return new BaseEntity(this) {

        @Override
        protected Builder emptyBuilder() {
          return new BaseEntityTest.Builder();
        }
      };
    }
  }

  @Before
  public void setUp() {
    builder = new Builder();
    builder.set("blob", BLOB).set("boolean", true).set("dateTime", DATE_TIME);
    builder.set("double", 1.25).set("key", KEY).set("string", "hello world");
    builder.set("long", 125).setNull("null").set("entity", ENTITY);
    builder.set("partialEntity", PARTIAL_ENTITY).set("stringValue", StringValue.of("bla"));
    builder.set("list1", NullValue.of(), StringValue.of("foo"));
    builder.set("list2", ImmutableList.of(LongValue.of(10), DoubleValue.of(2)));
    builder.set("list3", Collections.singletonList(BooleanValue.of(true)));
  }

  @Test
  public void testContains() throws Exception {
    BaseEntity entity = builder.build();
    assertTrue(entity.contains("list1"));
    assertFalse(entity.contains("bla"));
    entity = builder.clear().build();
    assertFalse(entity.contains("list1"));
  }

  @Test
  public void testGetValue() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(BlobValue.of(BLOB), entity.getValue("blob"));
  }

  @Test(expected = DatastoreServiceException.class)
  public void testGetValueNotFound() throws Exception {
    BaseEntity entity = builder.clear().build();
    entity.getValue("blob");
  }

  @Test
  public void testIsNull() throws Exception {
    BaseEntity entity = builder.build();
    assertTrue(entity.isNull("null"));
    assertFalse(entity.isNull("blob"));
    entity = builder.setNull("blob").build();
    assertTrue(entity.isNull("blob"));
  }

  @Test(expected = DatastoreServiceException.class)
  public void testIsNullNotFound() throws Exception {
    BaseEntity entity = builder.clear().build();
    entity.isNull("null");
  }

  @Test
  public void testGetString() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals("hello world", entity.getString("string"));
    assertEquals("bla", entity.getString("stringValue"));
    entity = builder.set("string", "foo").build();
    assertEquals("foo", entity.getString("string"));
  }

  @Test
  public void testGetLong() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(125, entity.getLong("long"));
    entity = builder.set("long", LongValue.of(10)).build();
    assertEquals(10, entity.getLong("long"));
  }

  @Test
  public void testGetDouble() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(1.25, entity.getDouble("double"), 0);
    entity = builder.set("double", DoubleValue.of(10)).build();
    assertEquals(10, entity.getDouble("double"), 0);
  }

  @Test
  public void testGetBoolean() throws Exception {
    BaseEntity entity = builder.build();
    assertTrue(entity.getBoolean("boolean"));
    entity = builder.set("boolean", BooleanValue.of(false)).build();
    assertFalse(entity.getBoolean("boolean"));
  }

  @Test
  public void testGetDateTime() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(DATE_TIME, entity.getDateTime("dateTime"));
    Calendar cal = Calendar.getInstance();
    cal.add(Calendar.DATE, -1);
    DateTime dateTime = DateTime.copyFrom(cal);
    entity = builder.set("dateTime", DateTimeValue.of(dateTime)).build();
    assertEquals(dateTime, entity.getDateTime("dateTime"));
  }

  @Test
  public void testGetKey() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(KEY, entity.getKey("key"));
    Key key = Key.builder(KEY).name("BLA").build();
    entity = builder.set("key", key).build();
    assertEquals(key, entity.getKey("key"));
  }

  @Test
  public void testGetEntity() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(ENTITY, entity.getEntity("entity"));
    assertEquals(PARTIAL_ENTITY, entity.getEntity("partialEntity"));
    entity = builder.set("entity", EntityValue.of(PARTIAL_ENTITY)).build();
    assertEquals(PARTIAL_ENTITY, entity.getEntity("entity"));
  }

  @Test
  public void testGetList() throws Exception {
    BaseEntity entity = builder.build();
    List> list = entity.getList("list1");
    assertEquals(2, list.size());
    assertEquals(NullValue.of(), list.get(0));
    assertEquals("foo", list.get(1).get());
    list = entity.getList("list2");
    assertEquals(2, list.size());
    assertEquals(Long.valueOf(10), list.get(0).get());
    assertEquals(Double.valueOf(2), list.get(1).get());
    list = entity.getList("list3");
    assertEquals(1, list.size());
    assertEquals(Boolean.TRUE, list.get(0).get());
    entity = builder.set("list1", ListValue.of(list)).build();
    assertEquals(list, entity.getList("list1"));
  }

  @Test
  public void testGetBlob() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(BLOB, entity.getBlob("blob"));
    Blob blob = Blob.copyFrom(new byte[] {});
    entity = builder.set("blob", BlobValue.of(blob)).build();
    assertEquals(blob, entity.getBlob("blob"));
  }

  @Test
  public void testNames() throws Exception {
    Set names = ImmutableSet.builder()
        .add("string", "stringValue", "boolean", "double", "long", "list1", "list2", "list3")
        .add("entity", "partialEntity", "null", "dateTime", "blob", "key")
        .build();
    BaseEntity entity = builder.build();
    assertEquals(names, entity.names());
  }
}
\ No newline at end of file
+/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.gcloud.datastore;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;

import org.junit.Before;
import org.junit.Test;

import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Set;

public class BaseEntityTest {

  private static final Blob BLOB = Blob.copyFrom(new byte[]{1, 2});
  private static final DateTime DATE_TIME = DateTime.now();
  private static final Key KEY = Key.builder("ds1", "k1", "n1").build();
  private static final Entity ENTITY = Entity.builder(KEY).set("name", "foo").build();
  private static final IncompleteKey INCOMPLETE_KEY = IncompleteKey.builder("ds1", "k1").build();
  private static final FullEntity PARTIAL_ENTITY =
      Entity.builder(INCOMPLETE_KEY).build();

  private Builder builder;

  private class Builder extends BaseEntity.Builder {

    @Override public BaseEntity build() {

      return new BaseEntity(this) {

        @Override
        protected Builder emptyBuilder() {
          return new BaseEntityTest.Builder();
        }
      };
    }
  }

  @Before
  public void setUp() {
    builder = new Builder();
    builder.set("blob", BLOB).set("boolean", true).set("dateTime", DATE_TIME);
    builder.set("double", 1.25).set("key", KEY).set("string", "hello world");
    builder.set("long", 125).setNull("null").set("entity", ENTITY);
    builder.set("partialEntity", PARTIAL_ENTITY).set("stringValue", StringValue.of("bla"));
    builder.set("list1", NullValue.of(), StringValue.of("foo"));
    builder.set("list2", ImmutableList.of(LongValue.of(10), DoubleValue.of(2)));
    builder.set("list3", Collections.singletonList(BooleanValue.of(true)));
  }

  @Test
  public void testContains() throws Exception {
    BaseEntity entity = builder.build();
    assertTrue(entity.contains("list1"));
    assertFalse(entity.contains("bla"));
    entity = builder.clear().build();
    assertFalse(entity.contains("list1"));
  }

  @Test
  public void testGetValue() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(BlobValue.of(BLOB), entity.getValue("blob"));
  }

  @Test(expected = DatastoreException.class)
  public void testGetValueNotFound() throws Exception {
    BaseEntity entity = builder.clear().build();
    entity.getValue("blob");
  }

  @Test
  public void testIsNull() throws Exception {
    BaseEntity entity = builder.build();
    assertTrue(entity.isNull("null"));
    assertFalse(entity.isNull("blob"));
    entity = builder.setNull("blob").build();
    assertTrue(entity.isNull("blob"));
  }

  @Test(expected = DatastoreException.class)
  public void testIsNullNotFound() throws Exception {
    BaseEntity entity = builder.clear().build();
    entity.isNull("null");
  }

  @Test
  public void testGetString() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals("hello world", entity.getString("string"));
    assertEquals("bla", entity.getString("stringValue"));
    entity = builder.set("string", "foo").build();
    assertEquals("foo", entity.getString("string"));
  }

  @Test
  public void testGetLong() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(125, entity.getLong("long"));
    entity = builder.set("long", LongValue.of(10)).build();
    assertEquals(10, entity.getLong("long"));
  }

  @Test
  public void testGetDouble() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(1.25, entity.getDouble("double"), 0);
    entity = builder.set("double", DoubleValue.of(10)).build();
    assertEquals(10, entity.getDouble("double"), 0);
  }

  @Test
  public void testGetBoolean() throws Exception {
    BaseEntity entity = builder.build();
    assertTrue(entity.getBoolean("boolean"));
    entity = builder.set("boolean", BooleanValue.of(false)).build();
    assertFalse(entity.getBoolean("boolean"));
  }

  @Test
  public void testGetDateTime() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(DATE_TIME, entity.getDateTime("dateTime"));
    Calendar cal = Calendar.getInstance();
    cal.add(Calendar.DATE, -1);
    DateTime dateTime = DateTime.copyFrom(cal);
    entity = builder.set("dateTime", DateTimeValue.of(dateTime)).build();
    assertEquals(dateTime, entity.getDateTime("dateTime"));
  }

  @Test
  public void testGetKey() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(KEY, entity.getKey("key"));
    Key key = Key.builder(KEY).name("BLA").build();
    entity = builder.set("key", key).build();
    assertEquals(key, entity.getKey("key"));
  }

  @Test
  public void testGetEntity() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(ENTITY, entity.getEntity("entity"));
    assertEquals(PARTIAL_ENTITY, entity.getEntity("partialEntity"));
    entity = builder.set("entity", EntityValue.of(PARTIAL_ENTITY)).build();
    assertEquals(PARTIAL_ENTITY, entity.getEntity("entity"));
  }

  @Test
  public void testGetList() throws Exception {
    BaseEntity entity = builder.build();
    List> list = entity.getList("list1");
    assertEquals(2, list.size());
    assertEquals(NullValue.of(), list.get(0));
    assertEquals("foo", list.get(1).get());
    list = entity.getList("list2");
    assertEquals(2, list.size());
    assertEquals(Long.valueOf(10), list.get(0).get());
    assertEquals(Double.valueOf(2), list.get(1).get());
    list = entity.getList("list3");
    assertEquals(1, list.size());
    assertEquals(Boolean.TRUE, list.get(0).get());
    entity = builder.set("list1", ListValue.of(list)).build();
    assertEquals(list, entity.getList("list1"));
  }

  @Test
  public void testGetBlob() throws Exception {
    BaseEntity entity = builder.build();
    assertEquals(BLOB, entity.getBlob("blob"));
    Blob blob = Blob.copyFrom(new byte[] {});
    entity = builder.set("blob", BlobValue.of(blob)).build();
    assertEquals(blob, entity.getBlob("blob"));
  }

  @Test
  public void testNames() throws Exception {
    Set names = ImmutableSet.builder()
        .add("string", "stringValue", "boolean", "double", "long", "list1", "list2", "list3")
        .add("entity", "partialEntity", "null", "dateTime", "blob", "key")
        .build();
    BaseEntity entity = builder.build();
    assertEquals(names, entity.names());
  }
}
\ No newline at end of file
diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreExceptionTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreExceptionTest.java
new file mode 100644
index 000000000000..1dd0f255ceca
--- /dev/null
+++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreExceptionTest.java
@@ -0,0 +1 @@
+/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.gcloud.datastore;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import com.google.gcloud.datastore.DatastoreException.Code;
import com.google.gcloud.spi.DatastoreRpc.DatastoreRpcException;
import com.google.gcloud.spi.DatastoreRpc.DatastoreRpcException.Reason;

import org.junit.Test;

public class DatastoreExceptionTest {

  @Test
  public void testCode() throws Exception {
    for (Reason reason : Reason.values()) {
      Code code = Code.valueOf(reason.name());
      assertEquals(reason.retryable(), code.retryable());
      assertEquals(reason.description(), code.description());
      assertEquals(reason.httpStatus(), code.httpStatus());
    }

    DatastoreException exception = new DatastoreException(Code.ABORTED, "bla");
    assertEquals(Code.ABORTED, exception.code());
  }

  @Test
  public void testTranslateAndThrow() throws Exception {
    for (Reason reason : Reason.values()) {
      try {
        DatastoreException.translateAndThrow(new DatastoreRpcException(reason));
        fail("Exception expected");
      } catch (DatastoreException ex) {
        assertEquals(reason.name(), ex.code().name());
      }
    }
  }

  @Test
  public void testThrowInvalidRequest() throws Exception {
    try {
      DatastoreException.throwInvalidRequest("message %s %d", "a", 1);
      fail("Exception expected");
    } catch (DatastoreException ex) {
      assertEquals(Code.FAILED_PRECONDITION, ex.code());
      assertEquals("message a 1", ex.getMessage());
    }
  }
}
\ No newline at end of file
diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreHelperTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreHelperTest.java
index 8a31ece583ff..55c8d0cf3ce6 100644
--- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreHelperTest.java
+++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreHelperTest.java
@@ -28,7 +28,7 @@
 import static org.junit.Assert.fail;
 
 import com.google.common.collect.Iterators;
-import com.google.gcloud.datastore.DatastoreService.TransactionCallable;
+import com.google.gcloud.datastore.Datastore.TransactionCallable;
 
 import org.easymock.EasyMock;
 import org.junit.Test;
@@ -40,7 +40,7 @@ public class DatastoreHelperTest {
 
   @Test
   public void testNewKeyFactory() {
-    DatastoreServiceOptions options = createMock(DatastoreServiceOptions.class);
+    DatastoreOptions options = createMock(DatastoreOptions.class);
     expect(options.projectId()).andReturn("ds1").once();
     expect(options.namespace()).andReturn("ns1").once();
     replay(options);
@@ -55,73 +55,73 @@ public void testNewKeyFactory() {
 
   @Test
   public void testAllocateId() throws Exception {
-    DatastoreService datastoreService = createStrictMock(DatastoreService.class);
+    Datastore datastore = createStrictMock(Datastore.class);
     IncompleteKey pKey1 = IncompleteKey.builder("ds", "k").build();
     Key key1 = Key.builder(pKey1, 1).build();
-    expect(datastoreService.allocateId(new IncompleteKey[] {pKey1}))
+    expect(datastore.allocateId(new IncompleteKey[] {pKey1}))
         .andReturn(Collections.singletonList(key1));
-    replay(datastoreService);
-    assertEquals(key1, DatastoreHelper.allocateId(datastoreService, pKey1));
-    verify(datastoreService);
+    replay(datastore);
+    assertEquals(key1, DatastoreHelper.allocateId(datastore, pKey1));
+    verify(datastore);
   }
 
   @Test
   public void testGet() throws Exception {
-    DatastoreService datastoreService = createStrictMock(DatastoreService.class);
+    Datastore datastore = createStrictMock(Datastore.class);
     IncompleteKey pKey1 = IncompleteKey.builder("ds", "k").build();
     Key key1 = Key.builder(pKey1, 1).build();
     Entity entity1 = Entity.builder(key1).build();
     Key key2 = Key.builder(pKey1, 2).build();
-    expect(datastoreService.get(new Key[]{key1}))
+    expect(datastore.get(new Key[]{key1}))
         .andReturn(Collections.singletonList(entity1).iterator());
-    expect(datastoreService.get(new Key[]{key2}))
+    expect(datastore.get(new Key[]{key2}))
         .andReturn(Collections.emptyIterator());
-    replay(datastoreService);
-    assertEquals(entity1, DatastoreHelper.get(datastoreService, key1));
-    assertNull(DatastoreHelper.get(datastoreService, key2));
-    verify(datastoreService);
+    replay(datastore);
+    assertEquals(entity1, DatastoreHelper.get(datastore, key1));
+    assertNull(DatastoreHelper.get(datastore, key2));
+    verify(datastore);
   }
 
   @Test
   public void testAdd() throws Exception {
-    DatastoreService datastoreService = createStrictMock(DatastoreService.class);
+    Datastore datastore = createStrictMock(Datastore.class);
     IncompleteKey pKey = IncompleteKey.builder("ds", "k").build();
     Key key = Key.builder(pKey, 1).build();
     Entity entity = Entity.builder(key).build();
-    expect(datastoreService.add(new Entity[]{entity}))
+    expect(datastore.add(new Entity[]{entity}))
         .andReturn(Collections.singletonList(entity));
-    replay(datastoreService);
-    assertEquals(entity, DatastoreHelper.add(datastoreService, entity));
-    verify(datastoreService);
+    replay(datastore);
+    assertEquals(entity, DatastoreHelper.add(datastore, entity));
+    verify(datastore);
   }
 
   @Test
   public void testFetch() throws Exception {
-    DatastoreService datastoreService = createStrictMock(DatastoreService.class);
+    Datastore datastore = createStrictMock(Datastore.class);
     IncompleteKey pKey1 = IncompleteKey.builder("ds", "k").build();
     Key key1 = Key.builder(pKey1, 1).build();
     Key key2 = Key.builder(pKey1, "a").build();
     Entity entity1 = Entity.builder(key1).build();
     Entity entity2 = Entity.builder(key2).build();
-    expect(datastoreService.get(key1, key2)).andReturn(Iterators.forArray(entity1, entity2)).once();
-    replay(datastoreService);
-    List values = DatastoreHelper.fetch(datastoreService, key1, key2);
+    expect(datastore.get(key1, key2)).andReturn(Iterators.forArray(entity1, entity2)).once();
+    replay(datastore);
+    List values = DatastoreHelper.fetch(datastore, key1, key2);
     assertEquals(2, values.size());
     assertEquals(entity1, values.get(0));
     assertEquals(entity2, values.get(1));
-    verify(datastoreService);
+    verify(datastore);
   }
 
   @Test
   public void testRunInTransaction() throws Exception {
-    final DatastoreService datastoreService = createStrictMock(DatastoreService.class);
+    final Datastore datastore = createStrictMock(Datastore.class);
     final Transaction transaction = createStrictMock(Transaction.class);
-    expect(datastoreService.newTransaction()).andReturn(transaction).once();
+    expect(datastore.newTransaction()).andReturn(transaction).once();
     expect(transaction.active()).andReturn(true).once();
     expect(transaction.commit()).andReturn(null).once();
     expect(transaction.active()).andReturn(false).once();
-    replay(datastoreService, transaction);
-    String value = DatastoreHelper.runInTransaction(datastoreService,
+    replay(datastore, transaction);
+    String value = DatastoreHelper.runInTransaction(datastore,
         new TransactionCallable() {
           @Override
           public String run(DatastoreReaderWriter readerWriter) {
@@ -130,22 +130,22 @@ public String run(DatastoreReaderWriter readerWriter) {
             return "done";
           }
         });
-    verify(datastoreService, transaction);
+    verify(datastore, transaction);
     assertEquals("done", value);
   }
 
   @Test
   public void testRunInTransactionWithException() throws Exception {
-    final DatastoreService datastoreService = createStrictMock(DatastoreService.class);
+    final Datastore datastore = createStrictMock(Datastore.class);
     final Transaction transaction = createStrictMock(Transaction.class);
-    expect(datastoreService.newTransaction()).andReturn(transaction).once();
+    expect(datastore.newTransaction()).andReturn(transaction).once();
     expect(transaction.active()).andReturn(true).once();
     transaction.rollback();
     EasyMock.expectLastCall().once();
     expect(transaction.active()).andReturn(false).once();
-    replay(datastoreService, transaction);
+    replay(datastore, transaction);
     try {
-      DatastoreHelper.runInTransaction(datastoreService, new TransactionCallable() {
+      DatastoreHelper.runInTransaction(datastore, new TransactionCallable() {
         @Override
         public Void run(DatastoreReaderWriter readerWriter) throws Exception {
           assertTrue(transaction.active());
@@ -153,10 +153,10 @@ public Void run(DatastoreReaderWriter readerWriter) throws Exception {
           throw new Exception("Bla");
         }
       });
-      fail("DatastoreServiceException was expected");
-    } catch (DatastoreServiceException ex) {
+      fail("DatastoreException was expected");
+    } catch (DatastoreException ex) {
       assertEquals("Bla", ex.getCause().getMessage());
     }
-    verify(datastoreService, transaction);
+    verify(datastore, transaction);
   }
 }
diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreServiceOptionsTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreOptionsTest.java
similarity index 90%
rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreServiceOptionsTest.java
rename to gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreOptionsTest.java
index 59468be58129..e7dc71c50ff6 100644
--- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreServiceOptionsTest.java
+++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreOptionsTest.java
@@ -31,23 +31,23 @@
 
 import java.io.IOException;
 
-public class DatastoreServiceOptionsTest {
+public class DatastoreOptionsTest {
 
   private static final String PROJECT_ID = "project_id";
   private DatastoreRpcFactory datastoreRpcFactory;
   private DatastoreRpc datastoreRpc;
-  private DatastoreServiceOptions.Builder options;
+  private DatastoreOptions.Builder options;
 
   @Before
   public void setUp() throws IOException, InterruptedException {
     datastoreRpcFactory = EasyMock.createMock(DatastoreRpcFactory.class);
     datastoreRpc = EasyMock.createMock(DatastoreRpc.class);
-    options = DatastoreServiceOptions.builder()
+    options = DatastoreOptions.builder()
         .normalizeDataset(false)
         .serviceRpcFactory(datastoreRpcFactory)
         .projectId(PROJECT_ID)
         .host("http://localhost:" + LocalGcdHelper.PORT);
-    EasyMock.expect(datastoreRpcFactory.create(EasyMock.anyObject(DatastoreServiceOptions.class)))
+    EasyMock.expect(datastoreRpcFactory.create(EasyMock.anyObject(DatastoreOptions.class)))
         .andReturn(datastoreRpc)
         .anyTimes();
     EasyMock.replay(datastoreRpcFactory, datastoreRpc);
@@ -83,8 +83,8 @@ public void testDatastore() throws Exception {
 
   @Test
   public void testToBuilder() throws Exception {
-    DatastoreServiceOptions original = options.namespace("ns1").force(true).build();
-    DatastoreServiceOptions copy = original.toBuilder().build();
+    DatastoreOptions original = options.namespace("ns1").force(true).build();
+    DatastoreOptions copy = original.toBuilder().build();
     assertEquals(original.projectId(), copy.projectId());
     assertEquals(original.namespace(), copy.namespace());
     assertEquals(original.host(), copy.host());
diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreServiceExceptionTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreServiceExceptionTest.java
deleted file mode 100644
index 1a06de833cf2..000000000000
--- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreServiceExceptionTest.java
+++ /dev/null
@@ -1 +0,0 @@
-/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.gcloud.datastore;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import com.google.gcloud.datastore.DatastoreServiceException.Code;
import com.google.gcloud.spi.DatastoreRpc.DatastoreRpcException;
import com.google.gcloud.spi.DatastoreRpc.DatastoreRpcException.Reason;

import org.junit.Test;

public class DatastoreServiceExceptionTest {

  @Test
  public void testCode() throws Exception {
    for (Reason reason : Reason.values()) {
      Code code = Code.valueOf(reason.name());
      assertEquals(reason.retryable(), code.retryable());
      assertEquals(reason.description(), code.description());
      assertEquals(reason.httpStatus(), code.httpStatus());
    }

    DatastoreServiceException exception = new DatastoreServiceException(Code.ABORTED, "bla");
    assertEquals(Code.ABORTED, exception.code());
  }

  @Test
  public void testTranslateAndThrow() throws Exception {
    for (Reason reason : Reason.values()) {
      try {
        DatastoreServiceException.translateAndThrow(new DatastoreRpcException(reason));
        fail("Exception expected");
      } catch (DatastoreServiceException ex) {
        assertEquals(reason.name(), ex.code().name());
      }
    }
  }

  @Test
  public void testThrowInvalidRequest() throws Exception {
    try {
      DatastoreServiceException.throwInvalidRequest("message %s %d", "a", 1);
      fail("Exception expected");
    } catch (DatastoreServiceException ex) {
      assertEquals(Code.FAILED_PRECONDITION, ex.code());
      assertEquals("message a 1", ex.getMessage());
    }
  }
}
\ No newline at end of file
diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java
similarity index 95%
rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
rename to gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java
index 535fcb5f13e1..156f9684f8ba 100644
--- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
+++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java
@@ -51,7 +51,7 @@
 import java.util.List;
 
 @RunWith(JUnit4.class)
-public class DatastoreServiceTest {
+public class DatastoreTest {
 
   private static final String PROJECT_ID = LocalGcdHelper.DEFAULT_PROJECT_ID;
   private static final String KIND1 = "kind1";
@@ -97,8 +97,8 @@ public class DatastoreServiceTest {
   private static final Entity ENTITY3 = Entity.builder(ENTITY1).key(KEY3).remove("str")
       .set("null", NULL_VALUE).set("partial1", PARTIAL_ENTITY2).set("partial2", ENTITY2).build();
 
-  private DatastoreServiceOptions options;
-  private DatastoreService datastore;
+  private DatastoreOptions options;
+  private Datastore datastore;
 
   private static LocalGcdHelper gcdHelper;
 
@@ -111,11 +111,11 @@ public static void beforeClass() throws IOException, InterruptedException {
 
   @Before
   public void setUp() throws IOException, InterruptedException {
-    options = DatastoreServiceOptions.builder()
+    options = DatastoreOptions.builder()
         .projectId(PROJECT_ID)
         .host("http://localhost:" + LocalGcdHelper.PORT)
         .build();
-    datastore = DatastoreServiceFactory.instance().get(options);
+    datastore = DatastoreFactory.instance().get(options);
     StructuredQuery query = Query.keyQueryBuilder().build();
     QueryResults result = datastore.run(query);
     datastore.delete(Iterators.toArray(result, Key.class));
@@ -155,14 +155,14 @@ public void testNewTransactionCommit() {
     try {
       transaction.commit();
       fail("Expecting a failure");
-    } catch (DatastoreServiceException ex) {
+    } catch (DatastoreException ex) {
       // expected to fail
     }
 
     try {
       transaction.rollback();
       fail("Expecting a failure");
-    } catch (DatastoreServiceException ex) {
+    } catch (DatastoreException ex) {
       // expected to fail
     }
 
@@ -185,8 +185,8 @@ public void testTransactionWithRead() {
     try {
       transaction.commit();
       fail("Expecting a failure");
-    } catch (DatastoreServiceException expected) {
-      assertEquals(DatastoreServiceException.Code.ABORTED, expected.code());
+    } catch (DatastoreException expected) {
+      assertEquals(DatastoreException.Code.ABORTED, expected.code());
     }
   }
 
@@ -213,8 +213,8 @@ public void testTransactionWithQuery() {
     try {
       transaction.commit();
       fail("Expecting a failure");
-    } catch (DatastoreServiceException expected) {
-      assertEquals(DatastoreServiceException.Code.ABORTED, expected.code());
+    } catch (DatastoreException expected) {
+      assertEquals(DatastoreException.Code.ABORTED, expected.code());
     }
   }
 
@@ -232,7 +232,7 @@ public void testNewTransactionRollback() {
     try {
       transaction.commit();
       fail("Expecting a failure");
-    } catch (DatastoreServiceException ex) {
+    } catch (DatastoreException ex) {
       // expected to fail
     }
 
@@ -249,28 +249,28 @@ private void verifyNotUsable(DatastoreWriter writer) {
     try {
       writer.add(ENTITY3);
       fail("Expecting a failure");
-    } catch (DatastoreServiceException ex) {
+    } catch (DatastoreException ex) {
       // expected to fail
     }
 
     try {
       writer.put(ENTITY3);
       fail("Expecting a failure");
-    } catch (DatastoreServiceException ex) {
+    } catch (DatastoreException ex) {
       // expected to fail
     }
 
     try {
       writer.update(ENTITY3);
       fail("Expecting a failure");
-    } catch (DatastoreServiceException ex) {
+    } catch (DatastoreException ex) {
       // expected to fail
     }
 
     try {
       writer.delete(ENTITY3.key());
       fail("Expecting a failure");
-    } catch (DatastoreServiceException ex) {
+    } catch (DatastoreException ex) {
       // expected to fail
     }
   }
@@ -315,7 +315,7 @@ public void testNewBatch() {
     try {
       batch.submit();
       fail("Expecting a failure");
-    } catch (DatastoreServiceException ex) {
+    } catch (DatastoreException ex) {
       // expected to fail
     }
     verifyNotUsable(batch);
@@ -535,7 +535,7 @@ public void testGetArray() {
     try {
       entity3.getString("str");
       fail("Expecting a failure");
-    } catch (DatastoreServiceException expected) {
+    } catch (DatastoreException expected) {
       // expected - no such property
     }
     assertFalse(result.hasNext());
@@ -552,7 +552,7 @@ public void testAddEntity() {
     try {
       datastore.add(ENTITY1);
       fail("Expecting a failure");
-    } catch (DatastoreServiceException expected) {
+    } catch (DatastoreException expected) {
       // expected;
     }
 
@@ -578,7 +578,7 @@ public void testUpdate() {
     try {
       datastore.update(ENTITY3);
       fail("Expecting a failure");
-    } catch (DatastoreServiceException expected) {
+    } catch (DatastoreException expected) {
       // expected;
     }
     datastore.add(ENTITY3);
@@ -642,17 +642,17 @@ public void testRetires() throws Exception {
         .addFound(EntityResult.newBuilder().setEntity(ENTITY1.toPb())).build();
     DatastoreRpcFactory rpcFactoryMock = EasyMock.createStrictMock(DatastoreRpcFactory.class);
     DatastoreRpc rpcMock = EasyMock.createStrictMock(DatastoreRpc.class);
-    EasyMock.expect(rpcFactoryMock.create(EasyMock.anyObject(DatastoreServiceOptions.class)))
+    EasyMock.expect(rpcFactoryMock.create(EasyMock.anyObject(DatastoreOptions.class)))
         .andReturn(rpcMock);
     EasyMock.expect(rpcMock.lookup(requestPb))
         .andThrow(new DatastoreRpc.DatastoreRpcException(Reason.UNAVAILABLE))
         .andReturn(responsePb);
     EasyMock.replay(rpcFactoryMock, rpcMock);
-    DatastoreServiceOptions options = this.options.toBuilder()
+    DatastoreOptions options = this.options.toBuilder()
         .retryParams(RetryParams.getDefaultInstance())
         .serviceRpcFactory(rpcFactoryMock)
         .build();
-    DatastoreService datastore = DatastoreServiceFactory.instance().get(options);
+    Datastore datastore = DatastoreFactory.instance().get(options);
     Entity entity = datastore.get(KEY1);
     assertEquals(ENTITY1, entity);
     EasyMock.verify(rpcFactoryMock, rpcMock);
diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/ListValueTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/ListValueTest.java
index 04fdbec54727..36e3571d49ac 100644
--- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/ListValueTest.java
+++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/ListValueTest.java
@@ -45,7 +45,7 @@ public void testOf() throws Exception {
     assertFalse(value.hasMeaning());
   }
 
-  @Test(expected = DatastoreServiceException.class)
+  @Test(expected = DatastoreException.class)
   public void testIndexedCannotBeSpecified() {
     ListValue.builder().indexed(false);
   }
diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/SerializationTest.java
index 1e827971c7fe..9574f1e246d2 100644
--- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/SerializationTest.java
+++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/SerializationTest.java
@@ -132,12 +132,12 @@ public class SerializationTest {
 
   @Test
   public void testServiceOptions() throws Exception {
-    DatastoreServiceOptions options = DatastoreServiceOptions.builder()
+    DatastoreOptions options = DatastoreOptions.builder()
         .authCredentials(AuthCredentials.createForAppEngine())
         .normalizeDataset(false)
         .projectId("ds1")
         .build();
-    DatastoreServiceOptions serializedCopy = serializeAndDeserialize(options);
+    DatastoreOptions serializedCopy = serializeAndDeserialize(options);
     assertEquals(options, serializedCopy);
 
     options = options.toBuilder()
diff --git a/gcloud-java-examples/pom.xml b/gcloud-java-examples/pom.xml
index 66d9fc9c93e3..1c0357d63635 100644
--- a/gcloud-java-examples/pom.xml
+++ b/gcloud-java-examples/pom.xml
@@ -21,4 +21,15 @@
       ${project.version}
     
   
+  
+    
+      
+        org.codehaus.mojo
+        exec-maven-plugin
+        
+          false
+        
+      
+    
+  
 
diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/DatastoreExample.java b/gcloud-java-examples/src/main/java/com/google/gcloud/examples/DatastoreExample.java
index 3d7feae66e8c..9188117e4327 100644
--- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/DatastoreExample.java
+++ b/gcloud-java-examples/src/main/java/com/google/gcloud/examples/DatastoreExample.java
@@ -16,9 +16,9 @@
 
 package com.google.gcloud.examples;
 
-import com.google.gcloud.datastore.DatastoreService;
-import com.google.gcloud.datastore.DatastoreServiceFactory;
-import com.google.gcloud.datastore.DatastoreServiceOptions;
+import com.google.gcloud.datastore.Datastore;
+import com.google.gcloud.datastore.DatastoreFactory;
+import com.google.gcloud.datastore.DatastoreOptions;
 import com.google.gcloud.datastore.DateTime;
 import com.google.gcloud.datastore.Entity;
 import com.google.gcloud.datastore.FullEntity;
@@ -172,21 +172,21 @@ public String getRequiredParams() {
 
   public static void main(String... args) {
     DatastoreAction action = null;
-    DatastoreService datastore = null;
+    Datastore datastore = null;
     Key key = null;
     String projectId = args.length > 0 ? args[0] : null;
     // If you want to access a local Datastore running via the gcd sdk, do
-    //   DatastoreServiceOptions options = DatastoreServiceOptions.builder()
+    //   DatastoreOptions options = DatastoreOptions.builder()
     //       .projectId(projectId)
     //       .namespace(NAMESPACE)
     //       .host("http://localhost:8080")
     //       .build();
-    DatastoreServiceOptions options = DatastoreServiceOptions.builder()
+    DatastoreOptions options = DatastoreOptions.builder()
         .projectId(projectId)
         .namespace(NAMESPACE)
         .build();
     String name = args.length > 1 ? args[1] : System.getProperty("user.name");
-    datastore = DatastoreServiceFactory.instance().get(options);
+    datastore = DatastoreFactory.instance().get(options);
     KeyFactory keyFactory = datastore.newKeyFactory().kind(USER_KIND);
     key = keyFactory.newKey(name);
     String actionName = args.length > 2 ? args[2].toLowerCase() : DEFAULT_ACTION;
diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java b/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java
index b0d44c292d2c..7786c124f6cf 100644
--- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java
+++ b/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java
@@ -16,6 +16,8 @@
 
 package com.google.gcloud.examples;
 
+import com.google.gcloud.AuthCredentials;
+import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials;
 import com.google.gcloud.RetryParams;
 import com.google.gcloud.spi.StorageRpc.Tuple;
 import com.google.gcloud.storage.BatchRequest;
@@ -24,11 +26,12 @@
 import com.google.gcloud.storage.BlobReadChannel;
 import com.google.gcloud.storage.BlobWriteChannel;
 import com.google.gcloud.storage.Bucket;
-import com.google.gcloud.storage.StorageService;
-import com.google.gcloud.storage.StorageService.ComposeRequest;
-import com.google.gcloud.storage.StorageService.CopyRequest;
-import com.google.gcloud.storage.StorageServiceFactory;
-import com.google.gcloud.storage.StorageServiceOptions;
+import com.google.gcloud.storage.Storage;
+import com.google.gcloud.storage.Storage.ComposeRequest;
+import com.google.gcloud.storage.Storage.CopyRequest;
+import com.google.gcloud.storage.Storage.SignUrlOption;
+import com.google.gcloud.storage.StorageFactory;
+import com.google.gcloud.storage.StorageOptions;
 
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -40,7 +43,14 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
 import java.util.Arrays;
+import java.util.Calendar;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -58,7 +68,8 @@
  * -Dexec.args="[] list []| info [ []]|
  *  download   [local_file]| upload   []|
  *  delete  +| cp    |
- *  compose  + | update_metadata   [key=value]*"}
+ *  compose  + | update_metadata   [key=value]*|
+ *  sign_url    "}
  * 
  * 
  *
@@ -73,9 +84,9 @@ public class StorageExample {
 
   private static abstract class StorageAction {
 
-    abstract void run(StorageService storage, T request) throws Exception;
+    abstract void run(Storage storage, T request) throws Exception;
 
-    abstract T parse(String... args) throws IllegalArgumentException, IOException;
+    abstract T parse(String... args) throws Exception; 
 
     protected String params() {
       return "";
@@ -127,7 +138,7 @@ public String params() {
    */
   private static class InfoAction extends BlobsAction {
     @Override
-    public void run(StorageService storage, Blob... blobs) {
+    public void run(Storage storage, Blob... blobs) {
       if (blobs.length == 1) {
         if (blobs[0].name().isEmpty()) {
           // get Bucket
@@ -174,7 +185,7 @@ public String params() {
    */
   private static class DeleteAction extends BlobsAction {
     @Override
-    public void run(StorageService storage, Blob... blobs) {
+    public void run(Storage storage, Blob... blobs) {
       if (blobs.length == 1) {
         boolean wasDeleted = storage.delete(blobs[0].bucket(), blobs[0].name());
         if (wasDeleted) {
@@ -218,7 +229,7 @@ String parse(String... args) {
     }
 
     @Override
-    public void run(StorageService storage, String bucket) {
+    public void run(Storage storage, String bucket) {
       if (bucket == null) {
         // list buckets
         for (Bucket b : storage.list()) {
@@ -245,11 +256,11 @@ public String params() {
    */
   private static class UploadAction extends StorageAction> {
     @Override
-    public void run(StorageService storage, Tuple tuple) throws Exception {
+    public void run(Storage storage, Tuple tuple) throws Exception {
       run(storage, tuple.x(), tuple.y());
     }
 
-    private void run(StorageService storage, Path uploadFrom, Blob blob) throws IOException {
+    private void run(Storage storage, Path uploadFrom, Blob blob) throws IOException {
       if (Files.size(uploadFrom) > 1_000_000) {
         // When content is not available or large (1MB or more) it is recommended
         // to write it in chunks via the blob's channel writer.
@@ -301,11 +312,11 @@ public String params() {
   private static class DownloadAction extends StorageAction> {
 
     @Override
-    public void run(StorageService storage, Tuple tuple) throws IOException {
+    public void run(Storage storage, Tuple tuple) throws IOException {
       run(storage, tuple.x().bucket(), tuple.x().name(), tuple.y());
     }
 
-    private void run(StorageService storage, String bucket, String blobName, Path downloadTo)
+    private void run(Storage storage, String bucket, String blobName, Path downloadTo)
         throws IOException {
       Blob blob = storage.get(bucket, blobName);
       if (blob == null) {
@@ -369,7 +380,7 @@ public String params() {
    */
   private static class CopyAction extends StorageAction {
     @Override
-    public void run(StorageService storage, CopyRequest request) {
+    public void run(Storage storage, CopyRequest request) {
       Blob copiedBlob = storage.copy(request);
       System.out.println("Copied " + copiedBlob);
     }
@@ -395,7 +406,7 @@ public String params() {
    */
   private static class ComposeAction extends StorageAction {
     @Override
-    public void run(StorageService storage, ComposeRequest request) {
+    public void run(Storage storage, ComposeRequest request) {
       Blob composedBlob = storage.compose(request);
       System.out.println("Composed " + composedBlob);
     }
@@ -424,15 +435,15 @@ public String params() {
    *
    * @see Objects: update
    */
-  private static class UpdateMetadata extends StorageAction>> {
+  private static class UpdateMetadataAction extends StorageAction>> {
 
     @Override
-    public void run(StorageService storage, Tuple> tuple)
+    public void run(Storage storage, Tuple> tuple)
         throws IOException {
       run(storage, tuple.x().bucket(), tuple.x().name(), tuple.y());
     }
 
-    private void run(StorageService storage, String bucket, String blobName,
+    private void run(Storage storage, String bucket, String blobName,
         Map metadata) {
       Blob blob = storage.get(bucket, blobName);
       if (blob == null) {
@@ -467,6 +478,52 @@ public String params() {
     }
   }
 
+  /**
+   * This class demonstrates how to sign a url.
+   * URL will be valid for 1 day.
+   *
+   * @see Signed URLs
+   */
+  private static class SignUrlAction extends
+      StorageAction> {
+
+    private static final char[] PASSWORD =  "notasecret".toCharArray();
+
+    @Override
+    public void run(Storage storage, Tuple tuple)
+        throws Exception {
+      run(storage, tuple.x(), tuple.y());
+    }
+
+    private void run(Storage storage, ServiceAccountAuthCredentials cred, Blob blob)
+        throws IOException {
+      Calendar cal = Calendar.getInstance();
+      cal.add(Calendar.DATE, 1);
+      long expiration = cal.getTimeInMillis() / 1000;
+      System.out.println("Signed URL: " +
+          storage.signUrl(blob, expiration, SignUrlOption.serviceAccount(cred)));
+    }
+
+    @Override
+    Tuple parse(String... args)
+        throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException,
+        UnrecoverableKeyException {
+      if (args.length != 4) {
+        throw new IllegalArgumentException();
+      }
+      KeyStore keystore = KeyStore.getInstance("PKCS12");
+      keystore.load(Files.newInputStream(Paths.get(args[0])), PASSWORD);
+      PrivateKey privateKey = (PrivateKey) keystore.getKey("privatekey", PASSWORD);
+      ServiceAccountAuthCredentials cred = AuthCredentials.createFor(args[1], privateKey);
+      return Tuple.of(cred, Blob.of(args[2], args[3]));
+    }
+
+    @Override
+    public String params() {
+      return "   ";
+    }
+  }
+
   static {
     ACTIONS.put("info", new InfoAction());
     ACTIONS.put("delete", new DeleteAction());
@@ -475,7 +532,8 @@ public String params() {
     ACTIONS.put("download", new DownloadAction());
     ACTIONS.put("cp", new CopyAction());
     ACTIONS.put("compose", new ComposeAction());
-    ACTIONS.put("update_metadata", new UpdateMetadata());
+    ACTIONS.put("update_metadata", new UpdateMetadataAction());
+    ACTIONS.put("sign_url", new SignUrlAction());
   }
 
   public static void printUsage() {
@@ -499,8 +557,8 @@ public static void main(String... args) throws Exception {
       printUsage();
       return;
     }
-    StorageServiceOptions.Builder optionsBuilder =
-        StorageServiceOptions.builder().retryParams(RetryParams.getDefaultInstance());
+    StorageOptions.Builder optionsBuilder =
+        StorageOptions.builder().retryParams(RetryParams.getDefaultInstance());
     StorageAction action;
     if (args.length >= 2 && !ACTIONS.containsKey(args[0])) {
       optionsBuilder.projectId(args[0]);
@@ -515,7 +573,7 @@ public static void main(String... args) throws Exception {
       printUsage();
       return;
     }
-    StorageService storage = StorageServiceFactory.instance().get(optionsBuilder.build());
+    Storage storage = StorageFactory.instance().get(optionsBuilder.build());
     Object request;
     try {
       request = action.parse(args);
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/DefaultStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/gcloud/spi/DefaultStorageRpc.java
index e27d837d7173..89dfb4c6c414 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/DefaultStorageRpc.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/spi/DefaultStorageRpc.java
@@ -58,8 +58,8 @@
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
-import com.google.gcloud.storage.StorageServiceException;
-import com.google.gcloud.storage.StorageServiceOptions;
+import com.google.gcloud.storage.StorageException;
+import com.google.gcloud.storage.StorageOptions;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -71,41 +71,41 @@
 public class DefaultStorageRpc implements StorageRpc {
 
   public static final String DEFAULT_PROJECTION = "full";
-  private final StorageServiceOptions options;
+  private final StorageOptions options;
   private final Storage storage;
 
   // see: https://cloud.google.com/storage/docs/concepts-techniques#practices
   private static final Set RETRYABLE_CODES = ImmutableSet.of(504, 503, 502, 500, 408);
 
-  public DefaultStorageRpc(StorageServiceOptions options) {
+  public DefaultStorageRpc(StorageOptions options) {
     HttpTransport transport = options.httpTransportFactory().create();
     HttpRequestInitializer initializer = options.httpRequestInitializer();
     this.options = options;
     storage = new Storage.Builder(transport, new JacksonFactory(), initializer)
+        .setRootUrl(options.host())
         .setApplicationName("gcloud-java")
         .build();
-    // Todo: make sure nulls are being used as Data.asNull()
   }
 
-  private static StorageServiceException translate(IOException exception) {
-    StorageServiceException translated;
+  private static StorageException translate(IOException exception) {
+    StorageException translated;
     if (exception instanceof GoogleJsonResponseException) {
       translated = translate(((GoogleJsonResponseException) exception).getDetails());
     } else {
-      translated = new StorageServiceException(0, exception.getMessage(), false);
+      translated = new StorageException(0, exception.getMessage(), false);
     }
     translated.initCause(exception);
     return translated;
   }
 
-  private static StorageServiceException translate(GoogleJsonError exception) {
+  private static StorageException translate(GoogleJsonError exception) {
     boolean retryable = RETRYABLE_CODES.contains(exception.getCode())
         || "InternalError".equals(exception.getMessage());
-    return new StorageServiceException(exception.getCode(), exception.getMessage(), retryable);
+    return new StorageException(exception.getCode(), exception.getMessage(), retryable);
   }
 
   @Override
-  public Bucket create(Bucket bucket, Map options) throws StorageServiceException {
+  public Bucket create(Bucket bucket, Map options) throws StorageException {
     try {
       return storage.buckets()
           .insert(this.options.projectId(), bucket)
@@ -120,7 +120,7 @@ public Bucket create(Bucket bucket, Map options) throws StorageServic
 
   @Override
   public StorageObject create(StorageObject storageObject, final byte[] content,
-      Map options) throws StorageServiceException {
+      Map options) throws StorageException {
     try {
       return storage.objects()
           .insert(storageObject.getBucket(), storageObject,
@@ -253,7 +253,7 @@ public boolean delete(Bucket bucket, Map options) {
           .execute();
       return true;
     } catch (IOException ex) {
-      StorageServiceException serviceException = translate(ex);
+      StorageException serviceException = translate(ex);
       if (serviceException.code() == 404) {
         return false;
       }
@@ -267,7 +267,7 @@ public boolean delete(StorageObject blob, Map options) {
       deleteRequest(blob, options).execute();
       return true;
     } catch (IOException ex) {
-      StorageServiceException serviceException = translate(ex);
+      StorageException serviceException = translate(ex);
       if (serviceException.code() == 404) {
         return false;
       }
@@ -288,7 +288,7 @@ private Storage.Objects.Delete deleteRequest(StorageObject blob, Map
 
   @Override
   public StorageObject compose(Iterable sources, StorageObject target,
-      Map targetOptions) throws StorageServiceException {
+      Map targetOptions) throws StorageException {
     ComposeRequest request = new ComposeRequest();
     if (target.getContentType() == null) {
       // todo: remove once this is no longer requirement (b/20681287).
@@ -322,7 +322,7 @@ public StorageObject compose(Iterable sources, StorageObject targ
 
   @Override
   public StorageObject copy(StorageObject source, Map sourceOptions,
-      StorageObject target, Map targetOptions) throws StorageServiceException {
+      StorageObject target, Map targetOptions) throws StorageException {
     try {
       return storage
           .objects()
@@ -345,7 +345,7 @@ public StorageObject copy(StorageObject source, Map sourceOptions,
 
   @Override
   public byte[] load(StorageObject from, Map options)
-      throws StorageServiceException {
+      throws StorageException {
     try {
       Storage.Objects.Get getRequest = storage.objects()
           .get(from.getBucket(), from.getName())
@@ -363,25 +363,25 @@ public byte[] load(StorageObject from, Map options)
   }
 
   @Override
-  public BatchResponse batch(BatchRequest request) throws StorageServiceException {
+  public BatchResponse batch(BatchRequest request) throws StorageException {
     com.google.api.client.googleapis.batch.BatchRequest batch = storage.batch();
-    final Map> deletes =
+    final Map> deletes =
         Maps.newConcurrentMap();
-    final Map> updates =
+    final Map> updates =
         Maps.newConcurrentMap();
-    final Map> gets =
+    final Map> gets =
         Maps.newConcurrentMap();
     try {
       for (final Tuple> tuple : request.toDelete) {
         deleteRequest(tuple.x(), tuple.y()).queue(batch, new JsonBatchCallback() {
           @Override
           public void onSuccess(Void ignore, HttpHeaders responseHeaders) {
-            deletes.put(tuple.x(), Tuple.of(Boolean.TRUE, null));
+            deletes.put(tuple.x(), Tuple.of(Boolean.TRUE, null));
           }
 
           @Override
           public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
-            deletes.put(tuple.x(), Tuple.of(null, translate(e)));
+            deletes.put(tuple.x(), Tuple.of(null, translate(e)));
           }
         });
       }
@@ -390,13 +390,13 @@ public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
           @Override
           public void onSuccess(StorageObject storageObject, HttpHeaders responseHeaders) {
             updates.put(tuple.x(),
-                Tuple.of(storageObject, null));
+                Tuple.of(storageObject, null));
           }
 
           @Override
           public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
             updates.put(tuple.x(),
-                Tuple.of(null, translate(e)));
+                Tuple.of(null, translate(e)));
           }
         });
       }
@@ -405,13 +405,13 @@ public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
           @Override
           public void onSuccess(StorageObject storageObject, HttpHeaders responseHeaders) {
             gets.put(tuple.x(),
-                Tuple.of(storageObject, null));
+                Tuple.of(storageObject, null));
           }
 
           @Override
           public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
             gets.put(tuple.x(),
-                Tuple.of(null, translate(e)));
+                Tuple.of(null, translate(e)));
           }
         });
       }
@@ -424,7 +424,7 @@ public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
 
   @Override
   public byte[] read(StorageObject from, Map options, long position, int bytes)
-      throws StorageServiceException {
+      throws StorageException {
     try {
       Get req = storage.objects().get(from.getBucket(), from.getName());
       req.setIfMetagenerationMatch(IF_METAGENERATION_MATCH.getLong(options))
@@ -445,7 +445,7 @@ public byte[] read(StorageObject from, Map options, long position, in
 
   @Override
   public void write(String uploadId, byte[] toWrite, int toWriteOffset, StorageObject dest,
-      long destOffset, int length, boolean last) throws StorageServiceException {
+      long destOffset, int length, boolean last) throws StorageException {
     try {
       GenericUrl url = new GenericUrl(uploadId);
       HttpRequest httpRequest = storage.getRequestFactory().buildPostRequest(url,
@@ -487,7 +487,7 @@ public void write(String uploadId, byte[] toWrite, int toWriteOffset, StorageObj
 
   @Override
   public String open(StorageObject object, Map options)
-      throws StorageServiceException {
+      throws StorageException {
     try {
       Insert req = storage.objects().insert(object.getBucket(), object);
       GenericUrl url = req.buildHttpRequest().getUrl();
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java b/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java
index ab1e9affbbce..bd5d9b782975 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java
@@ -1 +1 @@
-/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.gcloud.spi;

import com.google.api.services.storage.model.Bucket;
import com.google.api.services.storage.model.StorageObject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gcloud.storage.StorageServiceException;

import java.util.List;
import java.util.Map;

public interface StorageRpc {

  enum Option {
    PREDEFINED_ACL("predefinedAcl"),
    PREDEFINED_DEFAULT_OBJECT_ACL("predefinedDefaultObjectAcl"),
    IF_METAGENERATION_MATCH("ifMetagenerationMatch"),
    IF_METAGENERATION_NOT_MATCH("ifMetagenerationNotMatch"),
    IF_GENERATION_NOT_MATCH("ifGenerationMatch"),
    IF_GENERATION_MATCH("ifGenerationNotMatch"),
    IF_SOURCE_METAGENERATION_MATCH("ifSourceMetagenerationMatch"),
    IF_SOURCE_METAGENERATION_NOT_MATCH("ifSourceMetagenerationNotMatch"),
    IF_SOURCE_GENERATION_MATCH("ifSourceGenerationMatch"),
    IF_SOURCE_GENERATION_NOT_MATCH("ifSourceGenerationNotMatch"),
    PREFIX("prefix"),
    MAX_RESULTS("maxResults"),
    PAGE_TOKEN("pageToken"),
    DELIMITER("delimiter"),
    VERSIONS("versions");

    private final String value;

    Option(String value) {
      this.value = value;
    }

    public String value() {
      return value;
    }

    @SuppressWarnings("unchecked")
     T get(Map options) {
      return (T) options.get(this);
    }

    String getString(Map options) {
      return get(options);
    }

    Long getLong(Map options) {
      return get(options);
    }

    Boolean getBoolean(Map options) {
      return get(options);
    }
  }

  class Tuple {

    private final X x;
    private final Y y;

    private Tuple(X x, Y y) {
      this.x = x;
      this.y = y;
    }

    public static  Tuple of(X x, Y y) {
      return new Tuple<>(x, y);
    }

    public X x() {
      return x;
    }

    public Y y() {
      return y;
    }
  }

  class BatchRequest {

    public final List>> toDelete;
    public final List>> toUpdate;
    public final List>> toGet;

    public BatchRequest(Iterable>> toDelete,
        Iterable>> toUpdate,
        Iterable>> toGet) {
      this.toDelete = ImmutableList.copyOf(toDelete);
      this.toUpdate = ImmutableList.copyOf(toUpdate);
      this.toGet = ImmutableList.copyOf(toGet);
    }
  }

  class BatchResponse {

    public final Map> deletes;
    public final Map> updates;
    public final Map> gets;

    public BatchResponse(Map> deletes,
        Map> updates,
        Map> gets) {
      this.deletes = ImmutableMap.copyOf(deletes);
      this.updates = ImmutableMap.copyOf(updates);
      this.gets = ImmutableMap.copyOf(gets);
    }
  }

  Bucket create(Bucket bucket, Map options) throws StorageServiceException;

  StorageObject create(StorageObject object, byte[] content, Map options)
      throws StorageServiceException;

  Tuple> list(Map options) throws StorageServiceException;

  Tuple> list(String bucket, Map options)
      throws StorageServiceException;

  Bucket get(Bucket bucket, Map options) throws StorageServiceException;

  StorageObject get(StorageObject object, Map options)
      throws StorageServiceException;

  Bucket patch(Bucket bucket, Map options) throws StorageServiceException;

  StorageObject patch(StorageObject storageObject, Map options)
      throws StorageServiceException;

  boolean delete(Bucket bucket, Map options) throws StorageServiceException;

  boolean delete(StorageObject object, Map options) throws StorageServiceException;

  BatchResponse batch(BatchRequest request) throws StorageServiceException;

  StorageObject compose(Iterable sources, StorageObject target,
      Map targetOptions) throws StorageServiceException;

  StorageObject copy(StorageObject source, Map sourceOptions,
      StorageObject target, Map targetOptions) throws StorageServiceException;

  byte[] load(StorageObject storageObject, Map options)
      throws StorageServiceException;

  byte[] read(StorageObject from, Map options, long position, int bytes)
      throws StorageServiceException;

  String open(StorageObject object, Map options) throws StorageServiceException;

  void write(String uploadId, byte[] toWrite, int toWriteOffset, StorageObject dest,
      long destOffset, int length, boolean last) throws StorageServiceException;
}
\ No newline at end of file
+/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.gcloud.spi;

import com.google.api.services.storage.model.Bucket;
import com.google.api.services.storage.model.StorageObject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gcloud.storage.StorageException;

import java.util.List;
import java.util.Map;

public interface StorageRpc {

  // These options are part of the Google Cloud storage header options
  enum Option {
    PREDEFINED_ACL("predefinedAcl"),
    PREDEFINED_DEFAULT_OBJECT_ACL("predefinedDefaultObjectAcl"),
    IF_METAGENERATION_MATCH("ifMetagenerationMatch"),
    IF_METAGENERATION_NOT_MATCH("ifMetagenerationNotMatch"),
    IF_GENERATION_NOT_MATCH("ifGenerationMatch"),
    IF_GENERATION_MATCH("ifGenerationNotMatch"),
    IF_SOURCE_METAGENERATION_MATCH("ifSourceMetagenerationMatch"),
    IF_SOURCE_METAGENERATION_NOT_MATCH("ifSourceMetagenerationNotMatch"),
    IF_SOURCE_GENERATION_MATCH("ifSourceGenerationMatch"),
    IF_SOURCE_GENERATION_NOT_MATCH("ifSourceGenerationNotMatch"),
    PREFIX("prefix"),
    MAX_RESULTS("maxResults"),
    PAGE_TOKEN("pageToken"),
    DELIMITER("delimiter"),
    VERSIONS("versions");

    private final String value;

    Option(String value) {
      this.value = value;
    }

    public String value() {
      return value;
    }

    @SuppressWarnings("unchecked")
     T get(Map options) {
      return (T) options.get(this);
    }

    String getString(Map options) {
      return get(options);
    }

    Long getLong(Map options) {
      return get(options);
    }

    Boolean getBoolean(Map options) {
      return get(options);
    }
  }

  class Tuple {

    private final X x;
    private final Y y;

    private Tuple(X x, Y y) {
      this.x = x;
      this.y = y;
    }

    public static  Tuple of(X x, Y y) {
      return new Tuple<>(x, y);
    }

    public X x() {
      return x;
    }

    public Y y() {
      return y;
    }
  }

  class BatchRequest {

    public final List>> toDelete;
    public final List>> toUpdate;
    public final List>> toGet;

    public BatchRequest(Iterable>> toDelete,
        Iterable>> toUpdate,
        Iterable>> toGet) {
      this.toDelete = ImmutableList.copyOf(toDelete);
      this.toUpdate = ImmutableList.copyOf(toUpdate);
      this.toGet = ImmutableList.copyOf(toGet);
    }
  }

  class BatchResponse {

    public final Map> deletes;
    public final Map> updates;
    public final Map> gets;

    public BatchResponse(Map> deletes,
        Map> updates,
        Map> gets) {
      this.deletes = ImmutableMap.copyOf(deletes);
      this.updates = ImmutableMap.copyOf(updates);
      this.gets = ImmutableMap.copyOf(gets);
    }
  }

  Bucket create(Bucket bucket, Map options) throws StorageException;

  StorageObject create(StorageObject object, byte[] content, Map options)
      throws StorageException;

  Tuple> list(Map options) throws StorageException;

  Tuple> list(String bucket, Map options)
      throws StorageException;

  Bucket get(Bucket bucket, Map options) throws StorageException;

  StorageObject get(StorageObject object, Map options)
      throws StorageException;

  Bucket patch(Bucket bucket, Map options) throws StorageException;

  StorageObject patch(StorageObject storageObject, Map options)
      throws StorageException;

  boolean delete(Bucket bucket, Map options) throws StorageException;

  boolean delete(StorageObject object, Map options) throws StorageException;

  BatchResponse batch(BatchRequest request) throws StorageException;

  StorageObject compose(Iterable sources, StorageObject target,
      Map targetOptions) throws StorageException;

  StorageObject copy(StorageObject source, Map sourceOptions,
      StorageObject target, Map targetOptions) throws StorageException;

  byte[] load(StorageObject storageObject, Map options)
      throws StorageException;

  byte[] read(StorageObject from, Map options, long position, int bytes)
      throws StorageException;

  String open(StorageObject object, Map options) throws StorageException;

  void write(String uploadId, byte[] toWrite, int toWriteOffset, StorageObject dest,
      long destOffset, int length, boolean last) throws StorageException;
}
\ No newline at end of file
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpcFactory.java b/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpcFactory.java
index f693e63018ce..f4959d617d17 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpcFactory.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpcFactory.java
@@ -16,12 +16,12 @@
 
 package com.google.gcloud.spi;
 
-import com.google.gcloud.storage.StorageServiceOptions;
+import com.google.gcloud.storage.StorageOptions;
 
 /**
  * An interface for Storage RPC factory.
  * Implementation will be loaded via {@link java.util.ServiceLoader}.
  */
-public interface StorageRpcFactory extends ServiceRpcFactory {
+public interface StorageRpcFactory extends ServiceRpcFactory {
 }
 
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchRequest.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchRequest.java
index 4d2f0cab8c96..5cea321b0071 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchRequest.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchRequest.java
@@ -18,8 +18,8 @@
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
-import com.google.gcloud.storage.StorageService.BlobSourceOption;
-import com.google.gcloud.storage.StorageService.BlobTargetOption;
+import com.google.gcloud.storage.Storage.BlobSourceOption;
+import com.google.gcloud.storage.Storage.BlobTargetOption;
 
 import java.io.Serializable;
 import java.util.LinkedHashMap;
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchResponse.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchResponse.java
index 7be1b22f6cc7..1a05fba819c4 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchResponse.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchResponse.java
@@ -40,7 +40,7 @@ public static class Result implements Serializable {
     private static final Result EMPTY = new BatchResponse.Result(null);
 
     private final T value;
-    private final StorageServiceException exception;
+    private final StorageException exception;
 
 
     public Result(T value) {
@@ -48,7 +48,7 @@ public Result(T value) {
       this.exception = null;
     }
 
-    public Result(StorageServiceException exception) {
+    public Result(StorageException exception) {
       this.exception = exception;
       this.value = null;
     }
@@ -60,9 +60,9 @@ static  Result of(T value) {
     /**
      * Returns the result.
      *
-     * @throws StorageServiceException if failed
+     * @throws StorageException if failed
      */
-    public T get() throws StorageServiceException {
+    public T get() throws StorageException {
       if (failed()) {
         throw failure();
       }
@@ -72,7 +72,7 @@ public T get() throws StorageServiceException {
     /**
      * Returns the failure or {@code null} if was successful.
      */
-    public StorageServiceException failure() {
+    public StorageException failure() {
       return exception;
     }
 
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobReadChannelImpl.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobReadChannelImpl.java
index 27d37b127d55..5a5d165751c4 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobReadChannelImpl.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobReadChannelImpl.java
@@ -36,7 +36,7 @@ class BlobReadChannelImpl implements BlobReadChannel {
   private static final int DEFAULT_CHUNK_SIZE = 2 * 1024 * 1024;
   private static final long serialVersionUID = 4821762590742862669L;
 
-  private final StorageServiceOptions serviceOptions;
+  private final StorageOptions serviceOptions;
   private final Blob blob;
   private final Map requestOptions;
   private int position;
@@ -49,7 +49,7 @@ class BlobReadChannelImpl implements BlobReadChannel {
   private transient int bufferPos;
   private transient byte[] buffer;
 
-  BlobReadChannelImpl(StorageServiceOptions serviceOptions, Blob blob,
+  BlobReadChannelImpl(StorageOptions serviceOptions, Blob blob,
       Map requestOptions) {
     this.serviceOptions = serviceOptions;
     this.blob = blob;
@@ -124,7 +124,7 @@ public int read(ByteBuffer byteBuffer) throws IOException {
         public byte[] call() {
           return storageRpc.read(storageObject, requestOptions, position, toRead);
         }
-      }, serviceOptions.retryParams(), StorageServiceImpl.EXCEPTION_HANDLER);
+      }, serviceOptions.retryParams(), StorageImpl.EXCEPTION_HANDLER);
       if (toRead > buffer.length) {
         endOfStream = true;
         if (buffer.length == 0) {
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobWriterChannelImpl.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobWriterChannelImpl.java
index 2b8e66cc33ce..5e41ed00fc54 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobWriterChannelImpl.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobWriterChannelImpl.java
@@ -38,7 +38,7 @@ class BlobWriterChannelImpl implements BlobWriteChannel {
   private static final int MIN_CHUNK_SIZE = 256 * 1024;
   private static final int DEFAULT_CHUNK_SIZE = 8 * MIN_CHUNK_SIZE;
 
-  private final StorageServiceOptions options;
+  private final StorageOptions options;
   private final Blob blob;
   private final String uploadId;
   private int position;
@@ -50,7 +50,7 @@ class BlobWriterChannelImpl implements BlobWriteChannel {
   private transient StorageRpc storageRpc;
   private transient StorageObject storageObject;
 
-  public BlobWriterChannelImpl(StorageServiceOptions options, Blob blob,
+  public BlobWriterChannelImpl(StorageOptions options, Blob blob,
       Map optionsMap) {
     this.options = options;
     this.blob = blob;
@@ -73,7 +73,7 @@ private void flush(boolean compact) {
         public void run() {
           storageRpc.write(uploadId, buffer, 0, storageObject, position, length, false);
         }
-      }), options.retryParams(), StorageServiceImpl.EXCEPTION_HANDLER);
+      }), options.retryParams(), StorageImpl.EXCEPTION_HANDLER);
       position += length;
       limit -= length;
       byte[] temp = new byte[compact ? limit : chunkSize];
@@ -129,7 +129,7 @@ public void close() throws IOException {
         public void run() {
           storageRpc.write(uploadId, buffer, 0, storageObject, position, limit, true);
         }
-      }), options.retryParams(), StorageServiceImpl.EXCEPTION_HANDLER);
+      }), options.retryParams(), StorageImpl.EXCEPTION_HANDLER);
       position += buffer.length;
       isOpen = false;
       buffer = null;
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Cors.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Cors.java
index b1953aa5e0e4..ce8cfb95b6e9 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Cors.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Cors.java
@@ -53,14 +53,10 @@ public Bucket.Cors apply(Cors cors) {
   };
 
   private final Integer maxAgeSeconds;
-  private final ImmutableList methods;
+  private final ImmutableList methods;
   private final ImmutableList origins;
   private final ImmutableList responseHeaders;
 
-  public enum Method {
-    ANY, GET, HEAD, PUT, POST, DELETE
-  }
-
   public static final class Origin implements Serializable {
 
     private static final long serialVersionUID = -4447958124895577993L;
@@ -118,7 +114,7 @@ public String value() {
   public static final class Builder {
 
     private Integer maxAgeSeconds;
-    private ImmutableList methods;
+    private ImmutableList methods;
     private ImmutableList origins;
     private ImmutableList responseHeaders;
 
@@ -129,7 +125,7 @@ public Builder maxAgeSeconds(Integer maxAgeSeconds) {
       return this;
     }
 
-    public Builder methods(Iterable methods) {
+    public Builder methods(Iterable methods) {
       this.methods = methods != null ? ImmutableList.copyOf(methods) : null;
       return this;
     }
@@ -160,7 +156,7 @@ public Integer maxAgeSeconds() {
     return maxAgeSeconds;
   }
 
-  public List methods() {
+  public List methods() {
     return methods;
   }
 
@@ -217,10 +213,10 @@ Bucket.Cors toPb() {
   static Cors fromPb(Bucket.Cors cors) {
     Builder builder = builder().maxAgeSeconds(cors.getMaxAgeSeconds());
     if (cors.getMethod() != null) {
-      builder.methods(transform(cors.getMethod(), new Function() {
+      builder.methods(transform(cors.getMethod(), new Function() {
         @Override
-        public Method apply(String name) {
-          return Method.valueOf(name.toUpperCase());
+        public HttpMethod apply(String name) {
+          return HttpMethod.valueOf(name.toUpperCase());
         }
       }));
     }
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/HttpMethod.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/HttpMethod.java
new file mode 100644
index 000000000000..9d7944140915
--- /dev/null
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/HttpMethod.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gcloud.storage;
+
+/**
+ * Http method supported by Storage service.
+ */
+public enum HttpMethod {
+  GET, HEAD, PUT, POST, DELETE
+}
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageService.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java
similarity index 81%
rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageService.java
rename to gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java
index 6fefb3af3b16..fb5cab108f30 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageService.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java
@@ -20,10 +20,12 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.collect.ImmutableList;
+import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials;
 import com.google.gcloud.Service;
 import com.google.gcloud.spi.StorageRpc;
 
 import java.io.Serializable;
+import java.net.URL;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedHashSet;
@@ -36,7 +38,7 @@
  *
  * @see Google Cloud Storage
  */
-public interface StorageService extends Service {
+public interface Storage extends Service {
 
   enum PredefinedAcl {
     AUTHENTICATED_READ("authenticatedRead"),
@@ -213,6 +215,64 @@ public static BlobListOption recursive(boolean recursive) {
     }
   }
 
+  class SignUrlOption implements Serializable {
+
+    private static final long serialVersionUID = 7850569877451099267L;
+
+    private final Option option;
+    private final Object value;
+
+    enum Option {
+      HTTP_METHOD, CONTENT_TYPE, MD5, SERVICE_ACCOUNT_CRED;
+    }
+
+    private SignUrlOption(Option option, Object value) {
+      this.option = option;
+      this.value = value;
+    }
+
+    Option option() {
+      return option;
+    }
+
+    Object value() {
+      return value;
+    }
+
+    /**
+     * The HTTP method to be used with the signed URL.
+     */
+    public static SignUrlOption httpMethod(HttpMethod httpMethod) {
+      return new SignUrlOption(Option.HTTP_METHOD, httpMethod.name());
+    }
+
+    /**
+     * Use it if signature should include the blob's content-type.
+     * When used, users of the signed URL should include the blob's content-type with their request.
+     */
+    public static SignUrlOption withContentType() {
+      return new SignUrlOption(Option.CONTENT_TYPE, true);
+    }
+
+    /**
+     * Use it if signature should include the blob's md5.
+     * When used, users of the signed URL should include the blob's md5 with their request.
+     */
+    public static SignUrlOption withMd5() {
+      return new SignUrlOption(Option.MD5, true);
+    }
+
+    /**
+     * Service account credentials which are used for signing the URL.
+     * If not provided an attempt will be made to get it from the environment.
+     *
+     * @see Service account
+     */
+    public static SignUrlOption serviceAccount(ServiceAccountAuthCredentials credentials) {
+      return new SignUrlOption(Option.SERVICE_ACCOUNT_CRED, credentials);
+    }
+  }
+
   class ComposeRequest implements Serializable {
 
     private static final long serialVersionUID = -7385681353748590911L;
@@ -411,7 +471,7 @@ public static Builder builder() {
    * Create a new bucket.
    *
    * @return a complete bucket information.
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   Bucket create(Bucket bucket, BucketTargetOption... options);
 
@@ -419,35 +479,35 @@ public static Builder builder() {
    * Create a new blob.
    *
    * @return a complete blob information.
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   Blob create(Blob blob, byte[] content, BlobTargetOption... options);
 
   /**
    * Return the requested bucket or {@code null} if not found.
    *
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   Bucket get(String bucket, BucketSourceOption... options);
 
   /**
    * Return the requested blob or {@code null} if not found.
    *
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   Blob get(String bucket, String blob, BlobSourceOption... options);
 
   /**
    * List the project's buckets.
    *
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   ListResult list(BucketListOption... options);
 
   /**
    * List the bucket's blobs.
    *
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   ListResult list(String bucket, BlobListOption... options);
 
@@ -455,7 +515,7 @@ public static Builder builder() {
    * Update bucket information.
    *
    * @return the updated bucket
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   Bucket update(Bucket bucket, BucketTargetOption... options);
 
@@ -463,7 +523,7 @@ public static Builder builder() {
    * Update blob information.
    *
    * @return the updated blob
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   Blob update(Blob blob, BlobTargetOption... options);
 
@@ -471,7 +531,7 @@ public static Builder builder() {
    * Delete the requested bucket.
    *
    * @return true if bucket was deleted
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   boolean delete(String bucket, BucketSourceOption... options);
 
@@ -479,7 +539,7 @@ public static Builder builder() {
    * Delete the requested blob.
    *
    * @return true if blob was deleted
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   boolean delete(String bucket, String blob, BlobSourceOption... options);
 
@@ -487,7 +547,7 @@ public static Builder builder() {
    * Send a compose request.
    *
    * @return the composed blob.
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   Blob compose(ComposeRequest composeRequest);
 
@@ -495,7 +555,7 @@ public static Builder builder() {
    * Send a copy request.
    *
    * @return the copied blob.
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   Blob copy(CopyRequest copyRequest);
 
@@ -503,7 +563,7 @@ public static Builder builder() {
    * Load the content of the given blob.
    *
    * @return the blob's content.
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   byte[] load(String bucket, String blob, BlobSourceOption... options);
 
@@ -511,21 +571,35 @@ public static Builder builder() {
    * Send a batch request.
    *
    * @return the batch response
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   BatchResponse apply(BatchRequest batchRequest);
 
   /**
    * Return a channel for reading the blob's content.
    *
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   BlobReadChannel reader(String bucket, String blob, BlobSourceOption... options);
 
   /**
    * Create a blob and return a channel for writing its content.
    *
-   * @throws StorageServiceException upon failure
+   * @throws StorageException upon failure
    */
   BlobWriteChannel writer(Blob blob, BlobTargetOption... options);
+
+  /**
+   * Generates a signed URL for a blob.
+   * If you have a blob that you want to allow access to for a fixed
+   * amount of time, you can use this method to generate a URL that
+   * is only valid within a certain time period.
+   * This is particularly useful if you don't want publicly
+   * accessible blobs, but don't want to require users to explicitly log in.
+   *
+   * @param blob the blob associated with the signed url
+   * @param  expirationTimeInSeconds the signed URL expiration (using epoch time)
+   * @see Signed-URLs
+   */
+  URL signUrl(Blob blob, long expirationTimeInSeconds, SignUrlOption... options);
 }
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageServiceException.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageException.java
similarity index 61%
rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageServiceException.java
rename to gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageException.java
index 900a762d46d2..9bf073bef6b4 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageServiceException.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageException.java
@@ -1 +1 @@
-/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.gcloud.storage;

/**
 * Storage service exception.
 *
 * @see Google Cloud
 *      Storage error codes
 */
public class StorageServiceException extends RuntimeException {

  private static final long serialVersionUID = -3748432005065428084L;

  private final int code;
  private final boolean retryable;

  public StorageServiceException(int code, String message, boolean retryable) {
    super(message);
    this.code = code;
    this.retryable = retryable;
  }

  /**
   * Returns the code associated with this exception.
   */
  public int code() {
    return code;
  }

  public boolean retryable() {
    return retryable;
  }
}
\ No newline at end of file
+/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.gcloud.storage;

/**
 * Storage service exception.
 *
 * @see Google Cloud
 *      Storage error codes
 */
public class StorageException extends RuntimeException {

  private static final long serialVersionUID = -3748432005065428084L;

  private final int code;
  private final boolean retryable;

  public StorageException(int code, String message, boolean retryable) {
    super(message);
    this.code = code;
    this.retryable = retryable;
  }

  /**
   * Returns the code associated with this exception.
   */
  public int code() {
    return code;
  }

  public boolean retryable() {
    return retryable;
  }
}
\ No newline at end of file
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageServiceFactory.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageFactory.java
similarity index 62%
rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageServiceFactory.java
rename to gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageFactory.java
index 51c7c1812e6f..e269f0c9d92b 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageServiceFactory.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageFactory.java
@@ -18,26 +18,26 @@
 
 
 /**
- * A base class for StorageService factories.
+ * A base class for Storage factories.
  */
-public abstract class StorageServiceFactory {
+public abstract class StorageFactory {
 
-  private static final StorageServiceFactory INSTANCE = new StorageServiceFactory() {
+  private static final StorageFactory INSTANCE = new StorageFactory() {
     @Override
-    public StorageService get(StorageServiceOptions options) {
-      return new StorageServiceImpl(options);
+    public Storage get(StorageOptions options) {
+      return new StorageImpl(options);
     }
   };
 
   /**
    * Returns the default factory instance.
    */
-  public static StorageServiceFactory instance() {
+  public static StorageFactory instance() {
     return INSTANCE;
   }
 
   /**
-   * Returns a {@code StorageService} for the given options.
+   * Returns a {@code Storage} service for the given options.
    */
-  public abstract StorageService get(StorageServiceOptions options);
+  public abstract Storage get(StorageOptions options);
 }
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java
similarity index 82%
rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java
rename to gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java
index 95b95141be14..29db26481935 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java
@@ -29,7 +29,7 @@
 import static com.google.gcloud.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_MATCH;
 import static com.google.gcloud.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_NOT_MATCH;
 import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
-import static java.util.concurrent.Executors.callable;
+import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.api.services.storage.model.StorageObject;
 import com.google.common.base.Function;
@@ -39,7 +39,9 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import com.google.common.io.BaseEncoding;
 import com.google.common.primitives.Ints;
+import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials;
 import com.google.gcloud.BaseService;
 import com.google.gcloud.ExceptionHandler;
 import com.google.gcloud.ExceptionHandler.Interceptor;
@@ -47,13 +49,22 @@
 import com.google.gcloud.spi.StorageRpc.Tuple;
 
 import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
 import java.util.Arrays;
+import java.util.EnumMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Callable;
 
-final class StorageServiceImpl extends BaseService implements StorageService {
+final class StorageImpl extends BaseService implements Storage {
 
   private static final Interceptor EXCEPTION_HANDLER_INTERCEPTOR = new Interceptor() {
 
@@ -66,8 +77,8 @@ public RetryResult afterEval(Exception exception, RetryResult retryResult) {
 
     @Override
     public RetryResult beforeEval(Exception exception) {
-      if (exception instanceof StorageServiceException) {
-        boolean retriable = ((StorageServiceException) exception).retryable();
+      if (exception instanceof StorageException) {
+        boolean retriable = ((StorageException) exception).retryable();
         return retriable ? Interceptor.RetryResult.RETRY : Interceptor.RetryResult.ABORT;
       }
       return null;
@@ -79,13 +90,11 @@ public RetryResult beforeEval(Exception exception) {
 
   private final StorageRpc storageRpc;
 
-  StorageServiceImpl(StorageServiceOptions options) {
+  StorageImpl(StorageOptions options) {
     super(options);
     storageRpc = options.storageRpc();
-    // todo: replace nulls with Value.asNull (per toPb)
     // todo: configure timeouts - https://developers.google.com/api-client-library/java/google-api-java-client/errors
     // todo: provide rewrite - https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite
-    // todo: provide signed urls - https://cloud.google.com/storage/docs/access-control#Signed-URLs
     // todo: check if we need to expose https://cloud.google.com/storage/docs/json_api/v1/bucketAccessControls/insert vs using bucket update/patch
   }
 
@@ -124,7 +133,7 @@ public Bucket get(String bucket, BucketSourceOption... options) {
           public com.google.api.services.storage.model.Bucket call() {
             try {
               return storageRpc.get(bucketPb, optionsMap);
-            } catch (StorageServiceException ex) {
+            } catch (StorageException ex) {
               if (ex.code() == HTTP_NOT_FOUND) {
                 return null;
               }
@@ -144,7 +153,7 @@ public Blob get(String bucket, String blob, BlobSourceOption... options) {
       public StorageObject call() {
         try {
           return storageRpc.get(storedObject, optionsMap);
-        } catch (StorageServiceException ex) {
+        } catch (StorageException ex) {
           if (ex.code() == HTTP_NOT_FOUND) {
             return null;
           }
@@ -160,9 +169,9 @@ private static abstract class BasePageFetcher
 
     private static final long serialVersionUID = 8236329004030295223L;
     protected final Map requestOptions;
-    protected final StorageServiceOptions serviceOptions;
+    protected final StorageOptions serviceOptions;
 
-    BasePageFetcher(StorageServiceOptions serviceOptions, String cursor,
+    BasePageFetcher(StorageOptions serviceOptions, String cursor,
         Map optionMap) {
       this.serviceOptions = serviceOptions;
       ImmutableMap.Builder builder = ImmutableMap.builder();
@@ -180,7 +189,7 @@ private static class BucketPageFetcher extends BasePageFetcher {
 
     private static final long serialVersionUID = -5490616010200159174L;
 
-    BucketPageFetcher(StorageServiceOptions serviceOptions, String cursor,
+    BucketPageFetcher(StorageOptions serviceOptions, String cursor,
         Map optionMap) {
       super(serviceOptions, cursor, optionMap);
     }
@@ -196,7 +205,7 @@ private static class BlobPageFetcher extends BasePageFetcher {
     private static final long serialVersionUID = -5490616010200159174L;
     private final String bucket;
 
-    BlobPageFetcher(String bucket, StorageServiceOptions serviceOptions, String cursor,
+    BlobPageFetcher(String bucket, StorageOptions serviceOptions, String cursor,
         Map optionMap) {
       super(serviceOptions, cursor, optionMap);
       this.bucket = bucket;
@@ -213,7 +222,7 @@ public ListResult list(BucketListOption... options) {
     return listBuckets(options(), optionMap(options));
   }
 
-  private static ListResult listBuckets(final StorageServiceOptions serviceOptions,
+  private static ListResult listBuckets(final StorageOptions serviceOptions,
       final Map optionsMap) {
     Tuple> result = runWithRetries(
         new Callable>>() {
@@ -239,7 +248,7 @@ public ListResult list(final String bucket, BlobListOption... options) {
   }
 
   private static ListResult listBlobs(final String bucket,
-      final StorageServiceOptions serviceOptions, final Map optionsMap) {
+      final StorageOptions serviceOptions, final Map optionsMap) {
     Tuple> result = runWithRetries(
         new Callable>>() {
           @Override
@@ -396,16 +405,16 @@ public BatchResponse apply(BatchRequest batchRequest) {
 
   private  List> transformBatchResult(
       Iterable>> request,
-      Map> results, Function transform,
+      Map> results, Function transform,
       int... nullOnErrorCodes) {
     Set nullOnErrorCodesSet = Sets.newHashSet(Ints.asList(nullOnErrorCodes));
     List> response = Lists.newArrayListWithCapacity(results.size());
     for (Tuple tuple : request) {
-      Tuple result = results.get(tuple.x());
+      Tuple result = results.get(tuple.x());
       if (result.x() != null) {
         response.add(BatchResponse.Result.of(transform.apply(result.x())));
       } else {
-        StorageServiceException exception = result.y();
+        StorageException exception = result.y();
         if (nullOnErrorCodesSet.contains(exception.code())) {
           //noinspection unchecked
           response.add(BatchResponse.Result.empty());
@@ -429,6 +438,69 @@ public BlobWriteChannel writer(Blob blob, BlobTargetOption... options) {
     return new BlobWriterChannelImpl(options(), blob, optionsMap);
   }
 
+  @Override
+  public URL signUrl(Blob blob, long expiration, SignUrlOption... options) {
+    EnumMap optionMap = Maps.newEnumMap(SignUrlOption.Option.class);
+    for (SignUrlOption option : options) {
+      optionMap.put(option.option(), option.value());
+    }
+    ServiceAccountAuthCredentials cred =
+        (ServiceAccountAuthCredentials) optionMap.get(SignUrlOption.Option.SERVICE_ACCOUNT_CRED);
+    if (cred == null) {
+      checkArgument(options().authCredentials() instanceof ServiceAccountAuthCredentials,
+          "Signing key was not provided and could not be derived");
+      cred = (ServiceAccountAuthCredentials) this.options().authCredentials();
+    }
+    // construct signature data - see https://cloud.google.com/storage/docs/access-control#Signed-URLs
+    StringBuilder stBuilder = new StringBuilder();
+    if (optionMap.containsKey(SignUrlOption.Option.HTTP_METHOD)) {
+      stBuilder.append(optionMap.get(SignUrlOption.Option.HTTP_METHOD));
+    } else {
+      stBuilder.append(HttpMethod.GET);
+    }
+    stBuilder.append('\n');
+    if (firstNonNull((Boolean) optionMap.get(SignUrlOption.Option.MD5) , false)) {
+      checkArgument(blob.md5() != null, "Blob is missing a value for md5");
+      stBuilder.append(blob.md5());
+    }
+    stBuilder.append('\n');
+    if (firstNonNull((Boolean) optionMap.get(SignUrlOption.Option.CONTENT_TYPE) , false)) {
+      checkArgument(blob.contentType() != null, "Blob is missing a value for content-type");
+      stBuilder.append(blob.contentType());
+    }
+    stBuilder.append('\n');
+    stBuilder.append(expiration).append('\n');
+    StringBuilder path = new StringBuilder();
+    if (!blob.bucket().startsWith("/")) {
+      path.append('/');
+    }
+    path.append(blob.bucket());
+    if (!blob.bucket().endsWith("/")) {
+      path.append('/');
+    }
+    if (blob.name().startsWith("/")) {
+      path.setLength(stBuilder.length() - 1);
+    }
+    path.append(blob.name());
+    stBuilder.append(path);
+    try {
+      Signature signer = Signature.getInstance("SHA256withRSA");
+      signer.initSign(cred.privateKey());
+      signer.update(stBuilder.toString().getBytes(UTF_8));
+      String signature =
+          URLEncoder.encode(BaseEncoding.base64().encode(signer.sign()), UTF_8.name());
+      stBuilder = new StringBuilder("https://storage.googleapis.com").append(path);
+      stBuilder.append("?GoogleAccessId=").append(cred.account());
+      stBuilder.append("&Expires=").append(expiration);
+      stBuilder.append("&Signature=").append(signature);
+      return new URL(stBuilder.toString());
+    } catch (MalformedURLException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
+      throw new IllegalStateException(e);
+    } catch (SignatureException | InvalidKeyException e) {
+      throw new IllegalArgumentException("Invalid service account private key");
+    }
+  }
+
   private Map optionMap(Long generation, Long metaGeneration,
       Iterable options) {
     return optionMap(generation, metaGeneration, options, false);
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageServiceOptions.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java
similarity index 83%
rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageServiceOptions.java
rename to gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java
index 1bb10743de68..9e4ba2b72407 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageServiceOptions.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java
@@ -26,7 +26,7 @@
 import java.util.Objects;
 import java.util.Set;
 
-public class StorageServiceOptions extends ServiceOptions {
+public class StorageOptions extends ServiceOptions {
 
   private static final long serialVersionUID = -7804860602287801084L;
   private static final String GCS_SCOPE = "https://www.googleapis.com/auth/devstorage.full_control";
@@ -37,13 +37,13 @@ public class StorageServiceOptions extends ServiceOptions {
+      ServiceOptions.Builder {
 
     private String pathDelimiter;
 
     private Builder() {}
 
-    private Builder(StorageServiceOptions options) {
+    private Builder(StorageOptions options) {
       super(options);
     }
 
@@ -53,12 +53,12 @@ public Builder pathDelimiter(String pathDelimiter) {
     }
 
     @Override
-    public StorageServiceOptions build() {
-      return new StorageServiceOptions(this);
+    public StorageOptions build() {
+      return new StorageOptions(this);
     }
   }
 
-  private StorageServiceOptions(Builder builder) {
+  private StorageOptions(Builder builder) {
     super(builder);
     pathDelimiter = MoreObjects.firstNonNull(builder.pathDelimiter, DEFAULT_PATH_DELIMITER);
     // todo: consider providing read-timeout
@@ -100,14 +100,14 @@ public int hashCode() {
 
   @Override
   public boolean equals(Object obj) {
-    if (!(obj instanceof StorageServiceOptions)) {
+    if (!(obj instanceof StorageOptions)) {
       return false;
     }
-    StorageServiceOptions other = (StorageServiceOptions) obj;
+    StorageOptions other = (StorageOptions) obj;
     return isEquals(other) && Objects.equals(pathDelimiter, other.pathDelimiter);
   }
 
-  public static StorageServiceOptions defaultInstance() {
+  public static StorageOptions defaultInstance() {
     return builder().build();
   }
 
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/package-info.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/package-info.java
index c02b743d0977..c648ea88e39d 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/package-info.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/package-info.java
@@ -19,8 +19,8 @@
  *
  * 

A simple usage example: *

{@code
- * StorageServiceOptions options = StorageServiceOptions.builder().projectId("project").build();
- * StorageService storage = StorageServiceFactory.instance().get(options);
+ * StorageOptions options = StorageOptions.builder().projectId("project").build();
+ * Storage storage = StorageFactory.instance().get(options);
  * byte[] content = readContent();
  * Blob blob = storage.get("bucket", "blob_name");
  * if (blob == null) {
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchRequestTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchRequestTest.java
index dbe0eaa19411..29be5b87e08f 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchRequestTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchRequestTest.java
@@ -16,14 +16,14 @@
 
 package com.google.gcloud.storage;
 
-import static com.google.gcloud.storage.StorageService.PredefinedAcl.PUBLIC_READ;
+import static com.google.gcloud.storage.Storage.PredefinedAcl.PUBLIC_READ;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import com.google.common.collect.Iterables;
-import com.google.gcloud.storage.StorageService.BlobSourceOption;
-import com.google.gcloud.storage.StorageService.BlobTargetOption;
+import com.google.gcloud.storage.Storage.BlobSourceOption;
+import com.google.gcloud.storage.Storage.BlobTargetOption;
 
 import org.junit.Test;
 
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CorsTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CorsTest.java
index 8b0379f03583..f978cb87f3d1 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CorsTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CorsTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 
 import com.google.common.collect.ImmutableList;
-import com.google.gcloud.storage.Cors.Method;
 import com.google.gcloud.storage.Cors.Origin;
 
 import org.junit.Test;
@@ -39,7 +38,7 @@ public void testOrigin() {
   public void corsTest() {
     List origins = ImmutableList.of(Origin.any(), Origin.of("o"));
     List headers = ImmutableList.of("h1", "h2");
-    List methods = ImmutableList.of(Method.ANY);
+    List methods = ImmutableList.of(HttpMethod.GET);
     Cors cors = Cors.builder()
         .maxAgeSeconds(100)
         .origins(origins)
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java
index 4960c49113ef..d765b0189c96 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java
@@ -52,26 +52,26 @@ public class SerializationTest {
       Collections.>emptyList());
   private static final ListResult LIST_RESULT =
       new ListResult<>(null, "c", Collections.singletonList(Blob.of("b", "n")));
-  private static StorageService.BlobListOption BLOB_LIST_OPTIONS =
-      StorageService.BlobListOption.maxResults(100);
-  private static StorageService.BlobSourceOption BLOB_SOURCE_OPTIONS =
-      StorageService.BlobSourceOption.generationMatch(1);
-  private static StorageService.BlobTargetOption BLOB_TARGET_OPTIONS =
-      StorageService.BlobTargetOption.generationMatch();
-  private static StorageService.BucketListOption BUCKET_LIST_OPTIONS =
-      StorageService.BucketListOption.prefix("bla");
-  private static StorageService.BucketSourceOption BUCKET_SOURCE_OPTIONS =
-      StorageService.BucketSourceOption.metagenerationMatch(1);
-  private static StorageService.BucketTargetOption BUCKET_TARGET_OPTIONS =
-      StorageService.BucketTargetOption.metagenerationNotMatch();
+  private static Storage.BlobListOption BLOB_LIST_OPTIONS =
+      Storage.BlobListOption.maxResults(100);
+  private static Storage.BlobSourceOption BLOB_SOURCE_OPTIONS =
+      Storage.BlobSourceOption.generationMatch(1);
+  private static Storage.BlobTargetOption BLOB_TARGET_OPTIONS =
+      Storage.BlobTargetOption.generationMatch();
+  private static Storage.BucketListOption BUCKET_LIST_OPTIONS =
+      Storage.BucketListOption.prefix("bla");
+  private static Storage.BucketSourceOption BUCKET_SOURCE_OPTIONS =
+      Storage.BucketSourceOption.metagenerationMatch(1);
+  private static Storage.BucketTargetOption BUCKET_TARGET_OPTIONS =
+      Storage.BucketTargetOption.metagenerationNotMatch();
 
   @Test
   public void testServiceOptions() throws Exception {
-    StorageServiceOptions options = StorageServiceOptions.builder()
+    StorageOptions options = StorageOptions.builder()
         .projectId("p1")
         .authCredentials(AuthCredentials.createForAppEngine())
         .build();
-    StorageServiceOptions serializedCopy = serializeAndDeserialize(options);
+    StorageOptions serializedCopy = serializeAndDeserialize(options);
     assertEquals(options, serializedCopy);
 
     options = options.toBuilder()
diff --git a/pom.xml b/pom.xml
index 0eaf0a920fa3..6705f19f6ee9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -85,6 +85,19 @@
     
   
   
+    
+      
+        
+          org.codehaus.mojo
+          exec-maven-plugin
+          1.3.2
+          
+            true
+            java
+          
+        
+      
+    
     
       
         org.apache.maven.plugins
diff --git a/src/main/java/com/google/gcloud/storage/HttpMethod.java b/src/main/java/com/google/gcloud/storage/HttpMethod.java
new file mode 100644
index 000000000000..f5889aedae90
--- /dev/null
+++ b/src/main/java/com/google/gcloud/storage/HttpMethod.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gcloud.storage;
+
+/**
+ *
+ */
+public enum HttpMethod {
+  GET, HEAD, PUT, POST, DELETE
+}