import com.datastax.driver.core.exceptions.DriverException;
public abstract class AbsCassDAO<TRANS extends TransStore,DATA> {
- protected static final char DOT = '.';
- protected static final char DOT_PLUS_ONE = '.'+1;
- protected static final String FIRST_CHAR = Character.toString((char)0);
- protected static final String LAST_CHAR = Character.toString((char)Character.MAX_VALUE);
- protected static final int FIELD_COMMAS = 0;
- protected static final int QUESTION_COMMAS = 1;
- protected static final int ASSIGNMENT_COMMAS = 2;
- protected static final int WHERE_ANDS = 3;
+ protected static final char DOT = '.';
+ protected static final char DOT_PLUS_ONE = '.'+1;
+ protected static final String FIRST_CHAR = Character.toString((char)0);
+ protected static final String LAST_CHAR = Character.toString((char)Character.MAX_VALUE);
+ protected static final int FIELD_COMMAS = 0;
+ protected static final int QUESTION_COMMAS = 1;
+ protected static final int ASSIGNMENT_COMMAS = 2;
+ protected static final int WHERE_ANDS = 3;
- private Cluster cluster;
- /*
- * From DataStax
- * com.datastax.driver.core.Session
- A session holds connections to a Cassandra cluster, allowing it to be queried. Each session maintains multiple connections to the cluster nodes,
- provides policies to choose which node to use for each query (round-robin on all nodes of the cluster by default), and handles retries for
- failed query (when it makes sense), etc...
- Session instances are thread-safe and usually a single instance is enough per application. However, a given session can only be set to one
- keyspace at a time, so one instance per keyspace is necessary.
- */
- private Session session;
- private final String keyspace;
- // If this is null, then we own session
- private final AbsCassDAO<TRANS,?> owningDAO;
- protected Class<DATA> dataClass;
- private final String name;
-// private static Slot sessionSlot; // not used since 2015
- private static final ArrayList<AbsCassDAO<? extends TransStore,?>.PSInfo> psinfos = new ArrayList<>();
- private static final List<Object> EMPTY = new ArrayList<>(0);
- private static final Deque<ResetRequest> resetDeque = new ConcurrentLinkedDeque<ResetRequest>();
- private static boolean resetTrigger = false;
- private static long nextAvailableReset = 0;
-
- public AbsCassDAO(TRANS trans, String name, Cluster cluster, String keyspace, Class<DATA> dataClass) {
- this.name = name;
- this.cluster = cluster;
- this.keyspace = keyspace;
- owningDAO = null; // we own session
- session = null;
- this.dataClass = dataClass;
- }
+ private Cluster cluster;
+ /*
+ * From DataStax
+ * com.datastax.driver.core.Session
+ A session holds connections to a Cassandra cluster, allowing it to be queried. Each session maintains multiple connections to the cluster nodes,
+ provides policies to choose which node to use for each query (round-robin on all nodes of the cluster by default), and handles retries for
+ failed query (when it makes sense), etc...
+ Session instances are thread-safe and usually a single instance is enough per application. However, a given session can only be set to one
+ keyspace at a time, so one instance per keyspace is necessary.
+ */
+ private Session session;
+ private final String keyspace;
+ // If this is null, then we own session
+ private final AbsCassDAO<TRANS,?> owningDAO;
+ protected Class<DATA> dataClass;
+ private final String name;
+// private static Slot sessionSlot; // not used since 2015
+ private static final ArrayList<AbsCassDAO<? extends TransStore,?>.PSInfo> psinfos = new ArrayList<>();
+ private static final List<Object> EMPTY = new ArrayList<>(0);
+ private static final Deque<ResetRequest> resetDeque = new ConcurrentLinkedDeque<ResetRequest>();
+ private static boolean resetTrigger = false;
+ private static long nextAvailableReset = 0;
+
+ public AbsCassDAO(TRANS trans, String name, Cluster cluster, String keyspace, Class<DATA> dataClass) {
+ this.name = name;
+ this.cluster = cluster;
+ this.keyspace = keyspace;
+ owningDAO = null; // we own session
+ session = null;
+ this.dataClass = dataClass;
+ }
- public AbsCassDAO(TRANS trans, String name, AbsCassDAO<TRANS,?> aDao, Class<DATA> dataClass) {
- this.name = name;
- cluster = aDao.cluster;
- keyspace = aDao.keyspace;
- session = null;
- // We do not own session
- owningDAO = aDao;
- this.dataClass = dataClass;
- }
-
+ public AbsCassDAO(TRANS trans, String name, AbsCassDAO<TRANS,?> aDao, Class<DATA> dataClass) {
+ this.name = name;
+ cluster = aDao.cluster;
+ keyspace = aDao.keyspace;
+ session = null;
+ // We do not own session
+ owningDAO = aDao;
+ this.dataClass = dataClass;
+ }
+
// Not used since 2015
-// public static void setSessionSlot(Slot slot) {
-// sessionSlot = slot;
-// }
+// public static void setSessionSlot(Slot slot) {
+// sessionSlot = slot;
+// }
- //Note: Lower case ON PURPOSE. These names used to create History Messages
- public enum CRUD {
- create,read,update,delete;
- }
+ //Note: Lower case ON PURPOSE. These names used to create History Messages
+ public enum CRUD {
+ create,read,update,delete;
+ }
- public class PSInfo {
- private PreparedStatement ps;
- private final int size;
- private final Loader<DATA> loader;
- private final CRUD crud; // Store CRUD, because it makes a difference in Object Order, see Loader
- private final String cql;
- private final ConsistencyLevel consistency;
+ public class PSInfo {
+ private PreparedStatement ps;
+ private final int size;
+ private final Loader<DATA> loader;
+ private final CRUD crud; // Store CRUD, because it makes a difference in Object Order, see Loader
+ private final String cql;
+ private final ConsistencyLevel consistency;
- /**
- * Create a PSInfo and create Prepared Statement
- *
- * @param trans
- * @param theCQL
- * @param loader
- */
- public PSInfo(TRANS trans, String theCQL, Loader<DATA> loader, ConsistencyLevel consistency) {
- this.loader = loader;
- this.consistency=consistency;
- psinfos.add(this);
+ /**
+ * Create a PSInfo and create Prepared Statement
+ *
+ * @param trans
+ * @param theCQL
+ * @param loader
+ */
+ public PSInfo(TRANS trans, String theCQL, Loader<DATA> loader, ConsistencyLevel consistency) {
+ this.loader = loader;
+ this.consistency=consistency;
+ psinfos.add(this);
- cql = theCQL.trim().toUpperCase();
- if(cql.startsWith("INSERT")) {
- crud = CRUD.create;
- } else if(cql.startsWith("UPDATE")) {
- crud = CRUD.update;
- } else if(cql.startsWith("DELETE")) {
- crud = CRUD.delete;
- } else {
- crud = CRUD.read;
- }
-
- int idx = 0, count=0;
- while((idx=cql.indexOf('?',idx))>=0) {
- ++idx;
- ++count;
- }
- size=count;
- }
-
- public synchronized void reset() {
- ps = null;
- }
-
- private synchronized BoundStatement ps(TransStore trans) throws APIException, IOException {
- /* From Datastax
- You should prepare only once, and cache the PreparedStatement in your application (it is thread-safe).
- If you call prepare multiple times with the same query string, the driver will log a warning.
- */
- if(ps==null) {
- TimeTaken tt = trans.start("Preparing PSInfo " + crud.toString().toUpperCase() + " on " + name,Env.SUB);
- try {
- ps = getSession(trans).prepare(cql);
- ps.setConsistencyLevel(consistency);
- } catch (DriverException e) {
- reportPerhapsReset(trans,e);
- throw e;
- } finally {
- tt.done();
- }
- }
- // BoundStatements are NOT threadsafe... need a new one each time.
- return new BoundStatement(ps);
- }
+ cql = theCQL.trim().toUpperCase();
+ if(cql.startsWith("INSERT")) {
+ crud = CRUD.create;
+ } else if(cql.startsWith("UPDATE")) {
+ crud = CRUD.update;
+ } else if(cql.startsWith("DELETE")) {
+ crud = CRUD.delete;
+ } else {
+ crud = CRUD.read;
+ }
+
+ int idx = 0, count=0;
+ while((idx=cql.indexOf('?',idx))>=0) {
+ ++idx;
+ ++count;
+ }
+ size=count;
+ }
+
+ public synchronized void reset() {
+ ps = null;
+ }
+
+ private synchronized BoundStatement ps(TransStore trans) throws APIException, IOException {
+ /* From Datastax
+ You should prepare only once, and cache the PreparedStatement in your application (it is thread-safe).
+ If you call prepare multiple times with the same query string, the driver will log a warning.
+ */
+ if(ps==null) {
+ TimeTaken tt = trans.start("Preparing PSInfo " + crud.toString().toUpperCase() + " on " + name,Env.SUB);
+ try {
+ ps = getSession(trans).prepare(cql);
+ ps.setConsistencyLevel(consistency);
+ } catch (DriverException e) {
+ reportPerhapsReset(trans,e);
+ throw e;
+ } finally {
+ tt.done();
+ }
+ }
+ // BoundStatements are NOT threadsafe... need a new one each time.
+ return new BoundStatement(ps);
+ }
- /**
- * Execute a Prepared Statement by extracting from DATA object
- *
- * @param trans
- * @param text
- * @param data
- * @return
- */
- public Result<ResultSetFuture> execAsync(TRANS trans, String text, DATA data) {
- TimeTaken tt = trans.start(text, Env.REMOTE);
- try {
- return Result.ok(getSession(trans).executeAsync(
- ps(trans).bind(loader.extract(data, size, crud))));
- } catch (DriverException | APIException | IOException e) {
- AbsCassDAO.this.reportPerhapsReset(trans,e);
- return Result.err(Status.ERR_Backend,"%s-%s executing %s",e.getClass().getName(),e.getMessage(), cql);
- } finally {
- tt.done();
- }
- }
+ /**
+ * Execute a Prepared Statement by extracting from DATA object
+ *
+ * @param trans
+ * @param text
+ * @param data
+ * @return
+ */
+ public Result<ResultSetFuture> execAsync(TRANS trans, String text, DATA data) {
+ TimeTaken tt = trans.start(text, Env.REMOTE);
+ try {
+ return Result.ok(getSession(trans).executeAsync(
+ ps(trans).bind(loader.extract(data, size, crud))));
+ } catch (DriverException | APIException | IOException e) {
+ AbsCassDAO.this.reportPerhapsReset(trans,e);
+ return Result.err(Status.ERR_Backend,"%s-%s executing %s",e.getClass().getName(),e.getMessage(), cql);
+ } finally {
+ tt.done();
+ }
+ }
- /**
- * Execute a Prepared Statement on Object[] key
- *
- * @param trans
- * @param text
- * @param objs
- * @return
- */
- public Result<ResultSetFuture> execAsync(TRANS trans, String text, Object ... objs) {
- TimeTaken tt = trans.start(text, Env.REMOTE);
- try {
- return Result.ok(getSession(trans).executeAsync(ps(trans).bind(objs)));
- } catch (DriverException | APIException | IOException e) {
- AbsCassDAO.this.reportPerhapsReset(trans,e);
- return Result.err(Status.ERR_Backend,"%s-%s executing %s",e.getClass().getName(),e.getMessage(), cql);
- } finally {
- tt.done();
- }
- }
-
- /*
- * Note:
- *
- */
+ /**
+ * Execute a Prepared Statement on Object[] key
+ *
+ * @param trans
+ * @param text
+ * @param objs
+ * @return
+ */
+ public Result<ResultSetFuture> execAsync(TRANS trans, String text, Object ... objs) {
+ TimeTaken tt = trans.start(text, Env.REMOTE);
+ try {
+ return Result.ok(getSession(trans).executeAsync(ps(trans).bind(objs)));
+ } catch (DriverException | APIException | IOException e) {
+ AbsCassDAO.this.reportPerhapsReset(trans,e);
+ return Result.err(Status.ERR_Backend,"%s-%s executing %s",e.getClass().getName(),e.getMessage(), cql);
+ } finally {
+ tt.done();
+ }
+ }
+
+ /*
+ * Note:
+ *
+ */
- /**
- * Execute a Prepared Statement by extracting from DATA object
- *
- * @param trans
- * @param text
- * @param data
- * @return
- */
- public Result<ResultSet> exec(TRANS trans, String text, DATA data) {
- TimeTaken tt = trans.start(text, Env.REMOTE);
- try {
- /*
- * "execute" (and executeAsync)
- * Executes the provided query.
- This method blocks until at least some result has been received from the database. However,
- for SELECT queries, it does not guarantee that the result has been received in full. But it
- does guarantee that some response has been received from the database, and in particular
- guarantee that if the request is invalid, an exception will be thrown by this method.
+ /**
+ * Execute a Prepared Statement by extracting from DATA object
+ *
+ * @param trans
+ * @param text
+ * @param data
+ * @return
+ */
+ public Result<ResultSet> exec(TRANS trans, String text, DATA data) {
+ TimeTaken tt = trans.start(text, Env.REMOTE);
+ try {
+ /*
+ * "execute" (and executeAsync)
+ * Executes the provided query.
+ This method blocks until at least some result has been received from the database. However,
+ for SELECT queries, it does not guarantee that the result has been received in full. But it
+ does guarantee that some response has been received from the database, and in particular
+ guarantee that if the request is invalid, an exception will be thrown by this method.
- Parameters:
- statement - the CQL query to execute (that can be any Statement).
- Returns:
- the result of the query. That result will never be null but can be empty (and will
- be for any non SELECT query).
- */
- return Result.ok(getSession(trans).execute(
- ps(trans).bind(loader.extract(data, size, crud))));
- } catch (DriverException | APIException | IOException e) {
- AbsCassDAO.this.reportPerhapsReset(trans,e);
- return Result.err(Status.ERR_Backend,"%s-%s executing %s",e.getClass().getName(),e.getMessage(), cql);
- } finally {
- tt.done();
- }
- }
+ Parameters:
+ statement - the CQL query to execute (that can be any Statement).
+ Returns:
+ the result of the query. That result will never be null but can be empty (and will
+ be for any non SELECT query).
+ */
+ return Result.ok(getSession(trans).execute(
+ ps(trans).bind(loader.extract(data, size, crud))));
+ } catch (DriverException | APIException | IOException e) {
+ AbsCassDAO.this.reportPerhapsReset(trans,e);
+ return Result.err(Status.ERR_Backend,"%s-%s executing %s",e.getClass().getName(),e.getMessage(), cql);
+ } finally {
+ tt.done();
+ }
+ }
- /**
- * Execute a Prepared Statement on Object[] key
- *
- * @param trans
- * @param text
- * @param objs
- * @return
- */
- public Result<ResultSet> exec(TRANS trans, String text, Object ... objs) {
- TimeTaken tt = trans.start(text, Env.REMOTE);
- try {
- return Result.ok(getSession(trans).execute(ps(trans).bind(objs)));
- } catch (DriverException | APIException | IOException e) {
- AbsCassDAO.this.reportPerhapsReset(trans,e);
- return Result.err(Status.ERR_Backend,"%s-%s executing %s",e.getClass().getName(),e.getMessage(), cql);
- } finally {
- tt.done();
- }
- }
+ /**
+ * Execute a Prepared Statement on Object[] key
+ *
+ * @param trans
+ * @param text
+ * @param objs
+ * @return
+ */
+ public Result<ResultSet> exec(TRANS trans, String text, Object ... objs) {
+ TimeTaken tt = trans.start(text, Env.REMOTE);
+ try {
+ return Result.ok(getSession(trans).execute(ps(trans).bind(objs)));
+ } catch (DriverException | APIException | IOException e) {
+ AbsCassDAO.this.reportPerhapsReset(trans,e);
+ return Result.err(Status.ERR_Backend,"%s-%s executing %s",e.getClass().getName(),e.getMessage(), cql);
+ } finally {
+ tt.done();
+ }
+ }
- /**
- * Read the Data from Cassandra given a Prepared Statement (defined by the
- * DAO Instance)
- *
- * This is common behavior among all DAOs.
- * @throws DAOException
- */
- public Result<List<DATA>> read(TRANS trans, String text, Object[] key) {
- TimeTaken tt = trans.start(text,Env.REMOTE);
-
- ResultSet rs;
- try {
- rs = getSession(trans).execute(key==null?ps(trans):ps(trans).bind(key));
-/// TEST CODE for Exception
-// boolean force = true;
-// if(force) {
-// Map<InetSocketAddress, Throwable> misa = new HashMap<>();
-// //misa.put(new InetSocketAddress(444),new Exception("no host was tried"));
-// misa.put(new InetSocketAddress(444),new Exception("Connection has been closed"));
-// throw new com.datastax.driver.core.exceptions.NoHostAvailableException(misa);
-//// throw new com.datastax.driver.core.exceptions.AuthenticationException(new InetSocketAddress(9999),"no host was tried");
-// }
+ /**
+ * Read the Data from Cassandra given a Prepared Statement (defined by the
+ * DAO Instance)
+ *
+ * This is common behavior among all DAOs.
+ * @throws DAOException
+ */
+ public Result<List<DATA>> read(TRANS trans, String text, Object[] key) {
+ TimeTaken tt = trans.start(text,Env.REMOTE);
+
+ ResultSet rs;
+ try {
+ rs = getSession(trans).execute(key==null?ps(trans):ps(trans).bind(key));
+/// TEST CODE for Exception
+// boolean force = true;
+// if(force) {
+// Map<InetSocketAddress, Throwable> misa = new HashMap<>();
+// //misa.put(new InetSocketAddress(444),new Exception("no host was tried"));
+// misa.put(new InetSocketAddress(444),new Exception("Connection has been closed"));
+// throw new com.datastax.driver.core.exceptions.NoHostAvailableException(misa);
+//// throw new com.datastax.driver.core.exceptions.AuthenticationException(new InetSocketAddress(9999),"no host was tried");
+// }
//// END TEST CODE
- } catch (DriverException | APIException | IOException e) {
- AbsCassDAO.this.reportPerhapsReset(trans,e);
- return Result.err(Status.ERR_Backend,"%s-%s executing %s",e.getClass().getName(),e.getMessage(), cql);
- } finally {
- tt.done();
- }
-
- return extract(loader,rs,null /*let Array be created if necessary*/,dflt);
- }
-
- public Result<List<DATA>> read(TRANS trans, String text, DATA data) {
- return read(trans,text, loader.extract(data, size, crud));
- }
-
- public Object[] keyFrom(DATA data) {
- return loader.extract(data, size, CRUD.delete); // Delete is key only
- }
+ } catch (DriverException | APIException | IOException e) {
+ AbsCassDAO.this.reportPerhapsReset(trans,e);
+ return Result.err(Status.ERR_Backend,"%s-%s executing %s",e.getClass().getName(),e.getMessage(), cql);
+ } finally {
+ tt.done();
+ }
+
+ return extract(loader,rs,null /*let Array be created if necessary*/,dflt);
+ }
+
+ public Result<List<DATA>> read(TRANS trans, String text, DATA data) {
+ return read(trans,text, loader.extract(data, size, crud));
+ }
+
+ public Object[] keyFrom(DATA data) {
+ return loader.extract(data, size, CRUD.delete); // Delete is key only
+ }
- /*
- * Note: in case PSInfos are deleted, we want to remove them from list. This is not expected,
- * but we don't want a data leak if it does. Finalize doesn't have to happen quickly
- */
- @Override
- protected void finalize() throws Throwable {
- psinfos.remove(this);
- }
- }
+ /*
+ * Note: in case PSInfos are deleted, we want to remove them from list. This is not expected,
+ * but we don't want a data leak if it does. Finalize doesn't have to happen quickly
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ psinfos.remove(this);
+ }
+ }
- protected final Accept<DATA> dflt = new Accept<DATA>() {
- @Override
- public boolean ok(DATA data) {
- return true;
- }
- };
+ protected final Accept<DATA> dflt = new Accept<DATA>() {
+ @Override
+ public boolean ok(DATA data) {
+ return true;
+ }
+ };
- @SuppressWarnings("unchecked")
+ @SuppressWarnings("unchecked")
protected final Result<List<DATA>> extract(Loader<DATA> loader, ResultSet rs, List<DATA> indata, Accept<DATA> accept) {
- List<Row> rows = rs.all();
- if(rows.isEmpty()) {
- return Result.ok((List<DATA>)EMPTY); // Result sets now .emptyList(true);
- } else {
- DATA d;
- List<DATA> data = indata==null?new ArrayList<>(rows.size()):indata;
-
- for(Row row : rows) {
- try {
- d = loader.load(dataClass.newInstance(),row);
- if(accept.ok(d)) {
- data.add(d);
- }
- } catch(Exception e) {
- return Result.err(e);
- }
- }
- return Result.ok(data);
- }
+ List<Row> rows = rs.all();
+ if(rows.isEmpty()) {
+ return Result.ok((List<DATA>)EMPTY); // Result sets now .emptyList(true);
+ } else {
+ DATA d;
+ List<DATA> data = indata==null?new ArrayList<>(rows.size()):indata;
+
+ for(Row row : rows) {
+ try {
+ d = loader.load(dataClass.newInstance(),row);
+ if(accept.ok(d)) {
+ data.add(d);
+ }
+ } catch(Exception e) {
+ return Result.err(e);
+ }
+ }
+ return Result.ok(data);
+ }
}
- private static final String NEW_CASSANDRA_SESSION_CREATED = "New Cassandra Session Created";
- private static final String NEW_CASSANDRA_CLUSTER_OBJECT_CREATED = "New Cassandra Cluster Object Created";
- private static final String NEW_CASSANDRA_SESSION = "New Cassandra Session";
- private static final Object LOCK = new Object();
+ private static final String NEW_CASSANDRA_SESSION_CREATED = "New Cassandra Session Created";
+ private static final String NEW_CASSANDRA_CLUSTER_OBJECT_CREATED = "New Cassandra Cluster Object Created";
+ private static final String NEW_CASSANDRA_SESSION = "New Cassandra Session";
+ private static final Object LOCK = new Object();
- private static class ResetRequest {
- //package on purpose
- Session session;
- long timestamp;
-
- public ResetRequest(Session session) {
- this.session = session;
- timestamp = System.currentTimeMillis();
- }
- }
+ private static class ResetRequest {
+ //package on purpose
+ Session session;
+ long timestamp;
+
+ public ResetRequest(Session session) {
+ this.session = session;
+ timestamp = System.currentTimeMillis();
+ }
+ }
-
- public static final void primePSIs(TransStore trans) throws APIException, IOException {
- for(AbsCassDAO<? extends TransStore, ?>.PSInfo psi : psinfos) {
- if(psi.ps==null) {
- psi.ps(trans);
- }
- }
- }
-
- public final Session getSession(TransStore trans) throws APIException, IOException {
- // SessionFilter unused since 2015
- // Try to use Trans' session, if exists
-// if(sessionSlot!=null) { // try to get from Trans
-// Session sess = trans.get(sessionSlot, null);
-// if(sess!=null) {
-// return sess;
-// }
-// }
-
- // If there's an owning DAO, use it's session
- if(owningDAO!=null) {
- return owningDAO.getSession(trans);
- }
-
- // OK, nothing else works... get our own.
- if(session==null || resetTrigger) {
- Cluster tempCluster = null;
- Session tempSession = null;
- try {
- synchronized(LOCK) {
- boolean reset = false;
- for(ResetRequest r : resetDeque) {
- if(r.session == session) {
- if(r.timestamp>nextAvailableReset) {
- reset=true;
- nextAvailableReset = System.currentTimeMillis() + 60000;
- tempCluster = cluster;
- tempSession = session;
- break;
- } else {
- trans.warn().log("Cassandra Connection Reset Ignored: Recent Reset");
- }
- }
- }
-
- if(reset || session == null) {
- TimeTaken tt = trans.start(NEW_CASSANDRA_SESSION, Env.SUB);
- try {
- // Note: Maitrayee recommended not closing the cluster, just
- // overwrite it. Jonathan 9/30/2016 assuming same for Session
- // This was a bad idea. Ran out of File Handles as I suspected, Jonathan
- if(reset) {
- for(AbsCassDAO<? extends TransStore, ?>.PSInfo psi : psinfos) {
- psi.reset();
- }
- }
- if(reset || cluster==null) {
- cluster = CassAccess.cluster(trans, keyspace);
- trans.warn().log(NEW_CASSANDRA_CLUSTER_OBJECT_CREATED);
- }
- if(reset || session==null) {
- session = cluster.connect(keyspace);
- trans.warn().log(NEW_CASSANDRA_SESSION_CREATED);
- }
- } finally {
- resetTrigger=false;
- tt.done();
- }
- }
- }
- } finally {
- TimeTaken tt = trans.start("Clear Reset Deque", Env.SUB);
- try {
- resetDeque.clear();
- // Not clearing Session/Cluster appears to kill off FileHandles
- if(tempSession!=null && !tempSession.isClosed()) {
- tempSession.close();
- }
- if(tempCluster!=null && !tempCluster.isClosed()) {
- tempCluster.close();
- }
- } finally {
- tt.done();
- }
- }
- }
- return session;
- }
-
- public final boolean reportPerhapsReset(TransStore trans, Exception e) {
- if(owningDAO!=null) {
- return owningDAO.reportPerhapsReset(trans, e);
- } else {
- boolean rv = false;
- if(CassAccess.isResetException(e)) {
- trans.warn().printf("Session Reset called for %s by %s ",session==null?"":session,e==null?"Mgmt Command":e.getClass().getName());
- resetDeque.addFirst(new ResetRequest(session));
- rv = resetTrigger = true;
- }
- trans.error().log(e);
- return rv;
- }
- }
+
+ public static final void primePSIs(TransStore trans) throws APIException, IOException {
+ for(AbsCassDAO<? extends TransStore, ?>.PSInfo psi : psinfos) {
+ if(psi.ps==null) {
+ psi.ps(trans);
+ }
+ }
+ }
+
+ public final Session getSession(TransStore trans) throws APIException, IOException {
+ // SessionFilter unused since 2015
+ // Try to use Trans' session, if exists
+// if(sessionSlot!=null) { // try to get from Trans
+// Session sess = trans.get(sessionSlot, null);
+// if(sess!=null) {
+// return sess;
+// }
+// }
+
+ // If there's an owning DAO, use it's session
+ if(owningDAO!=null) {
+ return owningDAO.getSession(trans);
+ }
+
+ // OK, nothing else works... get our own.
+ if(session==null || resetTrigger) {
+ Cluster tempCluster = null;
+ Session tempSession = null;
+ try {
+ synchronized(LOCK) {
+ boolean reset = false;
+ for(ResetRequest r : resetDeque) {
+ if(r.session == session) {
+ if(r.timestamp>nextAvailableReset) {
+ reset=true;
+ nextAvailableReset = System.currentTimeMillis() + 60000;
+ tempCluster = cluster;
+ tempSession = session;
+ break;
+ } else {
+ trans.warn().log("Cassandra Connection Reset Ignored: Recent Reset");
+ }
+ }
+ }
+
+ if(reset || session == null) {
+ TimeTaken tt = trans.start(NEW_CASSANDRA_SESSION, Env.SUB);
+ try {
+ // Note: Maitrayee recommended not closing the cluster, just
+ // overwrite it. Jonathan 9/30/2016 assuming same for Session
+ // This was a bad idea. Ran out of File Handles as I suspected, Jonathan
+ if(reset) {
+ for(AbsCassDAO<? extends TransStore, ?>.PSInfo psi : psinfos) {
+ psi.reset();
+ }
+ }
+ if(reset || cluster==null) {
+ cluster = CassAccess.cluster(trans, keyspace);
+ trans.warn().log(NEW_CASSANDRA_CLUSTER_OBJECT_CREATED);
+ }
+ if(reset || session==null) {
+ session = cluster.connect(keyspace);
+ trans.warn().log(NEW_CASSANDRA_SESSION_CREATED);
+ }
+ } finally {
+ resetTrigger=false;
+ tt.done();
+ }
+ }
+ }
+ } finally {
+ TimeTaken tt = trans.start("Clear Reset Deque", Env.SUB);
+ try {
+ resetDeque.clear();
+ // Not clearing Session/Cluster appears to kill off FileHandles
+ if(tempSession!=null && !tempSession.isClosed()) {
+ tempSession.close();
+ }
+ if(tempCluster!=null && !tempCluster.isClosed()) {
+ tempCluster.close();
+ }
+ } finally {
+ tt.done();
+ }
+ }
+ }
+ return session;
+ }
+
+ public final boolean reportPerhapsReset(TransStore trans, Exception e) {
+ if(owningDAO!=null) {
+ return owningDAO.reportPerhapsReset(trans, e);
+ } else {
+ boolean rv = false;
+ if(CassAccess.isResetException(e)) {
+ trans.warn().printf("Session Reset called for %s by %s ",session==null?"":session,e==null?"Mgmt Command":e.getClass().getName());
+ resetDeque.addFirst(new ResetRequest(session));
+ rv = resetTrigger = true;
+ }
+ trans.error().log(e);
+ return rv;
+ }
+ }
- public void close(TransStore trans) {
- if(owningDAO==null) {
- if(session!=null) {
- TimeTaken tt = trans.start("Cassandra Session Close", Env.SUB);
- try {
- session.close();
- } finally {
- tt.done();
- }
- session = null;
- } else {
- trans.debug().log("close called(), Session already closed");
- }
- } else {
- owningDAO.close(trans);
- }
- }
+ public void close(TransStore trans) {
+ if(owningDAO==null) {
+ if(session!=null) {
+ TimeTaken tt = trans.start("Cassandra Session Close", Env.SUB);
+ try {
+ session.close();
+ } finally {
+ tt.done();
+ }
+ session = null;
+ } else {
+ trans.debug().log("close called(), Session already closed");
+ }
+ } else {
+ owningDAO.close(trans);
+ }
+ }
- protected void wasModified(TRANS trans, CRUD modified, DATA data, String ... override) {
- }
-
- protected interface Accept<DATA> {
- public boolean ok(DATA data);
- }
+ protected void wasModified(TRANS trans, CRUD modified, DATA data, String ... override) {
+ }
+
+ protected interface Accept<DATA> {
+ public boolean ok(DATA data);
+ }
}