/* * JBoss, Home of Professional Open Source. * Copyright 2012, 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.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.Future; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import com.google.appengine.api.datastore.BaseDatastoreService; import com.google.appengine.api.datastore.DatastoreServiceConfig; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.PreparedQuery; import com.google.appengine.api.datastore.Query; import com.google.appengine.api.datastore.Transaction; import com.google.common.base.Function; import com.google.common.collect.Lists; /** * Abstract base DatastoreService impl. * * @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a> */ public abstract class AbstractDatastoreService implements BaseDatastoreService, CurrentTransactionProvider { private final DatastoreServiceInternal datastoreService; public AbstractDatastoreService(DatastoreServiceInternal datastoreService) { this.datastoreService = datastoreService; } protected DatastoreCallbacks getDatastoreCallbacks() { return getDelegate().getDatastoreCallbacks(); } DatastoreServiceInternal getDelegate() { return datastoreService; } protected abstract CurrentTransactionProvider postTxProvider(final Transaction tx); protected abstract <T> Future<T> wrap(final Transaction transaction, final Callable<T> callable, final Runnable pre, final Function<T, Void> post); protected int getStatus(javax.transaction.Transaction tx) { try { return tx.getStatus(); } catch (SystemException e) { throw new RuntimeException(e); } } protected boolean isNotActive(javax.transaction.Transaction tx) { final int status = getStatus(tx); return (status != Status.STATUS_ACTIVE); // TODO -- more fine-grained? } protected boolean isActive(javax.transaction.Transaction tx) { return (getStatus(tx) == Status.STATUS_ACTIVE); } protected void handlePost(javax.transaction.Transaction tx, final Runnable post) { if (tx == null || isNotActive(tx)) { post.run(); } else if (isActive(tx)) { try { tx.registerSynchronization(new Synchronization() { public void beforeCompletion() { } public void afterCompletion(int status) { if (status == Status.STATUS_COMMITTED) { post.run(); } } }); } catch (Exception e) { throw new RuntimeException(e); } } } protected <T> Future<T> handleGetWithPost(final Future<T> wrap, final javax.transaction.Transaction tx, final Runnable post) { return new PostFuture<T>(wrap) { protected void after(T result) { handlePost(tx, post); } }; } protected Future<Entity> doGet(final Transaction transaction, final Key key) { final Map<Key, Entity> map = new LinkedHashMap<Key, Entity>(); final Runnable pre = new Runnable() { public void run() { getDatastoreCallbacks().executePreGetCallbacks(AbstractDatastoreService.this, Lists.newArrayList(key), map); } }; final Function<Entity, Void> post = new Function<Entity, Void>() { public Void apply(Entity input) { if (input != null) { getDatastoreCallbacks().executePostLoadCallbacks(postTxProvider(transaction), input); } return null; } }; return wrap(transaction, new Callable<Entity>() { public Entity call() throws Exception { final Entity previous = map.get(key); return (previous == null) ? getDelegate().get(transaction, key) : previous; } }, pre, post); } protected Future<Key> doPut(final Transaction transaction, final Entity entity, final boolean applyPost) { final boolean isSpecialKind = KindUtils.inProgress(KindUtils.Type.METADATA); final Runnable pre = new Runnable() { public void run() { if (isSpecialKind == false) { getDatastoreCallbacks().executePrePutCallbacks(AbstractDatastoreService.this, Lists.newArrayList(entity)); } } }; final Runnable post = new Runnable() { public void run() { if (isSpecialKind == false) { getDatastoreCallbacks().executePostPutCallbacks(postTxProvider(transaction), Lists.newArrayList(entity)); } } }; final Future<Key> wrap = wrap(transaction, new Callable<Key>() { public Key call() throws Exception { return getDelegate().put(transaction, entity, applyPost ? post : null); } }, pre, null); // do not add post! if (applyPost) { return wrap; } else { return handleGetWithPost(wrap, CapedwarfTransaction.getTx(), post); } } @AutoTx public PreparedQuery prepare(Query query) { getDatastoreCallbacks().executePreQueryCallbacks(this, query); return getDelegate().prepare(query); } @AutoTx public PreparedQuery prepare(Transaction transaction, Query query) { getDatastoreCallbacks().executePreQueryCallbacks(this, query); return getDelegate().prepare(transaction, query); } @Deadline public Transaction getCurrentTransaction() { return getDelegate().getCurrentTransaction(); } @Deadline public Transaction getCurrentTransaction(Transaction transaction) { return getDelegate().getCurrentTransaction(transaction); } @Deadline public Collection<Transaction> getActiveTransactions() { return getDelegate().getActiveTransactions(); } public DatastoreServiceConfig getDatastoreServiceConfig() { return getDelegate().getDatastoreServiceConfig(); } public Iterator<Entity> getAllEntitiesIterator() { return getDelegate().getAllEntitiesIterator(); } /** * Testing only! */ public void clearCache() { getDelegate().clearCache(); } }