Android Open Source - android-sqlite-server Server Impl






From Project

Back to project page android-sqlite-server.

License

The source code is released under:

Apache License

If you think the Android project android-sqlite-server listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package org.devtcg.sqliteserver.impl.binder;
// w w w.java2s. c o m
import android.database.sqlite.SQLiteException;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import org.devtcg.sqliteserver.impl.ExecutorHelper;
import org.devtcg.sqliteserver.impl.SQLiteExecutor;
import org.devtcg.sqliteserver.impl.binder.protocol.AbstractCommandMessage;
import org.devtcg.sqliteserver.impl.binder.protocol.ExceptionTransportHelper;
import org.devtcg.sqliteserver.impl.binder.protocol.MethodName;

import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;

import static android.os.IBinder.DeathRecipient;
import static org.devtcg.sqliteserver.impl.binder.protocol.AcquireCommand.AcquireHandler;
import static org.devtcg.sqliteserver.impl.binder.protocol.BeginTransactionCommand.BeginTransactionHandler;
import static org.devtcg.sqliteserver.impl.binder.protocol.DeleteCommand.DeleteHandler;
import static org.devtcg.sqliteserver.impl.binder.protocol.EndTransactionCommand.EndTransactionHandler;
import static org.devtcg.sqliteserver.impl.binder.protocol.ExecSQLCommand.ExecSQLHandler;
import static org.devtcg.sqliteserver.impl.binder.protocol.InsertCommand.InsertHandler;
import static org.devtcg.sqliteserver.impl.binder.protocol.RawQueryCommand.RawQueryHandler;
import static org.devtcg.sqliteserver.impl.binder.protocol.ReleaseCommand.ReleaseHandler;
import static org.devtcg.sqliteserver.impl.binder.protocol.SetTransactionSuccessfulCommand.SetTransactionSuccessfulHandler;
import static org.devtcg.sqliteserver.impl.binder.protocol.UpdateCommand.UpdateHandler;

/**
 * Unflattens server command messages expressed in a Bundle and delegates them to the
 * {@link SQLiteExecutor}.  Then, packages up the response back into a Bundle for
 * {@link AbstractBinderClient} to process on the other end.
 * <p>
 * Requests sent here will be executed on a dedicated thread per client connection.  This is
 * to preserve the thread affinity normally observed with a local
 * {@link android.database.sqlite.SQLiteDatabase} instance.
 */
public class ServerImpl {
    private final String mTag;
    private final SQLiteExecutor mExecutor;
    private final String mServerName;

    /**
     * Each unique client connection gets its own separate server state simulating the
     * ThreadLocal nature of SQLiteDatabase through our IPC interface.  For example,
     * transaction isolation is guaranteed on a per thread, per open database basis
     * with SQLiteDatabase, so we must simulate this when even multiple connections may
     * be served by the same Binder thread.
     * <p>
     * TODO: Currently we're not ensuring that each unique client runs on its own thread,
     * breaking an important part of transaction isolation.
     */
    private final Map<IBinder, ServerState> mServerStateMap =
            new ConcurrentHashMap<IBinder, ServerState>();

    public ServerImpl(String logTag, SQLiteExecutor executor, String serverName) {
        mTag = logTag;
        mExecutor = executor;
        mServerName = serverName;
    }

    public SQLiteExecutor getExecutor() {
        return mExecutor;
    }

    /**
     * Used by the service implementation to trigger close.  Need to revisit
     * this policy at some point in the future.  It's hard to decide whether it's
     * best to have the ContentProvider and Service implementations try to match
     * each other semantically or if there is cause to offer a more flexible model
     * with the Service implementation.
     * <p>
     * The ContentProvider currently never closes the database, and never shuts down, per usual
     * with the ContentProvider paradigm.
     */
    public void closeDatabase() {
        mExecutor.close();
    }

    public ServerState getServerState(BinderHandle clientHandle) {
        return mServerStateMap.get(clientHandle.asBinder());
    }

    public void initServerState(BinderHandle clientHandle)
            throws SQLiteServerProtocolException, RemoteException {
        if (getServerState(clientHandle) != null) {
            throw new SQLiteServerProtocolException("Refusing to handle multiple " +
                    "acquire operations");
        }

        // Handle cleanup if the client disappears.
        ClientDiedHandler deathHandler = new ClientDiedHandler(clientHandle);
        clientHandle.asBinder().linkToDeath(deathHandler, 0);

        ServerState state = new ServerState();
        state.deathRecipient = deathHandler;
        state.clientHandle = clientHandle;
        state.executor = ExecutorHelper.createThreadAffinityExecutor();
        mServerStateMap.put(clientHandle.asBinder(), state);
    }

    public void performRelease(final ServerState state) {
        if (state != null) {
            state.clientHandle.asBinder().unlinkToDeath(state.deathRecipient, 0);
            try {
                state.executor.runSynchronously(new Callable<Bundle>() {
                    public Bundle call() {
                        endTransactionsIfNecessary(state);
                        return null;
                    }
                });
            } catch (ExecutionException e) {
                // Doesn't throw any checked exceptions...
                throw new RuntimeException(e.getCause());
            }
            mServerStateMap.remove(state.clientHandle.asBinder());
            state.executor.shutdown();
        }
    }

    private void endTransactionsIfNecessary(ServerState state) {
        if (state.numTransactions > 0) {
            Log.i(mTag, "Forcefully ending " + state.numTransactions + " transactions");
            while (state.numTransactions > 0) {
                // Note that if the client had eeked out a setTransactionSuccessful before
                // dying this will actually commit instead of rollback.
                getExecutor().endTransaction();
                state.numTransactions--;
            }
        }
    }

    /**
     * This code path smells a little too heavy for individual database operations (consider
     * a large number of inserts).  Need to benchmark the performance versus ContentProvider
     * and also versus SQLiteDatabase.
     */
    public Bundle onTransact(Bundle request) {
        request.setClassLoader(getClass().getClassLoader());
        MethodName methodName = null;
        try {
            int methodNameOrdinal = BundleUtils.getIntOrThrow(request,
                    AbstractCommandMessage.KEY_METHOD_NAME);
            methodName = MethodName.values()[methodNameOrdinal];
            return runOnTransact(methodName, request);
        } catch (SQLiteServerProtocolException e) {
            return replyWithError(e);
        } catch (SQLiteException e) {
            return replyWithError(e);
        } catch (RuntimeException e) {
            return replyWithError(e);
        }
    }

    // XXX: Not all RuntimeExceptions can be sent through a Parcelable.  If any
    // such unsupported exception is detected, ExceptionTransportHelper will
    // throw the exception when delivering the reply to the server.
    private Bundle replyWithError(Exception e) {
        Bundle ret = new Bundle();
        RuntimeException transportException;
        if (e instanceof SQLiteServerProtocolException) {
            // We should really handle transporting this exception ourselves...
            transportException = new SQLiteException(
                    "SQLiteServerProtocolException: " + e.getMessage(), e);
        } else if (e instanceof SQLiteException) {
            transportException = (SQLiteException)e;
        } else if (e instanceof RuntimeException) {
            transportException = (RuntimeException)e;
        } else {
            throw new IllegalArgumentException("Unsupported exception: " +
                    e.getClass().getName(), e);
        }
        ret.putParcelable(AbstractCommandMessage.KEY_SERVER_EXCEPTION_HELPER,
                new ExceptionTransportHelper(transportException));
        return ret;
    }

    private Bundle runOnTransact(MethodName methodName, Bundle request)
            throws SQLiteException, SQLiteServerProtocolException {
        switch (methodName) {
            // Acquire can run on the Binder thread since it doesn't interact with
            // SQLiteDatabase.  It's used to set up the dedicated thread executor so it's
            // simplest to keep it out of that code path.
            case ACQUIRE:
                return doOnTransact(methodName, request);
            // TODO: If this server is running in the same process as the client, we can skip
            // all this scheduling overhead...
            default:
                return runOnDedicatedThread(methodName, request);
        }
    }

    private Bundle runOnDedicatedThread(final MethodName methodName, final Bundle request)
            throws SQLiteException, SQLiteServerProtocolException {
        BinderHandle clientHandle = BundleUtils.getParcelableOrThrow(
                request, AbstractCommandMessage.KEY_CLIENT_BINDER);
        ServerState state = getServerState(clientHandle);
        if (state == null) {
            throw new SQLiteServerProtocolException("No client state found for clientHandle=" +
                    clientHandle);
        }

        try {
            return state.executor.runSynchronously(new Callable<Bundle>() {
                @Override
                public Bundle call() throws Exception {
                    Log.d(mTag, "Invoking " + methodName + " on thread " +
                            Thread.currentThread().getId());
                    return doOnTransact(methodName, request);
                }
            });
        } catch (ExecutionException e) {
            Throwable cause = e.getCause();
            if (cause instanceof RuntimeException) {
                // Note that SQLiteException is actually a RuntimeException...
                throw (RuntimeException)cause;
            } else if (cause instanceof SQLiteServerProtocolException) {
                throw (SQLiteServerProtocolException)cause;
            } else {
                throw new RuntimeException(e);
            }
        }
    }

    private Bundle doOnTransact(MethodName methodName, Bundle request)
            throws SQLiteException, SQLiteServerProtocolException {
        switch (methodName) {
            case ACQUIRE:
                return new AcquireHandler(this).handle(request);
            case RELEASE:
                return new ReleaseHandler(this).handle(request);
            case BEGIN_TRANSACTION:
                return new BeginTransactionHandler(this).handle(request);
            case SET_TRANSACTION_SUCCESSFUL:
                return new SetTransactionSuccessfulHandler(this).handle(request);
            case END_TRANSACTION:
                return new EndTransactionHandler(this).handle(request);
            case RAW_QUERY:
                return new RawQueryHandler(this, mServerName).handle(request);
            case EXEC_SQL:
                return new ExecSQLHandler(this).handle(request);
            case INSERT:
                return new InsertHandler(this).handle(request);
            case UPDATE:
                return new UpdateHandler(this).handle(request);
            case DELETE:
                return new DeleteHandler(this).handle(request);
            default:
                throw new IllegalArgumentException("Undeclared methodName=" + methodName);
        }
    }

    private class ClientDiedHandler implements DeathRecipient {
        private final BinderHandle mClientHandle;

        public ClientDiedHandler(BinderHandle clientHandle) {
            mClientHandle = clientHandle;
        }

        @Override
        public void binderDied() {
            Log.w(mTag, "Unexpected client death (clientHandle=" + mClientHandle + ")");
            performRelease(getServerState(mClientHandle));
        }
    }
}




Java Source Code List

aosp.android.database.BulkCursorDescriptor.java
aosp.android.database.BulkCursorNative.java
aosp.android.database.BulkCursorToCursorAdaptor.java
aosp.android.database.CrossProcessCursorWrapper.java
aosp.android.database.CursorToBulkCursorAdaptor.java
aosp.android.database.IBulkCursor.java
aosp.android.database.MoreDatabaseUtils.java
org.devtcg.sqliteserver.SQLiteContentProviderServer.java
org.devtcg.sqliteserver.SQLiteServerConnectionManager.java
org.devtcg.sqliteserver.SQLiteServerConnection.java
org.devtcg.sqliteserver.SQLiteServer.java
org.devtcg.sqliteserver.SQLiteServiceServer.java
org.devtcg.sqliteserver.exception.SQLiteServerException.java
org.devtcg.sqliteserver.impl.ExecutorHelper.java
org.devtcg.sqliteserver.impl.SQLiteExecutor.java
org.devtcg.sqliteserver.impl.ServerImplProvider.java
org.devtcg.sqliteserver.impl.binder.AbstractBinderClient.java
org.devtcg.sqliteserver.impl.binder.BinderHandle.java
org.devtcg.sqliteserver.impl.binder.BundleUtils.java
org.devtcg.sqliteserver.impl.binder.ClientTransactor.java
org.devtcg.sqliteserver.impl.binder.ContentObserverProxy.java
org.devtcg.sqliteserver.impl.binder.ContentProviderClient.java
org.devtcg.sqliteserver.impl.binder.SQLiteServerProtocolException.java
org.devtcg.sqliteserver.impl.binder.ServerImpl.java
org.devtcg.sqliteserver.impl.binder.ServerState.java
org.devtcg.sqliteserver.impl.binder.ServiceClient.java
org.devtcg.sqliteserver.impl.binder.ThreadAffinityExecutor.java
org.devtcg.sqliteserver.impl.binder.protocol.AbstractCommandHandler.java
org.devtcg.sqliteserver.impl.binder.protocol.AbstractCommandMessage.java
org.devtcg.sqliteserver.impl.binder.protocol.AcquireCommand.java
org.devtcg.sqliteserver.impl.binder.protocol.BeginTransactionCommand.java
org.devtcg.sqliteserver.impl.binder.protocol.DeleteCommand.java
org.devtcg.sqliteserver.impl.binder.protocol.EndTransactionCommand.java
org.devtcg.sqliteserver.impl.binder.protocol.ExceptionTransportHelper.java
org.devtcg.sqliteserver.impl.binder.protocol.ExecSQLCommand.java
org.devtcg.sqliteserver.impl.binder.protocol.InsertCommand.java
org.devtcg.sqliteserver.impl.binder.protocol.MethodName.java
org.devtcg.sqliteserver.impl.binder.protocol.RawQueryCommand.java
org.devtcg.sqliteserver.impl.binder.protocol.ReleaseCommand.java
org.devtcg.sqliteserver.impl.binder.protocol.SetTransactionSuccessfulCommand.java
org.devtcg.sqliteserver.impl.binder.protocol.UpdateCommand.java
org.devtcg.sqliteserver.sample.MyActivity.java
org.devtcg.sqliteserver.sample.MyOpenHelper.java
org.devtcg.sqliteserver.sample.TestContentProvider.java
org.devtcg.sqliteserver.sample.TestService.java