Issue #2579 Make DecisionTree thread safe.

Change-Id: I351135189c58ba5a6107de206f8f5dc2d72fc8c0

Former-commit-id: fea2532ea7 [formerly b92f9efb53] [formerly b096554d97 [formerly 345316581c514136c823692a09ff0d71ce5b7877]]
Former-commit-id: b096554d97
Former-commit-id: 0d1898a070
This commit is contained in:
Ben Steffensmeier 2013-12-18 11:26:02 -06:00
parent eee649c9b1
commit 1dc5fcd852
9 changed files with 136 additions and 119 deletions

View file

@ -28,6 +28,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintType;
@ -43,12 +45,15 @@ import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintTyp
*
* <pre>
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jul 3, 2007 chammack Initial Creation.
* Jan 14, 2013 1442 rferrel Added method searchTreeUsingContraints.
* Addition checks on constraints.
* May 28, 2013 1638 mschenke Added proper support for {@link ConstraintType#ISNULL}
* Date Ticket# Engineer Description
* ------------- -------- ----------- -----------------------------------------
* Jul 03, 2007 chammack Initial Creation.
* Jan 14, 2013 1442 rferrel Added method searchTreeUsingContraints.
* Addition checks on constraints.
* May 28, 2013 1638 mschenke Added proper support for
* {@link ConstraintType#ISNULL}
* Dec 18, 2013 2579 bsteffen Replace synchronization with a
* read/write lock.
*
* </pre>
*
@ -94,9 +99,7 @@ public class DecisionTree<T> {
if (lvl == 0) {
// heuristic: Always start with pluginName
entropyPair = new EntropyPair[1];
entropyPair[0] = new EntropyPair();
entropyPair[0].attribute = "pluginName";
entropyPair[0].entropy = 1.0f;
entropyPair[0] = new EntropyPair("pluginName", 1.0f);
} else {
for (String attrib : localAttribList) {
// For an attribute, pull out the possible values
@ -133,9 +136,8 @@ public class DecisionTree<T> {
Iterator<String> attributeListIter = localAttribList.iterator();
for (int i = 0; attributeListIter.hasNext(); i++) {
entropyPair[i] = new EntropyPair();
entropyPair[i].attribute = attributeListIter.next();
entropyPair[i].entropy = entropyValues.get(i);
entropyPair[i] = new EntropyPair(attributeListIter.next(),
entropyValues.get(i));
}
Arrays.sort(entropyPair);
@ -225,22 +227,25 @@ public class DecisionTree<T> {
}
protected class DataPair {
public Map<String, RequestConstraint> metadata;
public final Map<String, RequestConstraint> metadata;
public final T data;
public DataPair(Map<String, RequestConstraint> metadata, T data) {
this.metadata = metadata;
this.data = data;
}
public T data;
}
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final List<DataPair> dataPairs;
private final Set<String> attributes;
private Node head;
private int size = 0;
public DecisionTree() {
dataPairs = new ArrayList<DataPair>();
attributes = new HashSet<String>();
}
public void insertCriteria(Map<String, RequestConstraint> searchCriteria,
@ -249,25 +254,22 @@ public class DecisionTree<T> {
throw new IllegalArgumentException(
"Search criteria must not be null");
// Check for the case that the item is already listed
DataPair e = new DataPair();
e.data = item;
e.metadata = searchCriteria;
this.dataPairs.add(e);
size++;
DataPair e = new DataPair(searchCriteria, item);
Set<String> keys = searchCriteria.keySet();
this.attributes.addAll(keys);
if (rebuild) {
// Now, trigger a tree rebuild
rebuildTree();
lock.writeLock().lock();
try {
this.dataPairs.add(e);
if (rebuild) {
rebuildTree();
}
} finally {
lock.writeLock().unlock();
}
}
public void rebuildTree() {
synchronized (this) {
lock.writeLock().lock();
try {
if (this.dataPairs.size() == 0) {
this.head = null;
return;
@ -275,6 +277,8 @@ public class DecisionTree<T> {
this.head = new Node();
this.head.rebuildTree(dataPairs, new ArrayList<String>(), 0);
} finally {
lock.writeLock().unlock();
}
}
@ -318,7 +322,8 @@ public class DecisionTree<T> {
*/
private List<T> searchTree(Map<String, ?> searchCriteria,
boolean evaluateConstraints) {
synchronized (this) {
lock.readLock().lock();
try {
List<T> lst = new ArrayList<T>();
if (head == null) {
return lst;
@ -328,6 +333,8 @@ public class DecisionTree<T> {
searchTree(curNode, searchCriteria, lst, 0, evaluateConstraints);
return lst;
} finally {
lock.readLock().unlock();
}
}
@ -391,8 +398,10 @@ public class DecisionTree<T> {
* @param item
*/
public void remove(T item) {
boolean itemRemoved = false;
synchronized (this) {
lock.writeLock().lock();
try {
boolean itemRemoved = false;
// This could be optimized but removes are a very uncommon operation
Iterator<DataPair> exampleIterator = dataPairs.iterator();
while (exampleIterator.hasNext()) {
@ -404,39 +413,26 @@ public class DecisionTree<T> {
itemRemoved = true;
}
}
}
if (itemRemoved) {
rebuildTree();
}
}
public void traverse() {
System.out.println("Head:");
traverse(head);
}
public void traverse(Node n) {
if (n == null)
return;
System.out.println(n.type);
if (n.type == NodeType.LEAF) {
System.out.println(n.values);
} else if (n.type == NodeType.DECISION) {
System.out.println(n.decision);
}
System.out.println(n.decisionAttribute);
System.out.println("-------");
for (int i = 0; i < n.nodeChildren.size(); i++) {
System.out.println("Child of: " + n.decisionAttribute + " " + i);
Node n2 = n.nodeChildren.get(i);
traverse(n2);
if (itemRemoved) {
rebuildTree();
}
} finally {
lock.writeLock().unlock();
}
}
protected List<DataPair> getDataPairs() {
return new ArrayList<DecisionTree<T>.DataPair>(dataPairs);
/*
* Copy dataPairs to avoid external iterators getting concurrent
* modification. Must get read lock because copying iterates over
* dataPairs.
*/
lock.readLock().lock();
try {
return new ArrayList<DataPair>(dataPairs);
} finally {
lock.readLock().unlock();
}
}
private static double calcEntropy(int numExamples, Integer[] values) {
@ -451,9 +447,14 @@ public class DecisionTree<T> {
}
private static class EntropyPair implements Comparable<EntropyPair> {
public String attribute;
public final String attribute;
public double entropy;
public final double entropy;
public EntropyPair(String attribute, double entropy) {
this.attribute = attribute;
this.entropy = entropy;
}
/*
* (non-Javadoc)

View file

@ -25,7 +25,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.raytheon.uf.common.serialization.ISerializableObject;
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
@ -35,16 +34,18 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
*
* <pre>
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 11/07/08 #1673 bphillip Initial Creation
* Date Ticket# Engineer Description
* ------------- -------- ----------- --------------------------
* Nov 07, 2008 1673 bphillip Initial Creation
* Dec 18, 2013 2579 bsteffen Remove ISerializableObject
*
* </pre>
*
* @author bphillip
* @version 1.0
*/
@DynamicSerialize
public class QueryResult implements ISerializableObject {
public class QueryResult {
/** A mapping of the column names to their index in the result */
@DynamicSerializeElement
@ -167,12 +168,10 @@ public class QueryResult implements ISerializableObject {
*/
public String[] getColumnNameArray() {
String[] names = new String[columnNames.size()];
int i = 0;
for (Iterator<String> it = columnNames.keySet().iterator(); it
.hasNext();) {
String key = it.next();
names[columnNames.get(key)] = key;
i++;
}
return names;
}

View file

@ -22,7 +22,6 @@ package com.raytheon.uf.common.dataquery.db;
import java.util.Arrays;
import com.raytheon.uf.common.serialization.ISerializableObject;
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
@ -31,16 +30,18 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
*
* <pre>
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 11/07/08 #1673 bphillip Initial Creation
* Date Ticket# Engineer Description
* ------------- -------- ----------- --------------------------
* Nov 07, 2008 1673 bphillip Initial Creation
* Dec 18, 2013 2579 bsteffen Remove ISerializableObject
*
* </pre>
*
* @author bphillip
* @version 1.0
*/
@DynamicSerialize
public class QueryResultRow implements ISerializableObject {
public class QueryResultRow {
/**
* The values of the returned columns

View file

@ -24,20 +24,24 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
import com.raytheon.uf.common.serialization.comm.IServerRequest;
/**
* TODO Add Description
* Send multiple {@link DbQueryRequestSet}s at once. This can be more efficient
* than sending multiple requests individually because it reduces the network
* overhead.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 30, 2011 rjpeter Initial creation
* Date Ticket# Engineer Description
* ------------- -------- ----------- --------------------------
* Jun 30, 2011 rjpeter Initial creation
* Dec 18, 2013 2579 bsteffen Class javadoc
*
* </pre>
*
* @author rjpeter
* @version 1.0
* @see DbQueryRequest
*/
@DynamicSerialize
public class DbQueryRequestSet implements IServerRequest {

View file

@ -21,7 +21,6 @@ package com.raytheon.uf.common.dataquery.requests;
import java.util.Map;
import com.raytheon.uf.common.serialization.ISerializableObject;
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
import com.raytheon.uf.common.serialization.comm.IServerRequest;
@ -32,9 +31,10 @@ import com.raytheon.uf.common.serialization.comm.IServerRequest;
* <pre>
*
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 16, 2011 #8070 ekladstrup Initial creation
* Date Ticket# Engineer Description
* ------------- -------- ----------- --------------------------
* Feb 16, 2011 8070 ekladstrup Initial creation
* Dec 18, 2013 2579 bsteffen Remove ISerializableObject
*
* </pre>
*
@ -42,7 +42,7 @@ import com.raytheon.uf.common.serialization.comm.IServerRequest;
* @version 1.0
*/
@DynamicSerialize
public class QlServerRequest implements IServerRequest, ISerializableObject {
public class QlServerRequest implements IServerRequest {
@DynamicSerializeElement
private Map<String, RequestConstraint> rcMap;

View file

@ -40,29 +40,31 @@ import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import com.raytheon.uf.common.serialization.ISerializableObject;
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
import com.raytheon.uf.common.time.util.TimeUtil;
/**
* RequestConstraint - Constraints on a uEngine request
*
* Allows non-equals style constraints
* Used in requests to limit the type of data returned. Similar to an sql WHERE
* clause, it consists of a type(operator) and a value. When a request is made
* fields will be compared to the constraint value with the specified type to
* determine what data to return.
*
* <pre>
*
* SOFTWARE HISTORY
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 21, 2007 chammack Initial Creation.
* May 27, 2009 2408 jsanchez Cast value to String.
* Sep 28, 2009 3099 bsteffen Fixed constraintCompare to convert
* all non-numeric objects to String
* Nov 05, 2009 3553 rjpeter Added isNull capability.
* Jul 09, 2013 1869 bsteffen Format Calendar when making
* Constraint Mapping.
* Date Ticket# Engineer Description
* ------------- -------- ----------- -----------------------------------------
* Aug 21, 2007 chammack Initial Creation.
* May 27, 2009 2408 jsanchez Cast value to String.
* Sep 28, 2009 3099 bsteffen Fixed constraintCompare to convert all
* non-numeric objects to String
* Nov 05, 2009 3553 rjpeter Added isNull capability.
* Jul 09, 2013 1869 bsteffen Format Calendar when making Constraint
* Mapping.
* Dec 18, 2013 2579 bsteffen Remove ISerializableObject
*
*
* </pre>
*
@ -73,7 +75,7 @@ import com.raytheon.uf.common.time.util.TimeUtil;
@XmlRootElement(name = "requestConstraint")
@XmlType(name = "requestConstraint")
@DynamicSerialize
public class RequestConstraint implements ISerializableObject, Cloneable {
public class RequestConstraint implements Cloneable {
public static final RequestConstraint WILDCARD;
static {
@ -114,7 +116,8 @@ public class RequestConstraint implements ISerializableObject, Cloneable {
@DynamicSerializeElement
protected String constraintValue;
protected transient Map<Class<?>, Object> asMap = new HashMap<Class<?>, Object>();
protected transient Map<Class<?>, Object> asMap = new HashMap<Class<?>, Object>(
2);
/**
* Constructor

View file

@ -24,20 +24,25 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
import com.raytheon.uf.common.serialization.comm.IServerRequest;
/**
* TODO Add Description
* Send multiple {@link TimeQueryRequest}s at once. This can be more efficient
* than sending multiple requests individually because it reduces the network
* overhead. The response will be a List<List<DataTime>> where the list contains
* one entry for each request, in the same order as the requests.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jul 1, 2011 rjpeter Initial creation
* Date Ticket# Engineer Description
* ------------- -------- ----------- --------------------------
* Jul0 1, 2011 rjpeter Initial creation
* Dec 18, 2013 2579 bsteffen Class javadoc
*
* </pre>
*
* @author rjpeter
* @version 1.0
* @see TimeQueryRequest
*/
@DynamicSerialize
public class TimeQueryRequestSet implements IServerRequest {

View file

@ -24,7 +24,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.raytheon.uf.common.serialization.ISerializableObject;
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
@ -37,9 +36,10 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
* <pre>
*
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jan 21, 2010 mschenke Initial creation
* Date Ticket# Engineer Description
* ------------- -------- ----------- --------------------------
* Jan 21, 2010 mschenke Initial creation
* Dec 18, 2013 2579 bsteffen Remove ISerializableObject
*
* </pre>
*
@ -47,7 +47,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
* @version 1.0
*/
@DynamicSerialize
public class DbQueryResponse implements ISerializableObject {
public class DbQueryResponse {
public static final String ENTITY_RESULT_KEY = null;

View file

@ -19,28 +19,32 @@
**/
package com.raytheon.uf.common.dataquery.responses;
import com.raytheon.uf.common.serialization.ISerializableObject;
import com.raytheon.uf.common.dataquery.requests.DbQueryRequestSet;
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
/**
* TODO Add Description
* Response to a {@link DbQueryResponseSet}, contains a response to every
* request in the same order.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jul 1, 2011 rjpeter Initial creation
* Date Ticket# Engineer Description
* ------------- -------- ----------- --------------------------
* Jul 01, 2011 rjpeter Initial creation
* Dec 18, 2013 2579 bsteffen Remove ISerializableObject
*
* </pre>
*
* @author rjpeter
* @version 1.0
* @see DbQueryResponse
* @see DbQueryRequestSet
*/
@DynamicSerialize
public class DbQueryResponseSet implements ISerializableObject {
public class DbQueryResponseSet {
@DynamicSerializeElement
private DbQueryResponse[] results;