Android Open Source - DKO Context






From Project

Back to project page DKO.

License

The source code is released under:

GNU Lesser General Public License

If you think the Android project DKO 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.kered.dko;
// w w  w  .  ja v  a2 s .co m
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;

import javax.sql.DataSource;

import org.kered.dko.Tuple.Tuple2;
import org.kered.dko.persistence.QuerySize;


/**
 * This class is used for tweaking query executions.  
 * Multiple context levels are supported (currently: vm, thread-group and thread)
 * in increasing order of precedence.  
 *
 * A context can start and stop transactions, set default {@code DataSource}s (optionally
 * per package or class), and change what schema is referenced.
 *
 * Each context-altering method returns an {@code Undoer} object, which lets you undo the
 * context change at a later date. (by calling {@code Undoer.undo()})  
 * By default all {@code Undoer}s undo themselves when they're GCed.   But you
 * can suppress this behavior by calling {@code Undoer.setAutoUndo(false)}.
 *
 * Typical use
 * would be like the following (where {@code db.DEFAULT} is the data source generated by
 * the {@code CodeGenerator} or pulled from a {@code Query} instance):
 * <pre>   {@code Context.getThreadContext().startTransaction(db.DEFAULT);
 *   SomeClass x = SomeClass.ALL.get(SomeClass.ID.eq(123));
 *   x.setName("my new name");
 *   x.update();
 *   doSomeOtherWork();
 *   SomeClass.ALL.where(SomeClass.ID.eq(456)).deleteAll();
 *   Context.getThreadContext().commitTransaction(db.DEFAULT);}</pre>
 * Note that database calls (through this API) in {@code doSomeOtherWork()} will also be within the
 * transaction (as long as they're still within the same thread).
 *
 * @author Derek Anderson
 */
public class Context {


  /**
   * @return the context for the current thread
   */
  public static Context getThreadContext() {
    return threadContextContainer.get();
  }

  /**
   * Threads spawned by default share their parent's thread group. &nbsp;
   * Use this if you want child threads spawned by some process to share this context.
   * @return the context for the current thread group
   */
  public static Context getThreadGroupContext() {
    final ThreadGroup tg = Thread.currentThread().getThreadGroup();
    Context context = threadGroupContexts.get(tg);
    if (context == null) {
      context = new Context();
      threadGroupContexts.put(tg, context);
    }
    return context;
  }

  /**
   * @return the singleton context for this VM
   */
  public static Context getVMContext() {
    return vmContext;
  }

  static DataSource getDataSource(final Class<? extends Table> cls) {
    // don't let DB overrides effect persistence classes
    if (QuerySize.class.getPackage().equals(cls.getPackage())) return null;
    
    final Context[] contexts = {getThreadContext(), getThreadGroupContext(), getVMContext()};
    for (final Context context : contexts) {
      DataSource ds = null;

      Map<UUID, DataSource> x = context.classDataSources.get(cls);
      if (x != null) {
        synchronized(x) {
          for (final DataSource tmp : x.values()) { ds = tmp; }
        }
        if (ds != null) return ds;
      }

      //System.err.println("woot2 "+ context);
      x = context.packageDataSources.get(cls.getPackage());
      if (x != null) {
        synchronized(x) {
          for (final DataSource tmp : x.values()) { ds = tmp; }
        }
        //System.err.println("woot2.1 "+ ds);
        if (ds != null) return ds;
      }

      //System.err.println("woot3 "+ context);
      synchronized(context.defaultDataSource) {
        for (final DataSource tmp : context.defaultDataSource.values()) { ds = tmp; }
      }
      if (ds != null) return ds;
    }
    return null;
  }

  static String getSchemaToUse(final DataSource ds, final String originalSchema) {
    final Tuple2<DataSource, String> key = new Tuple2<DataSource,String>(ds, originalSchema);
    String schema = null;
    final Context[] contexts = {getThreadContext(), getThreadGroupContext(), getVMContext()};
    for (final Context context : contexts) {
      final Map<UUID, String> x = context.schemaOverrides.get(key);
      if (x == null) continue;
      synchronized(x) {
        for (final String tmp : x.values()) { schema = tmp; }
      }
      if (schema != null) return schema;
    }
    return originalSchema;
  }

  static boolean usageWarningsEnabled() {
    final Context[] contexts = {getThreadContext(), getThreadGroupContext(), getVMContext()};
    for (final Context context : contexts) {
      Boolean x = null;
      synchronized(context.enableUsageWarnings) {
        for (final Boolean v : context.enableUsageWarnings.values()) {
          x = v;
        }
      }
      if (x != null) return x;
    }
    String prop = System.getProperty(Constants.PROPERTY_WARN_EXCESSIVE_LAZY_LOADING);
    if (prop == null) prop = System.getProperty(Constants.PROPERTY_WARN_EXCESSIVE_LAZY_LOADING_OLD);
    if (prop != null) return Util.truthy(prop);
    return true;
  }

  static boolean selectOptimizationsEnabled() {
    final Context[] contexts = {getThreadContext(), getThreadGroupContext(), getVMContext()};
    for (final Context context : contexts) {
      Boolean x = null;
      synchronized(context.enableSelectOptimizations) {
        for (final Boolean v : context.enableSelectOptimizations.values()) {
          x = v;
        }
      }
      if (x != null) return x;
    }
    String prop = System.getProperty(Constants.PROPERTY_OPTIMIZE_SELECT_FIELDS);
    if (prop == null) prop = System.getProperty(Constants.PROPERTY_OPTIMIZE_SELECT_FIELDS_OLD);
    if (prop != null) return Util.truthy(prop);
    return true;
  }

  /**
   * Returns true if currently inside a transaction.
   * @param ds
   * @return
   */
  public static boolean inTransaction(final DataSource ds) {
    boolean isInTransaction = getThreadContext().transactionConnections.containsKey(ds);
    if (isInTransaction) return true;
    isInTransaction = getThreadGroupContext().transactionConnections.containsKey(ds);
    if (isInTransaction) return true;
    return getVMContext().transactionConnections.containsKey(ds);
  }

  /**
   * Gets the connection for this transaction.
   * @param ds
   * @return null is not currently in a transaction
   */
  public static Connection getConnection(final DataSource ds) {
    Connection c = getThreadContext().transactionConnections.get(ds);
    if (c == null) c = getThreadGroupContext().transactionConnections.get(ds);
    if (c == null) c = getVMContext().transactionConnections.get(ds);
    return c;
  }

  /**
   * Starts a new transaction.
   * @param ds
   * @return success
   * @throws SQLException
   */
  public boolean startTransaction(final DataSource ds) throws SQLException {
    Connection c = transactionConnections.get(ds);
    if (c != null) return false;
    c = ds.getConnection();
    if (Constants.DB_TYPE.detect(ds)==Constants.DB_TYPE.SQLITE3) {
      Statement stmt = c.createStatement();
      try {
        String sql = "begin transaction";
        Util.log(sql, null);
        stmt.execute(sql);
      } finally {
        stmt.close();
      }
    } else {
      Util.log("connection.setAutoCommit(false)", null);
      c.setAutoCommit(false);
    }
    transactionConnections.put(ds, c);
    return true;
  }

  /**
   * Commits the current transaction.
   * @param ds
   * @return
   * @throws SQLException
   */
  public boolean commitTransaction(final DataSource ds) throws SQLException {
    final Connection c = transactionConnections.remove(ds);
    if (c == null) return false;
    if (Constants.DB_TYPE.detect(ds)==Constants.DB_TYPE.SQLITE3) {
      Statement stmt = c.createStatement();
      try {
        String sql = "commit";
        Util.log(sql, null);
        stmt.execute(sql);
      } finally {
        stmt.close();
      }
    } else {
      Util.log("connection.commit()", null);
      c.commit();
    }
    c.close();
    return true;
  }

  /**
   * Rolls back the current transaction. &nbsp;
   * This method hides the {@code SQLException} thrown by the rollback.
   * @param ds
   * @return
   */
  public boolean rollbackTransaction(final DataSource ds) {
    final Connection c = transactionConnections.get(ds);
    transactionConnections.remove(ds);
    if (c == null) return false;
    try {
      if (Constants.DB_TYPE.detect(ds)==Constants.DB_TYPE.SQLITE3) {
        Statement stmt = c.createStatement();
        try {
          String sql = "rollback";
          try {
            Thread.sleep(2000);
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
          Util.log(sql, null);
          stmt.execute(sql);
        } finally {
          stmt.close();
        }
      } else {
        Util.log("connection.rollback()", null);
        c.rollback();
      }
    } catch (final SQLException e) {
      e.printStackTrace();
      try {
        c.close();
      } catch (final SQLException e2) {
        e2.printStackTrace();
      }
      return false;
    }
    try {
      c.close();
    } catch (final SQLException e) {
      e.printStackTrace();
    }
    return true;
  }

  /**
   * Rolls back the current transaction.
   * @param ds
   * @return
   * @throws SQLException
   */
  public boolean rollbackTransactionThrowSQLException(final DataSource ds) throws SQLException {
    final Connection c = transactionConnections.get(ds);
    transactionConnections.remove(ds);
    if (c == null) return false;
    try {
      c.rollback();
    } catch (final SQLException e) {
      throw e;
    } finally {
      c.close();
    }
    return true;
  }

  /**
   * @deprecated Use {@link #overrideDatabaseName(DataSource,String,String)} instead
   */
  public Undoer overrideSchema(final DataSource ds, final String originalSchema, final String newSchema) {
    return overrideDatabaseName(ds, originalSchema, newSchema);
  }

  /**
   * All generated classes have their database name embedded in them, and always specify their
   * schema when performing a query. &nbsp; You can change what schema they reference by
   * overriding it here.  (per {@code DataSource})
   * @param ds
   * @param originalDatabaseName
   * @param newDatabaseName
   * @return
   */
  public Undoer overrideDatabaseName(final DataSource ds, final String originalDatabaseName, final String newDatabaseName) {
    final Tuple2<DataSource, String> key = new Tuple2<DataSource,String>(ds, originalDatabaseName);
    Map<UUID, String> map = schemaOverrides.get(key);
    if (map == null) {
      map = Collections.synchronizedMap(new LinkedHashMap<UUID, String>());
      schemaOverrides.put(key, map);
    }
    final UUID uuid = UUID.randomUUID();
    map.put(uuid, newDatabaseName);
    final Map<UUID, String> map2 = map;
    return new Undoer() {
      @Override
      public void undo() {
        map2.remove(uuid);
      }
    };
  }

  /**
   * Sets a default {@code DataSource} for this context. &nbsp; This will be overridden
   * by future calls to this method, or any matching calls
   * to the package or class versions of this method.
   * @param ds
   * @return
   */
  public Undoer setDataSource(final DataSource ds) {
    final UUID uuid = UUID.randomUUID();
    defaultDataSource.put(uuid, ds);
    return new Undoer() {
      @Override
      public void undo() {
        defaultDataSource.remove(uuid);
      }
    };
  }

  /**
   * Sets a default {@code DataSource} for all classes in this package in this context. &nbsp;
   * This will be overridden
   * by future calls to this method, or any matching calls
   * to the class version of this method.
   * @param pkg
   * @param ds
   * @return
   */
  public Undoer setDataSource(final Package pkg, final DataSource ds) {
    Map<UUID, DataSource> map = packageDataSources.get(pkg);
    if (map == null) {
      map = Collections.synchronizedMap(new LinkedHashMap<UUID, DataSource>());
      packageDataSources.put(pkg, map);
    }
    final UUID uuid = UUID.randomUUID();
    map.put(uuid, ds);
    final Map<UUID, DataSource> map2 = map;
    return new Undoer() {
      @Override
      public void undo() {
        map2.remove(uuid);
      }
    };
  }

  /**
   * Sets a default {@code DataSource} for the specified class in this context.
   * @param cls
   * @param ds
   * @return
   */
  public Undoer setDataSource(final Class<? extends Table> cls, final DataSource ds) {
    Map<UUID, DataSource> map = classDataSources.get(cls);
    if (map == null) {
      map = Collections.synchronizedMap(new LinkedHashMap<UUID, DataSource>());
      classDataSources.put(cls, map);
    }
    final UUID uuid = UUID.randomUUID();
    map.put(uuid, ds);
    final Map<UUID, DataSource> map2 = map;
    return new Undoer() {
      @Override
      public void undo() {
        map2.remove(uuid);
      }
    };
  }

  /**
   * Turns on and off warnings for "bad" usage patterns.
   * (like lazy loading fk relationships in a tight loop)
   * @param enable
   * @return
   */
  public Undoer enableUsageWarnings(final boolean enable) {
    final UUID uuid = UUID.randomUUID();
    enableUsageWarnings.put(uuid, enable);
    return new Undoer() {
      @Override
      public void undo() {
        enableUsageWarnings.remove(uuid);
      }
    };
  }

  /**
   * Turns on and off select optimizations that par down selected fields that are never used.
   * @param enable
   * @return
   */
  public Undoer enableSelectOptimizations(final boolean enable) {
    final UUID uuid = UUID.randomUUID();
    enableSelectOptimizations.put(uuid, enable);
    return new Undoer() {
      @Override
      public void undo() {
        enableSelectOptimizations.remove(uuid);
      }
    };
  }


  /**
   * Allows you to undo any context change. &nbsp; By default will automatically undo
   * once this object is GCed, but this can be turned off by calling {@code setAutoUndo(false)}.
   * @author Derek Anderson
   */
  public static abstract class Undoer {
    private boolean autoRevoke = true;
    public abstract void undo();
    public boolean willAutoUndo() {
      return autoRevoke ;
    }
    public Undoer setAutoUndo(final boolean v) {
      autoRevoke = v;
      return this;
    }

    private Undoer() {}
    protected void finalize() {
      if (autoRevoke) undo();
    }
  }

  private static Context vmContext = new Context();

  private static Map<ThreadGroup,Context> threadGroupContexts =
      Collections.synchronizedMap(new HashMap<ThreadGroup,Context>());

  private static ThreadLocal<Context> threadContextContainer = new ThreadLocal<Context>() {
    @Override
    protected Context initialValue() {
      return new Context();
    }
  };

  private final Map<Tuple2<DataSource,String>,Map<UUID,String>> schemaOverrides =
      Collections.synchronizedMap(new HashMap<Tuple2<DataSource,String>,Map<UUID,String>>());

  private final Map<UUID,Boolean> enableUsageWarnings =
      Collections.synchronizedMap(new LinkedHashMap<UUID,Boolean>());

  private final Map<UUID,Boolean> enableSelectOptimizations =
      Collections.synchronizedMap(new LinkedHashMap<UUID,Boolean>());

  private final Map<UUID,DataSource> defaultDataSource =
      Collections.synchronizedMap(new LinkedHashMap<UUID,DataSource>());

  private final Map<Package,Map<UUID,DataSource>> packageDataSources =
      Collections.synchronizedMap(new LinkedHashMap<Package,Map<UUID,DataSource>>());

  private final Map<Class<?>,Map<UUID,DataSource>> classDataSources =
      Collections.synchronizedMap(new LinkedHashMap<Class<?>,Map<UUID,DataSource>>());

  private final Map<DataSource,Connection> transactionConnections =
      Collections.synchronizedMap(new HashMap<DataSource,Connection>());

}




Java Source Code List

.HelloWorld.java
org.kered.contactlensfinder.DB.java
org.kered.contactlensfinder.MainActivity.java
org.kered.contactlensfinder.ViewManufacturersActivity.java
org.kered.contactlensfinder.ViewProductsActivity.java
org.kered.contactlensfinder.ViewPropertiesActivity.java
org.kered.dko.AbstractQuery.java
org.kered.dko.Bulk.java
org.kered.dko.CSV.java
org.kered.dko.ClosableIterator.java
org.kered.dko.Condition.java
org.kered.dko.Constants.java
org.kered.dko.Context.java
org.kered.dko.DBQuery.java
org.kered.dko.DBRowIterator.java
org.kered.dko.Diff.java
org.kered.dko.DualIterator.java
org.kered.dko.Expression.java
org.kered.dko.Field.java
org.kered.dko.FilteringQuery.java
org.kered.dko.Function.java
org.kered.dko.InMemoryQuery.java
org.kered.dko.Join.java
org.kered.dko.LazyCacheIterable.java
org.kered.dko.LocalJoin.java
org.kered.dko.M.java
org.kered.dko.Main.java
org.kered.dko.MatryoshkaQuery.java
org.kered.dko.PeekableClosableIterator.java
org.kered.dko.PeekableIterator.java
org.kered.dko.QueryAddField.java
org.kered.dko.QueryFactory.java
org.kered.dko.QuerySnapshot.java
org.kered.dko.Query.java
org.kered.dko.SQLFunction.java
org.kered.dko.SelectAsMapIterable.java
org.kered.dko.SelectFromOAI.java
org.kered.dko.SelectSingleColumn.java
org.kered.dko.SoftJoinUtil.java
org.kered.dko.SoftJoin.java
org.kered.dko.SqlContext.java
org.kered.dko.Statistics.java
org.kered.dko.SubQueryField.java
org.kered.dko.TableInfo.java
org.kered.dko.TableWrapper.java
org.kered.dko.Table.java
org.kered.dko.TemporaryTableFactory.java
org.kered.dko.TmpTableBuilder.java
org.kered.dko.Tuple.java
org.kered.dko.UsageMonitor.java
org.kered.dko.UsageStats.java
org.kered.dko.Util.java
org.kered.dko.ant.ClassGenerator.java
org.kered.dko.ant.CodeGeneratorBase.java
org.kered.dko.ant.CodeGenerator.java
org.kered.dko.ant.DataSourceGenerator.java
org.kered.dko.ant.GsonGenerator.java
org.kered.dko.ant.JoinGenerator.java
org.kered.dko.ant.Main.java
org.kered.dko.ant.SchemaExtractorBase.java
org.kered.dko.ant.SchemaExtractor.java
org.kered.dko.ant.Util.java
org.kered.dko.datasource.CheapConnectionPoolingDataSource.java
org.kered.dko.datasource.ConnectionCountingDataSource.java
org.kered.dko.datasource.JDBCDriverDataSource.java
org.kered.dko.datasource.MatryoshkaDataSource.java
org.kered.dko.datasource.MirroredDataSource.java
org.kered.dko.datasource.ReflectedDataSource.java
org.kered.dko.datasource.SingleConnectionDataSource.java
org.kered.dko.datasource.SingleThreadedDataSource.java
org.kered.dko.datasource.UnClosableConnection.java
org.kered.dko.datasource.Util.java
org.kered.dko.json.CDL.java
org.kered.dko.json.CookieList.java
org.kered.dko.json.Cookie.java
org.kered.dko.json.HTTPTokener.java
org.kered.dko.json.HTTP.java
org.kered.dko.json.JSONArray.java
org.kered.dko.json.JSONException.java
org.kered.dko.json.JSONML.java
org.kered.dko.json.JSONObject.java
org.kered.dko.json.JSONString.java
org.kered.dko.json.JSONStringer.java
org.kered.dko.json.JSONTokener.java
org.kered.dko.json.JSONWriter.java
org.kered.dko.json.Pickle.java
org.kered.dko.json.XMLTokener.java
org.kered.dko.json.XML.java
org.kered.dko.junk.DerbyLoadTestSchema.java
org.kered.dko.junk.OracleCreateTestUser.java
org.kered.dko.junk.OracleLoadTestSchema.java
org.kered.dko.persistence.Util.java
org.kered.dko.util.DumpDatabase.java
sakila.Example0.java
sakila.Example1.java
sakila.Example2.java
sakila.Util.java