/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.capedwarf.datastore;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import com.google.appengine.api.datastore.DatastoreAttributes;
import com.google.appengine.api.datastore.DatastoreServiceConfig;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Index;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyRange;
import com.google.appengine.api.datastore.Transaction;
import com.google.appengine.api.datastore.TransactionOptions;
import com.google.appengine.api.utils.FutureWrapper;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import org.jboss.capedwarf.common.async.Wrappers;
import org.jboss.capedwarf.common.threads.DirectFuture;
/**
* JBoss async DatastoreService impl.
*
* @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a>
*/
public class CapedwarfAsyncDatastoreService extends AbstractDatastoreService implements ExposedAsyncDatastoreService {
public CapedwarfAsyncDatastoreService() {
this(null);
}
public CapedwarfAsyncDatastoreService(DatastoreServiceConfig config) {
super(DatastoreServiceImpl.async(config));
}
protected static <T> Future<T> wrap(final Callable<T> callable) {
return Wrappers.future(callable);
}
protected <T> Future<T> tx(final Callable<T> callable) {
return wrap(getCurrentTransaction(null), callable, null, null);
}
protected CurrentTransactionProvider postTxProvider(final Transaction tx) {
return new CurrentTransactionProvider() {
public Transaction getCurrentTransaction(Transaction transaction) {
return (tx != null) ? tx : transaction;
}
};
}
protected <T> Callable<T> env(final Callable<T> callable) {
return Wrappers.wrap(callable);
}
protected <T> Future<T> wrap(final Transaction transaction, final Callable<T> callable, final Runnable pre, final Function<T, Void> post) {
if (pre != null) {
pre.run();
}
final TransactionWrapper tw = CapedwarfTransaction.getTxWrapper(transaction);
final Future<T> wrap = wrap(env(new Callable<T>() {
public T call() throws Exception {
CapedwarfTransaction.attach(tw);
try {
return callable.call();
} finally {
CapedwarfTransaction.detach(tw);
}
}
}));
return new PostFuture<T>(wrap) {
protected void after(T result) {
if (post != null) {
post.apply(result);
}
}
};
}
public Future<Transaction> beginTransaction() {
return beginTransaction(TransactionOptions.Builder.withDefaults());
}
public Future<Transaction> beginTransaction(final TransactionOptions transactionOptions) {
return DirectFuture.create(new Callable<Transaction>() {
public Transaction call() throws Exception {
return getDelegate().beginTransaction(transactionOptions);
}
});
}
@TxTask
@Deadline
public Future<Entity> get(final Key key) {
return get(getCurrentTransaction(null), key);
}
@TxTask
@Deadline
public Future<Entity> get(Transaction transaction, final Key key) {
Future<Entity> future = doGet(transaction, key);
return new FutureWrapper<Entity, Entity>(future) {
protected Entity wrap(Entity entity) throws Exception {
if (entity == null)
throw new EntityNotFoundException(key);
return entity;
}
protected Throwable convertException(Throwable throwable) {
return throwable;
}
};
}
@TxTask
@Deadline
public Future<Map<Key, Entity>> get(final Iterable<Key> keyIterable) {
return get(getCurrentTransaction(null), keyIterable);
}
@TxTask
@Deadline
public Future<Map<Key, Entity>> get(final Transaction transaction, final Iterable<Key> keyIterable) {
final Map<Key, Entity> map = new LinkedHashMap<Key, Entity>();
getDatastoreCallbacks().executePreGetCallbacks(this, Lists.newArrayList(keyIterable), map);
final List<Key> requiredKeys = Lists.newArrayList(keyIterable);
if (map.isEmpty() == false) {
requiredKeys.removeAll(map.keySet()); // remove manually added keys
}
final TransactionWrapper tw = CapedwarfTransaction.getTxWrapper(transaction);
final Future<Map<Key, Entity>> wrap = wrap(env(new Callable<Map<Key, Entity>>() {
public Map<Key, Entity> call() throws Exception {
CapedwarfTransaction.attach(tw);
try {
getDelegate().get(transaction, requiredKeys, map);
return map;
} finally {
CapedwarfTransaction.detach(tw);
}
}
}));
return new PostFuture<Map<Key,Entity>>(wrap) {
protected void after(Map<Key, Entity> result) {
getDatastoreCallbacks().executePostLoadCallbacks(postTxProvider(transaction), Lists.newArrayList(map.values()));
}
};
}
@TxTask
@Deadline
public Future<Key> put(final Entity entity) {
return put(getCurrentTransaction(null), entity);
}
@TxTask
@Deadline
public Future<List<Key>> put(final Iterable<Entity> entityIterable) {
return put(getCurrentTransaction(null), entityIterable);
}
@TxTask
@Deadline
public Future<Key> put(Transaction transaction, Entity entity) {
return doPut(transaction, entity, false);
}
@TxTask
@Deadline
public Future<List<Key>> put(final Transaction transaction, final Iterable<Entity> entityIterable) {
getDatastoreCallbacks().executePrePutCallbacks(this, Lists.newArrayList(entityIterable));
final Runnable post = new Runnable() {
public void run() {
getDatastoreCallbacks().executePostPutCallbacks(postTxProvider(transaction), Lists.newArrayList(entityIterable));
}
};
final TransactionWrapper tw = CapedwarfTransaction.getTxWrapper(transaction);
final Future<List<Key>> wrap = wrap(env(new Callable<List<Key>>() {
public List<Key> call() throws Exception {
CapedwarfTransaction.attach(tw);
try {
final List<Key> keys = new ArrayList<Key>();
keys.addAll(getDelegate().put(transaction, entityIterable, null)); // do not post, until get is called
return keys;
} finally {
CapedwarfTransaction.detach(tw);
}
}
}));
return handleGetWithPost(wrap, CapedwarfTransaction.getTx(), post);
}
@TxTask
@Deadline
public Future<Void> delete(final Key... keys) {
return delete(getCurrentTransaction(null), keys);
}
@TxTask
@Deadline
public Future<Void> delete(final Transaction transaction, final Key... keys) {
return delete(transaction, Arrays.asList(keys));
}
@TxTask
@Deadline
public Future<Void> delete(final Iterable<Key> keyIterable) {
return delete(getCurrentTransaction(null), keyIterable);
}
@TxTask
@Deadline
public Future<Void> delete(final Transaction transaction, final Iterable<Key> keyIterable) {
getDatastoreCallbacks().executePreDeleteCallbacks(this, Lists.newArrayList(keyIterable));
final Runnable post = new Runnable() {
public void run() {
getDatastoreCallbacks().executePostDeleteCallbacks(postTxProvider(transaction), Lists.newArrayList(keyIterable));
}
};
final TransactionWrapper tw = CapedwarfTransaction.getTxWrapper(transaction);
final Future<Void> wrap = wrap(env(new Callable<Void>() {
public Void call() throws Exception {
CapedwarfTransaction.attach(tw);
try {
getDelegate().delete(transaction, keyIterable, null); // do not delete until get is called
return null;
} finally {
CapedwarfTransaction.detach(tw);
}
}
}));
return handleGetWithPost(wrap, CapedwarfTransaction.getTx(), post);
}
@Deadline
public Future<KeyRange> allocateIds(final String s, final long l) {
return allocateIds(null, s, l);
}
@Deadline
public Future<KeyRange> allocateIds(final Key key, final String s, final long l) {
return tx(new Callable<KeyRange>() {
public KeyRange call() throws Exception {
return getDelegate().allocateIds(key, s, l);
}
});
}
@Deadline
public Future<DatastoreAttributes> getDatastoreAttributes() {
return wrap(env(new Callable<DatastoreAttributes>() {
public DatastoreAttributes call() throws Exception {
return getDelegate().getDatastoreAttributes();
}
}));
}
@Deadline
public Future<Map<Index, Index.IndexState>> getIndexes() {
return wrap(env(new Callable<Map<Index, Index.IndexState>>() {
public Map<Index, Index.IndexState> call() throws Exception {
return getDelegate().getIndexes();
}
}));
}
}