changeset 9:17ed97ca9960

commit
author shoshi
date Mon, 18 Apr 2011 01:07:27 +0900
parents f96193babac0
children 4e0ed81bea89
files src/treecms/api/Forest.java src/treecms/api/MonotonicTree.java src/treecms/api/MonotonicTreeNode.java src/treecms/api/SingleNode.java src/treecms/api/TreeEditor.java src/treecms/api/TreeNode.java src/treecms/gui/GUIEditor.java src/treecms/gui/NodeViewerTree.java src/treecms/memory/OnMemoryForest.java src/treecms/memory/OnMemoryTree.java src/treecms/memory/OnMemoryTreeEditor.java src/treecms/memory/test/OnMemoryForestTest.java src/treecms/memory/test/OnMemoryNodeTest.java src/treecms/test/AbstractForestTest.java src/treecms/test/AbstractNodeTest.java src/treecms/test/AbstractTreeEditorTest.java src/treecms/test/AbstractTreeTest.java src/treecms/test/ForestTest.java src/treecms/test/NodeTest.java src/treecms/test/OrderedSetTest.java src/treecms/test/TreeEditorTest.java src/treecms/test/TreeTest.java src/treecms/tree/cassandra/v1/CassandraForest.java src/treecms/tree/cassandra/v1/ClientThread.java src/treecms/tree/cassandra/v1/ClientThreadFactory.java src/treecms/tree/cassandra/v1/ClientWrapper.java src/treecms/tree/cassandra/v1/util/CassandraClientThread.java src/treecms/tree/cassandra/v1/util/CassandraClientThreadFactory.java src/treecms/tree/cassandra/v1/util/CassandraClientWrapper.java src/treecms/tree/cassandra/v1/util/ConcurrentCassandraClient.java
diffstat 30 files changed, 968 insertions(+), 675 deletions(-) [+]
line wrap: on
line diff
--- a/src/treecms/api/Forest.java	Thu Mar 31 02:08:44 2011 +0900
+++ b/src/treecms/api/Forest.java	Mon Apr 18 01:07:27 2011 +0900
@@ -14,20 +14,6 @@
 	Node get(NodeID _id);
 	
 	/**
-	 * あるNodeをルートとしてTreeのオブジェクトを取得します。
-	 * @param _id 木のルートとなるNodeのNodeID
-	 * @return Tree
-	 */
-	Tree getAsTree(NodeID _id);
-	
-	/**
-	 * あるNodeをルートとしたTreeを非破壊的に編集するTreeEditorを取得します。
-	 * @param _id 木のルートとなるNodeのNodeID
-	 * @return TreeEditor
-	 */
-	TreeEditor getAsTreeEditor(NodeID _id);
-	
-	/**
 	 * 同じUUIDを持つNode中で最新のNodeを取得します.
 	 * @param _uuid NodeIDのUUID
 	 * @return UUIDと一致するNodeが見つからない場合はnullを返します.
@@ -41,6 +27,20 @@
 	Node create();
 	
 	/**
+	 * あるNodeを木として返します
+	 * @param _root
+	 * @return Tree あるNodeをルートとした木
+	 */
+	Tree getTree(Node _root);
+	
+	/**
+	 * 木を非破壊的に編集するTreeEditorを取得します
+	 * @param _tree 対象
+	 * @return TreeEditor
+	 */
+	TreeEditor getTreeEditor(Tree _tree);
+	
+	/**
 	 * NodeDataを保持する新しいNodeを作成します.このメソッドで作成されるNodeは新しいUUIDを持ちます.
 	 * このメソッドはNodeDataをNodeに割り当てるとき防御的コピーを行います.
 	 * @param _data 新しいNodeが保持するNodeData
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/api/MonotonicTree.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,63 @@
+package treecms.api;
+
+import treecms.tree.util.PathNotFoundException;
+
+/**
+ * 木構造を非破壊的に更新する機能を提供します.TreeEditorはTreeを非破壊的に更新していき,commitすることでTreeに更新を適用します.
+ * TreeEditor.getRootはcommitされていない状態のRootNodeを取得します.
+ * この機能は分散リポジトリを参考に考案されました.
+ * @author shoshi
+ */
+public interface MonotonicTree
+{
+	/**
+	 * 非破壊的に更新した木構造を適用します.
+	 * 更新する際に他の方法により木構造がすでに更新されていた場合,commitは失敗します。_forceがtrueの場合,強制的に置き換えます. 
+	 * @param _force 強制コミットフラグ
+	 * @return 成功した場合true,失敗した場合false
+	 */
+	public boolean commit(boolean _force);
+	
+	/**
+	 * 監視している木構造をEditorにキャッシュします.
+	 * @return キャッシュが成功した場合はtrue,失敗した場合はfalse
+	 */
+	public boolean pull();
+	
+	/**
+	 * 監視されている木構造が更新されていないかチェックします.
+	 * @return 更新されていた場合はture,されていない場合はfalse
+	 */
+	public boolean check();
+	
+	/**
+	 * 監視している木構造をキャッシュにマージします.
+	 */
+	public void merge();
+	
+	/**
+	 * この木構造のルートNodeを返します。
+	 * @return この木構造のルートNode
+	 */
+	public Node getRoot();
+	
+	/**
+	 * 木構造を非破壊的に更新します.変更の対象となるNodeが木構造内に見つからない場合,PathNotFoundExceptionがスローされます.
+	 * @param _target 更新する対象のNode
+	 * @param _newData 新しいNodeに割り当てられるNodeData
+	 * @return 更新された新しいNode
+	 * @throws PathNotFoundException パスが見つからない場合
+	 */
+	public Node updateTree(Node _target,NodeData _newData) throws PathNotFoundException;
+	
+	/**
+	 * 木構造を非破壊的に更新します.Nodeへのパスが既知な場合このメソッドを使用できます。
+	 * このメソッドは使用時にパスの正当性を検証します。見つからない場合PathNotFoundExceptionがスローされます
+	 * @param _target
+	 * @param _newData
+	 * @param _path
+	 * @return 更新された新しいNode
+	 * @throws PathNotFoundException 
+	 */
+	public Node updateTree(Node _target,NodeData _newData,Node[] _path) throws PathNotFoundException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/api/MonotonicTreeNode.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,7 @@
+package treecms.api;
+
+public interface MonotonicTreeNode
+{
+	public MonotonicTreeNode getParent();
+	public SingleNode getNode();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/api/SingleNode.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,5 @@
+package treecms.api;
+
+public interface SingleNode extends Node
+{
+}
--- a/src/treecms/api/TreeEditor.java	Thu Mar 31 02:08:44 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-package treecms.api;
-
-import treecms.tree.util.PathNotFoundException;
-
-/**
- * 木構造を非破壊的に更新する機能を提供します.TreeEditorはTreeを非破壊的に更新していき,commitすることでTreeに更新を適用します.
- * TreeEditor.getRootはcommitされていない状態のRootNodeを取得します.
- * この機能は分散リポジトリを参考に考案されました.
- * @author shoshi
- */
-public interface TreeEditor
-{
-	/**
-	 * 非破壊的に更新した木構造を適用します.
-	 * 更新する際に他の方法により木構造がすでに更新されていた場合,commitは失敗します。_forceがtrueの場合,強制的に置き換えます. 
-	 * @param _force 強制コミットフラグ
-	 * @return 成功した場合true,失敗した場合false
-	 */
-	public boolean commit(boolean _force);
-	
-	/**
-	 * 監視している木構造をEditorにキャッシュします.
-	 * @return キャッシュが成功した場合はtrue,失敗した場合はfalse
-	 */
-	public boolean pull();
-	
-	/**
-	 * 監視されている木構造が更新されていないかチェックします.
-	 * @return 更新されていた場合はture,されていない場合はfalse
-	 */
-	public boolean check();
-	
-	/**
-	 * 監視している木構造をキャッシュにマージします.
-	 */
-	public void merge();
-	
-	/**
-	 * この木構造のルートNodeを返します。
-	 * @return この木構造のルートNode
-	 */
-	public Node getRoot();
-	
-	/**
-	 * 木構造を非破壊的に更新します.変更の対象となるNodeが木構造内に見つからない場合,PathNotFoundExceptionがスローされます.
-	 * @param _target 更新する対象のNode
-	 * @param _newData 新しいNodeに割り当てられるNodeData
-	 * @return 更新された新しいNode
-	 * @throws PathNotFoundException パスが見つからない場合
-	 */
-	public Node updateTree(Node _target,NodeData _newData) throws PathNotFoundException;
-	
-	/**
-	 * 木構造を非破壊的に更新します.Nodeへのパスが既知な場合このメソッドを使用できます。
-	 * このメソッドは使用時にパスの正当性を検証します。見つからない場合PathNotFoundExceptionがスローされます
-	 * @param _target
-	 * @param _newData
-	 * @param _path
-	 * @return 更新された新しいNode
-	 * @throws PathNotFoundException 
-	 */
-	public Node updateTree(Node _target,NodeData _newData,Node[] _path) throws PathNotFoundException;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/api/TreeNode.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,7 @@
+package treecms.api;
+
+public interface TreeNode extends Node
+{
+	public TreeNode getParent();
+	public SingleNode getNode();
+}
--- a/src/treecms/gui/GUIEditor.java	Thu Mar 31 02:08:44 2011 +0900
+++ b/src/treecms/gui/GUIEditor.java	Mon Apr 18 01:07:27 2011 +0900
@@ -31,6 +31,7 @@
 import treecms.api.Tree;
 import treecms.api.TreeEditor;
 import treecms.memory.OnMemoryForest;
+import treecms.tree.util.PathNotFoundException;
 
 public class GUIEditor
 {
@@ -38,7 +39,7 @@
 	
 	//GUIコンポーネント
 	private JFrame m_frame;
-	private JTree m_nodeTree;
+	private NodeViewerTree m_nodeTree;
 	private AttributeEditorTable m_attrTable;
 	private JButton m_commit,m_pull,m_check,m_merge;
 	private JButton m_attrSave,m_attrCancel;
@@ -164,17 +165,29 @@
 					NodeData data = new NodeData();
 					data.putAll(result);
 					
-					DefaultMutableTreeNode node = (DefaultMutableTreeNode)m_nodeTree.getLastSelectedPathComponent();
-					
-					Node parent = (Node)node.getUserObject();
-					Node child = parent.getForest().create(data);
-					parent.add(child);
+					//パスの取得
+					Object[] treePath = m_nodeTree.getSelectionPath().getPath();
+					Node[] nodePath = new Node[treePath.length];
+					for(int i = 0;i < treePath.length;i ++){
+						System.out.println(treePath[i].getClass().toString());
+						DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)treePath[i];
+						nodePath[i] = (Node)treeNode.getUserObject();
+					}
 					
-					DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(child);
-					node.add(newNode);
+					Node target = nodePath[nodePath.length-1];
+					Node child = target.getForest().create(data);
+					NodeData newData = target.getData();
+					newData.add(child);
+					try{
+						m_editor.updateTree(target,newData,nodePath);
+					}catch(PathNotFoundException _err){
+						_err.printStackTrace();
+						JOptionPane.showMessageDialog(m_frame,"追加に失敗しました:"+_err.getMessage());
+						return;
+					}
 					
-					DefaultTreeModel model = (DefaultTreeModel)m_nodeTree.getModel();
-					model.reload();
+					NodeViewerTree.NVTreeModel model = (NodeViewerTree.NVTreeModel)m_nodeTree.getModel();
+					model.setTree(m_editor.getRoot(),true);
 				}
 			}
 			
--- a/src/treecms/gui/NodeViewerTree.java	Thu Mar 31 02:08:44 2011 +0900
+++ b/src/treecms/gui/NodeViewerTree.java	Mon Apr 18 01:07:27 2011 +0900
@@ -28,7 +28,7 @@
 		node.add(newNode);
 	}
 	
-	private static class NVTreeModel extends DefaultTreeModel
+	static class NVTreeModel extends DefaultTreeModel
 	{
 		public NVTreeModel(Node _node)
 		{
--- a/src/treecms/memory/OnMemoryForest.java	Thu Mar 31 02:08:44 2011 +0900
+++ b/src/treecms/memory/OnMemoryForest.java	Mon Apr 18 01:07:27 2011 +0900
@@ -14,7 +14,6 @@
 
 /**
  * Forestのオンメモリ上の実装.
- * このクラスはスレッドセーフだと思います.たぶん
  * @author shoshi
  */
 public class OnMemoryForest implements Forest
@@ -84,9 +83,14 @@
 	 * @return Tree
 	 */
 	@Override
-	public Tree getAsTree(NodeID _id)
+	public Tree getTree(Node _node)
 	{
-		return new OnMemoryTree((OnMemoryNode) get(_id));
+		Forest forest = _node.getForest();
+		if(forest != this){
+			throw new IllegalArgumentException();
+		}
+		
+		return new OnMemoryTree((OnMemoryNode)_node);
 	}
 	
 	/**
@@ -95,9 +99,14 @@
 	 * @return TreeEditor
 	 */
 	@Override
-	public TreeEditor getAsTreeEditor(NodeID _id)
+	public TreeEditor getTreeEditor(Tree _tree)
 	{
-		return new OnMemoryTreeEditor((OnMemoryTree) getAsTree(_id));
+		Forest forest = _tree.getForest();
+		if(forest != this){
+			throw new IllegalArgumentException();
+		}
+		
+		return new OnMemoryTreeEditor((OnMemoryTree)_tree);
 	}
 	
 	/**
--- a/src/treecms/memory/OnMemoryTree.java	Thu Mar 31 02:08:44 2011 +0900
+++ b/src/treecms/memory/OnMemoryTree.java	Mon Apr 18 01:07:27 2011 +0900
@@ -4,6 +4,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 
 import treecms.api.Forest;
 import treecms.api.Node;
@@ -18,7 +19,8 @@
  */
 final class OnMemoryTree implements Tree
 {
-	private AtomicReference<OnMemoryNode> m_ref;
+	private static final AtomicReferenceFieldUpdater<OnMemoryTree,OnMemoryNode> m_refUpdater = AtomicReferenceFieldUpdater.newUpdater(OnMemoryTree.class,OnMemoryNode.class,"m_root");
+	private volatile OnMemoryNode m_root;
 	
 	/**
 	 * コンストラクタです。
@@ -26,7 +28,7 @@
 	 */
 	public OnMemoryTree(OnMemoryNode _newRoot)
 	{
-		m_ref = new AtomicReference<OnMemoryNode>(_newRoot);
+		m_root = _newRoot;
 	}
 	
 	/**
@@ -36,7 +38,7 @@
 	@Override
 	public Forest getForest()
 	{
-		return m_ref.get().getForest();
+		return m_root.getForest();
 	}
 
 	/**
@@ -46,7 +48,7 @@
 	@Override
 	public NodeID getID()
 	{
-		return m_ref.get().getID();
+		return m_root.getID();
 	}
 
 	/**
@@ -56,7 +58,7 @@
 	@Override
 	public NodeData getData()
 	{
-		return m_ref.get().getData();
+		return m_root.getData();
 	}
 
 	/**
@@ -66,7 +68,7 @@
 	@Override
 	public Node getRoot()
 	{
-		return m_ref.get();
+		return m_root;
 	}
 
 	/**
@@ -76,7 +78,7 @@
 	@Override
 	public void add(Node _child)
 	{
-		m_ref.get().add(_child);
+		m_root.add(_child);
 	}
 
 	/**
@@ -86,7 +88,7 @@
 	@Override
 	public void addAll(List<Node> _children)
 	{
-		m_ref.get().addAll(_children);
+		m_root.addAll(_children);
 	}
 
 	/**
@@ -96,7 +98,7 @@
 	@Override
 	public List<Node> children()
 	{
-		return m_ref.get().children();
+		return m_root.children();
 	}
 	
 	/**
@@ -107,7 +109,7 @@
 	@Override
 	public ByteBuffer get(ByteBuffer _key)
 	{
-		return m_ref.get().get(_key);
+		return m_root.get(_key);
 	}
 
 	/**
@@ -117,7 +119,7 @@
 	@Override
 	public Map<ByteBuffer,ByteBuffer> getAll()
 	{
-		return m_ref.get().getAll();
+		return m_root.getAll();
 	}
 
 	/**
@@ -128,7 +130,7 @@
 	@Override
 	public void put(ByteBuffer _key,ByteBuffer _value)
 	{
-		m_ref.get().put(_key,_value);
+		m_root.put(_key,_value);
 	}
 
 	/**
@@ -138,7 +140,7 @@
 	@Override
 	public void putAll(Map<ByteBuffer, ByteBuffer> _map)
 	{
-		m_ref.get().putAll(_map);
+		m_root.putAll(_map);
 	}
 	
 	/**
@@ -148,7 +150,7 @@
 	@Override
 	public void remove(ByteBuffer _key)
 	{
-		m_ref.get().remove(_key);
+		m_root.remove(_key);
 	}
 	
 	/**
@@ -158,7 +160,7 @@
 	@Override
 	public void remove(Node _child)
 	{
-		m_ref.get().remove(_child);
+		m_root.remove(_child);
 	}
 	
 	/**
@@ -166,13 +168,13 @@
 	 * @param _except 比較する対象 
 	 * @param _newRoot 一致した場合置き換える対象
 	 */
-	public boolean compareAndSwapRootNode(OnMemoryNode _except,OnMemoryNode _newRoot,boolean _force)
+	public boolean compareAndSwapRootNode(OnMemoryNode _expect,OnMemoryNode _newRoot,boolean _force)
 	{
 		if(_force){
-			return m_ref.compareAndSet(_except,_newRoot);
+			m_root = _newRoot;
 		}
 		
-		m_ref.set(_newRoot);
+		m_refUpdater.compareAndSet(this,_expect,_newRoot);
 		return true;
 	}
 
--- a/src/treecms/memory/OnMemoryTreeEditor.java	Thu Mar 31 02:08:44 2011 +0900
+++ b/src/treecms/memory/OnMemoryTreeEditor.java	Mon Apr 18 01:07:27 2011 +0900
@@ -1,9 +1,9 @@
 package treecms.memory;
 
-import java.util.LinkedList;
+import java.util.List;
+import treecms.api.Forest;
 import treecms.api.Node;
 import treecms.api.NodeData;
-import treecms.api.NodeID;
 import treecms.api.TreeEditor;
 import treecms.merger.Merger;
 import treecms.merger.ReplaceMerger;
@@ -78,24 +78,38 @@
 	}
 	
 	/**
-	 * 実装しろ
+	 * 木構造を非破壊的に更新します.
+	 * @param _target 更新する対象
+	 * @param _newData 更新に適用されるNodeData
+	 * @param _path 対象ノードまでのパス
+	 * @return 更新されたNode
+	 * @throws ルートNodeから_targetまでのパスが見つからない場合、PathNotFoundException
 	 */
 	@Override
-	public synchronized Node updateTree(Node _target,NodeData _newData,Node[] _path) throws PathNotFoundException
+	public Node updateTree(Node _target,NodeData _newData,Node[] _path) throws PathNotFoundException
 	{
 		//パスの正当性の検証
+		if(_path == null){
+			throw new NullPointerException("_path is null");
+		}
 		if(_path.length == 0){
 			throw new PathNotFoundException("node path is empty");
 		}else{
 			//ここでごちゃごちゃする
-			//めんどくさい
+			if(!m_root.getID().equals(_path[0].getID())){
+				throw new PathNotFoundException("wrong root node");
+			}
+			int size = _path.length;
+			Node prev = _path[0];
+			//重そう
+			for(int i = 1;i < size;i ++){
+				if(!prev.children().contains(_path[i])){
+					throw new PathNotFoundException("invalid node path");
+				}
+			}
 		}
 		
-		
-		
-		
-		
-		return null;
+		return _updateTree(_target,_newData,_path);
 	}
 	
 	/**
@@ -106,20 +120,47 @@
 	 * @throws ルートNodeから_targetまでのパスが見つからない場合、PathNotFoundException
 	 */
 	@Override
-	public synchronized Node updateTree(Node _target,NodeData _newData) throws PathNotFoundException
+	public Node updateTree(Node _target,NodeData _newData) throws PathNotFoundException
 	{
 		NodePathFinder finder = new NodePathFinder(m_root,_target);
-		OnMemoryForest forest = (OnMemoryForest)m_tree.getForest();
+		List<Node> path = finder.list();
 		
-		for(Node path : finder){
-			NodeData data = path.getData();
-			NodeID newID = path.getID().update();
-			Node clone = forest.createNode(newID,data);
-		}
-		
-		return null;
+		return _updateTree(_target,_newData,(Node[])path.toArray());
 	}
 	
+	private Node _updateTree(Node _target,NodeData _newData,Node[] _path)
+	{
+		Forest forest = m_root.getForest();
+		
+		/*
+		 * 改良する必要がある、ランダムアクセスの性能はデータ構造による。
+		 * とりあえず、配列使っとく
+		 */
+		Node ret = forest.create(_newData);
+		Node prev = ret;
+		for(int i = _path.length-1;i >= 0;i --){
+			Node prevParent = _path[i]; //prevの親
+			NodeData data = prevParent.getData();
+			
+			//子供をOrderdSetにすれば早いかもね
+			NodeData newData = new NodeData();
+			newData.putAll(data.getAll());
+			for(Node child : data.children()){
+				//UUIDに対応するノードを削除し、追加する
+				if(child.getID().isFamily(prev.getID())){
+					newData.add(prev);
+				}else{
+					newData.add(child);
+				}
+			}
+			
+			prev = forest.create(newData);
+		}
+		m_root = (OnMemoryNode)prev;
+		return ret;
+	}
+	
+	/*
 	private LinkedList<OnMemoryNode> findAndClone(OnMemoryNode _parent,OnMemoryNode _target,NodeData _newData)
 	{
 		OnMemoryForest forest = (OnMemoryForest)_target.getForest();
@@ -140,6 +181,7 @@
 		
 		return null;
 	}
+	*/
 	
 	/**
 	 * ルートNodeを取得します。
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/memory/test/OnMemoryForestTest.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,22 @@
+package treecms.memory.test;
+
+import org.junit.runner.JUnitCore;
+
+import treecms.api.Forest;
+import treecms.memory.OnMemoryForest;
+import treecms.test.AbstractForestTest;
+
+public class OnMemoryForestTest extends AbstractForestTest
+{
+	public static void main(String _args[])
+	{
+		JUnitCore.main(OnMemoryForestTest.class.getName());
+	}
+	
+	@Override
+	public Forest getInstance()
+	{
+		OnMemoryForest forest = new OnMemoryForest();
+		return forest;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/memory/test/OnMemoryNodeTest.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,29 @@
+package treecms.memory.test;
+
+import org.junit.runner.JUnitCore;
+
+import treecms.api.Node;
+import treecms.memory.OnMemoryForest;
+import treecms.test.AbstractNodeTest;
+
+public class OnMemoryNodeTest extends AbstractNodeTest
+{
+	public static void main(String _args[])
+	{
+		JUnitCore.main(OnMemoryForest.class.getName());
+	}
+	
+	private OnMemoryForest m_forest;
+	
+	public OnMemoryNodeTest()
+	{
+		m_forest = new OnMemoryForest();
+	}
+	
+	@Override
+	public Node getInstance()
+	{
+		return m_forest.create();
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/test/AbstractForestTest.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,80 @@
+package treecms.test;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import treecms.api.Forest;
+import treecms.api.Node;
+import treecms.api.NodeID;
+import treecms.api.Tree;
+
+/**
+ * Forest実装の基本的なテスト
+ * @author shoshi
+ */
+public abstract class AbstractForestTest
+{
+	/**
+	 * 基本的なテストを実装するためにはこのメソッドでインスタンスを返す。
+	 * @return Forest
+	 */
+	public abstract Forest getInstance();
+	
+	/**
+	 * Node作成テスト 
+	 * 新しく作成されたNodeがnullでなければOK
+	 */
+	@Test
+	public void testCreateNode()
+	{
+		Forest forest = getInstance();
+		Node newNode = forest.create();
+		Assert.assertNotNull(newNode);
+	}
+	
+	/**
+	 * Node取得テスト
+	 * 新しく作成されたNodeからNodeIDを取得し、ForestよりNodeを再取得する 
+	 */
+	@Test
+	public void testGetNode()
+	{
+		Forest forest = getInstance();
+		Node newNode = forest.create();
+		NodeID newID = newNode.getID();
+		
+		Node node = forest.get(newID);
+		
+		Assert.assertEquals(newNode,node);
+	}
+	
+	/**
+	 * NodeのTip(最新版)の取得テスト
+	 * 新しく作成されたNodeのUUIDを抜き出し、Forestよりtipを取得する
+	 */
+	@Test
+	public void testGetTip()
+	{
+		Forest forest = getInstance();
+		Node newNode = forest.create();
+		NodeID newID = newNode.getID();
+		
+		Node tip = forest.getTip(newID.getUUID());
+		
+		Assert.assertEquals(newNode,tip);
+	}
+	
+	/**
+	 * MainTreeが取得できるかテストします。
+	 * MainTreeとは、コンテンツ全体を含むツリーです
+	 */
+	@Test
+	public void testGetMainTree()
+	{
+		Forest forest = getInstance();
+		Tree contents = forest.getMainTree();
+		
+		Assert.assertNotNull(contents);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/test/AbstractNodeTest.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,94 @@
+package treecms.test;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import treecms.api.Node;
+import treecms.api.NodeID;
+
+/**
+ * Node実装の基本的なテスト
+ * @author shoshi
+ */
+public abstract class AbstractNodeTest
+{
+	/**
+	 * テストに用いるNodeを実装者は返す
+	 * @return Node
+	 */
+	public abstract Node getInstance();
+	
+	/**
+	 * NodeID取得のテスト
+	 */
+	@Test
+	public void testGetID()
+	{
+		Assert.assertNotNull(getInstance().getID());
+	}
+	
+	/**
+	 * Nodeのデータ(子供Nodeや属性のマップ)
+	 */
+	@Test
+	public void testGetData()
+	{
+		Assert.assertNotNull(getInstance().getData());
+	}
+	
+	/**
+	 * NodeからForestを取得するテスト
+	 */
+	@Test
+	public void testGetForest()
+	{
+		Assert.assertNotNull(getInstance().getForest());
+	}
+	
+	/**
+	 * Nodeに子供Nodeを追加するテスト
+	 */
+	@Test
+	public void testAddChildren()
+	{
+		Node node = getInstance();
+		
+		Node ch1 = node.getForest().create();
+		Node ch2 = node.getForest().create();
+		Node ch3 = node.getForest().create();
+		
+		LinkedList<Node> list = new LinkedList<Node>();
+		list.add(ch1);
+		list.add(ch2);
+		list.add(ch3);
+		
+		node.addAll(list);
+		
+		List<Node> children = node.children();
+		for(int i = 0;i < list.size();i ++){
+			NodeID id1 = children.get(i).getID();
+			NodeID id2 = list.get(i).getID();
+			
+			Assert.assertEquals(true,id1.equals(id2));
+		}
+	}
+	
+	/**
+	 * Nodeにセットした属性を取り出すテスト
+	 */
+	@Test
+	public void testSetAndGetAttribute()
+	{
+		Node node = getInstance();
+		byte[] name = "test".getBytes();
+		byte[] value = "test".getBytes();
+		
+		node.put(ByteBuffer.wrap(name),ByteBuffer.wrap(value));
+		Assert.assertEquals(true,node.get(ByteBuffer.wrap(name)).equals(ByteBuffer.wrap(value)));
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/test/AbstractTreeEditorTest.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,86 @@
+package treecms.test;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import treecms.api.Forest;
+import treecms.api.Node;
+import treecms.api.NodeData;
+import treecms.api.NodeID;
+import treecms.api.Tree;
+import treecms.api.TreeEditor;
+import treecms.tree.util.PathNotFoundException;
+
+public abstract class AbstractTreeEditorTest
+{
+	public abstract Forest getForest();
+	
+	@Test
+	public TreeEditor testEdit()
+	{
+		Forest forest = getForest();
+		Node root = forest.create();
+		
+		Node n1 = forest.create();
+		Node n11 = forest.create();
+		Node n12 = forest.create();
+		n1.addAll(Arrays.asList(n11,n12));
+		
+		Node n2 = forest.create();
+		Node n21 = forest.create();
+		Node n22 = forest.create();
+		n2.addAll(Arrays.asList(n21,n22));
+		
+		Node n221 = forest.create();
+		n22.add(n221);
+		
+		root.addAll(Arrays.asList(n1,n2));
+		
+		TreeEditor editor = forest.getTreeEditor(forest.getTree(root));
+		
+		//編集開始
+		NodeData data = n221.getData();
+		data.put(ByteBuffer.wrap("name".getBytes()),ByteBuffer.wrap("value".getBytes()));
+		
+		try{
+			Node new221 = editor.updateTree(n221,data); //n221を編集する。パスはroot -> n2 -> n22 -> n221となるはず
+			NodeID oldID = n221.getID();
+			NodeID newID = new221.getID();
+			
+			//編集したノードは同じUUIDを持つはず
+			Assert.assertTrue(oldID.isFamily(newID));
+			
+			Node newRoot = 
+			NodePathFinder finder = new NodePathFinder()
+			
+		}catch(PathNotFoundException _e){
+			Assert.fail(_e.getMessage());
+		}
+		
+		return editor;
+	}
+	
+	@Test
+	public void testForceCommit()
+	{
+		testEdit();
+	}
+	
+	@Test
+	public void testCommitFailsWhenTreeWasUpdated()
+	{
+	}
+	
+	@Test
+	public void testCheck()
+	{
+	}
+	
+	@Test
+	public void testUpdate()
+	{
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/test/AbstractTreeTest.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,83 @@
+package treecms.test;
+
+
+import junit.framework.Assert;
+import org.junit.Test;
+import treecms.api.Tree;
+
+/**
+ * Tree実装の基本的なテスト
+ * @author shoshi
+ */
+public abstract class AbstractTreeTest extends AbstractNodeTest
+{
+	/**
+	 * インスタンスください
+	 * @return Tree
+	 */
+	public abstract Tree getInstance();
+	
+	/**
+	 * 木のRootNodeが取得できるか確認する 
+	 */
+	@Test
+	public void testGetRoot()
+	{
+		Tree tree = getInstance();
+		Assert.assertNotNull(tree.getRoot());
+	}
+	
+	/*
+	@Test
+	public void testUpdateTree()
+	{
+		Node ch1 = m_tree.getForest().create();
+		Node ch2 = m_tree.getForest().create();
+		
+		Node ch11 = m_tree.getForest().create();
+		Node ch12 = m_tree.getForest().create();
+		
+		LinkedList<Node> list = new LinkedList<Node>();
+		list.add(ch1);
+		list.add(ch2);
+		
+		m_tree.getRoot().getData().add(list);
+		
+		LinkedList<Node> ch1list = new LinkedList<Node>();
+		ch1list.add(ch11);
+		ch1.getData().add(ch1list);
+		
+		LinkedList<Node> ch2list = new LinkedList<Node>();
+		ch2list.add(ch12);
+		ch2.getData().add(ch2list);
+		
+		LinkedList<Node> before = findPath(m_tree.getRoot(),ch11);
+		NodeData newData = ch11.getData().deepCopy();
+		m_tree.updateTree(ch11,newData);
+		LinkedList<Node> after = findPath(m_tree.getRoot(),ch11);
+		
+		for(int i = 0;i < before.size();i ++){
+			boolean result = before.get(i).getID().isFamily(after.get(i).getID());
+			Assert.assertEquals(true,result);
+		}
+	}
+	
+	public LinkedList<Node> findPath(Node _from,Node _target)
+	{
+		if(_from.getID().isFamily(_target.getID())){
+			LinkedList<Node> path = new LinkedList<Node>();
+			path.add(_from);
+			return path;
+		}
+		
+		for(Node child : _from.children()){
+			LinkedList<Node> path = findPath(child,_target);
+			if(path != null){
+				path.add(_from);
+			}
+		}
+		
+		return null;
+	}
+	*/
+}
--- a/src/treecms/test/ForestTest.java	Thu Mar 31 02:08:44 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-package treecms.test;
-
-public class ForestTest {
-
-}
--- a/src/treecms/test/NodeTest.java	Thu Mar 31 02:08:44 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-package treecms.test;
-
-import java.util.LinkedList;
-import java.util.List;
-
-import junit.framework.Assert;
-
-import org.junit.Test;
-
-import treecms.api.Node;
-import treecms.api.NodeID;
-
-public class NodeTest
-{
-	Node m_node;
-	
-	public NodeTest(Node _node)
-	{
-		m_node = _node;
-	}
-	
-	@Test
-	public void testGetID()
-	{
-		Assert.assertNotNull(m_node.getID());
-	}
-	
-	@Test
-	public void testGetData()
-	{
-		Assert.assertNotNull(m_node.getData());
-	}
-	
-	@Test
-	public void testGetForest()
-	{
-		Assert.assertNotNull(m_node.getForest());
-	}
-	
-	@Test
-	public void testAddChildren()
-	{
-		Node ch1 = m_node.getForest().create();
-		Node ch2 = m_node.getForest().create();
-		Node ch3 = m_node.getForest().create();
-		
-		LinkedList<Node> list = new LinkedList<Node>();
-		list.add(ch1);
-		list.add(ch2);
-		list.add(ch3);
-		
-		m_node.getData().add(list);
-		
-		List<Node> children = m_node.getData().list();
-		for(int i = 0;i < list.size();i ++){
-			NodeID id1 = children.get(i).getID();
-			NodeID id2 = list.get(i).getID();
-			
-			Assert.assertEquals(true,id1.equals(id2));
-		}
-	}
-	
-	@Test
-	public void testSetAndGetAttribute()
-	{
-		byte[] name = "test".getBytes();
-		byte[] value = "test".getBytes();
-		
-		m_node.getData().set(name,value);
-		Assert.assertEquals(true,m_node.getData().get(name).equals(value));
-	}
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/test/OrderedSetTest.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,18 @@
+package treecms.test;
+
+import java.util.LinkedHashSet;
+
+public class OrderedSetTest
+{
+	public static void main(String _args[])
+	{
+		LinkedHashSet<Integer> set = new LinkedHashSet<Integer>();
+		set.add(1);
+		set.add(2);
+		set.add(3);
+		
+		for(Integer i : set){
+			System.out.println(i);
+		}
+	}
+}
--- a/src/treecms/test/TreeEditorTest.java	Thu Mar 31 02:08:44 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-package treecms.test;
-
-import java.util.List;
-import java.util.LinkedList;
-
-import junit.framework.Assert;
-
-import org.junit.Before;
-import org.junit.Test;
-import treecms.api.Forest;
-import treecms.api.Node;
-import treecms.api.NodeID;
-import treecms.api.Tree;
-import treecms.api.TreeEditor;
-import treecms.tree.util.NodePathFinder;
-
-public class TreeEditorTest
-{
-	Tree m_tree;
-	TreeEditor m_editor;
-	
-	Node root,ch1,ch2,ch11,ch21,ch22,ch223,cloned;
-	
-	public TreeEditorTest(TreeEditor _editor,Tree _tree)
-	{
-		m_editor = _editor;
-		m_tree = _tree;
-		
-		Forest forest = m_editor.getForest();
-		root = m_editor.getRoot();
-		
-		ch1 = forest.create();
-		ch2 = forest.create();
-		ch11 = forest.create();
-		ch21 = forest.create();
-		ch22 = forest.create();
-		ch223 = forest.create(); //target
-		
-		LinkedList<Node> rootChildren = new LinkedList<Node>();
-		rootChildren.add(ch1);
-		rootChildren.add(ch2);
-		root.getData().add(rootChildren);
-		
-		ch1.getData().add(ch11);
-		
-		LinkedList<Node> ch2Children = new LinkedList<Node>();
-		ch2Children.add(ch21);
-		ch2Children.add(ch22);
-		ch2.getData().add(ch2Children);
-		
-		ch22.getData().add(ch223);
-		
-		//Edit
-		cloned = m_editor.updateTree(ch223,ch223.getData());
-	}
-	
-	@Test
-	public void testEdit()
-	{
-		NodePathFinder oldPath = new NodePathFinder(root,ch223);
-		NodePathFinder newPath = new NodePathFinder(m_editor.getRoot(),cloned);
-		List<Node> oldPathList = oldPath.list();
-		List<Node> newPathList = newPath.list();
-		
-		//compare
-		for(int i = 0;i < oldPathList.size();i ++){
-			Node node1 = oldPathList.get(i);
-			Node node2 = newPathList.get(i);
-			
-			Assert.assertEquals(true,node1.getID().isFamily(node2.getID()));
-		}
-	}
-	
-	@Test
-	public void testForceCommit()
-	{
-		m_editor.commit(true);
-		NodeID rootID1 = m_editor.getRoot().getID();
-		NodeID rootID2 = m_tree.getRoot().getID();
-		
-		Assert.assertEquals(true,rootID1.equals(rootID2));
-	}
-	
-	@Test
-	public void testCommitFailsWhenTreeWasUpdated()
-	{
-		
-	}
-	
-	@Test
-	public void testCheck()
-	{
-		
-	}
-	
-	@Test
-	public void testUpdate()
-	{
-		
-	}
-}
--- a/src/treecms/test/TreeTest.java	Thu Mar 31 02:08:44 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-package treecms.test;
-
-import java.util.LinkedList;
-
-import junit.framework.Assert;
-
-import org.junit.Test;
-
-import treecms.api.Node;
-import treecms.api.NodeData;
-import treecms.api.Tree;
-
-public class TreeTest
-{
-	Tree m_tree;
-	
-	public TreeTest(Tree _tree)
-	{
-		m_tree = _tree;
-	}
-	
-	@Test
-	public void testUpdateTree()
-	{
-		Node ch1 = m_tree.getForest().create();
-		Node ch2 = m_tree.getForest().create();
-		
-		Node ch11 = m_tree.getForest().create();
-		Node ch12 = m_tree.getForest().create();
-		
-		LinkedList<Node> list = new LinkedList<Node>();
-		list.add(ch1);
-		list.add(ch2);
-		
-		m_tree.getRoot().getData().add(list);
-		
-		LinkedList<Node> ch1list = new LinkedList<Node>();
-		ch1list.add(ch11);
-		ch1.getData().add(ch1list);
-		
-		LinkedList<Node> ch2list = new LinkedList<Node>();
-		ch2list.add(ch12);
-		ch2.getData().add(ch2list);
-		
-		LinkedList<Node> before = findPath(m_tree.getRoot(),ch11);
-		NodeData newData = ch11.getData().deepCopy();
-		m_tree.updateTree(ch11,newData);
-		LinkedList<Node> after = findPath(m_tree.getRoot(),ch11);
-		
-		for(int i = 0;i < before.size();i ++){
-			boolean result = before.get(i).getID().isFamily(after.get(i).getID());
-			Assert.assertEquals(true,result);
-		}
-	}
-	
-	public LinkedList<Node> findPath(Node _from,Node _target)
-	{
-		if(_from.getID().isFamily(_target.getID())){
-			LinkedList<Node> path = new LinkedList<Node>();
-			path.add(_from);
-			return path;
-		}
-		
-		for(Node child : _from.getData().list()){
-			LinkedList<Node> path = findPath(child,_target);
-			if(path != null){
-				path.add(_from);
-			}
-		}
-		
-		return null;
-	}
-}
--- a/src/treecms/tree/cassandra/v1/CassandraForest.java	Thu Mar 31 02:08:44 2011 +0900
+++ b/src/treecms/tree/cassandra/v1/CassandraForest.java	Mon Apr 18 01:07:27 2011 +0900
@@ -35,6 +35,7 @@
 import treecms.api.Node;
 import treecms.api.NodeData;
 import treecms.api.NodeID;
+import treecms.tree.cassandra.v1.util.CassandraClientThreadFactory;
 import treecms.tree.id.AbstractRandomNodeID;
 
 /**
@@ -78,7 +79,7 @@
 	
 	public CassandraForest(String _host,int _port,String _ks,int _threads)
 	{
-		m_service = Executors.newFixedThreadPool(_threads,new ClientThreadFactory(_host,_port));
+		m_service = Executors.newFixedThreadPool(_threads,new CassandraClientThreadFactory(_host,_port));
 		
 		m_cache = new ConcurrentHashMap<NodeID,CassandraNode>();
 		m_tipCache = new ConcurrentHashMap<String,CassandraNode>();
--- a/src/treecms/tree/cassandra/v1/ClientThread.java	Thu Mar 31 02:08:44 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-package treecms.tree.cassandra.v1;
-
-import org.apache.thrift.transport.TTransportException;
-
-/**
- * CassandraのClientを保持したスレッドオブジェクトです。
- * @author shoshi
- */
-final class ClientThread extends Thread
-{
-	private ClientWrapper m_wrapper;
-	
-	/**
-	 * コンストラクタです。
-	 * @param _host Cassandraのホスト名
-	 * @param _port Cassandraのポート番号
-	 * @param _runnable このスレッドで動作するRunnable
-	 * @throws TTransportException 
-	 */
-	private ClientThread(String _host,int _port,Runnable _runnable) throws TTransportException
-	{
-		super(_runnable);
-		m_wrapper = new ClientWrapper(_host,_port,2);
-	}
-	
-	/**
-	 * ファクトリメソッドです。
-	 * @param _host Cassandraのホスト名
-	 * @param _port Cassandraのポート番号
-	 * @param _runnable このスレッドで動作するRunnable
-	 * @return 新しいインスタンス
-	 * @throws TTransportException Cassandraへの接続が失敗したとき
-	 */
-	public static ClientThread newInstance(String _host,int _port,Runnable _runnable) throws TTransportException
-	{
-		ClientThread thread = new ClientThread(_host,_port,_runnable);
-		return thread;
-	}
-	
-	/**
-	 * ClientWrapperを取得します
-	 * @return CassandraへのClientWrapper
-	 */
-	public ClientWrapper getClientWrapper()
-	{
-		return m_wrapper;
-	}
-}
--- a/src/treecms/tree/cassandra/v1/ClientThreadFactory.java	Thu Mar 31 02:08:44 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-package treecms.tree.cassandra.v1;
-
-import java.util.concurrent.ThreadFactory;
-
-import org.apache.thrift.transport.TTransportException;
-
-/**
- * ローカル変数としてCassandra.Clientを保持するスレッドオブジェクトを生成するスレッドファクトリーです。
- * @author shoshi
- */
-final class ClientThreadFactory implements ThreadFactory
-{
-	private String m_host;
-	private int m_port;
-	
-	/**
-	 * コンストラクタです。
-	 * @param _host Cassandraのアドレス・ホスト名
-	 * @param _port Thriftポート番号
-	 */
-	public ClientThreadFactory(String _host,int _port)
-	{
-		m_host = _host;
-		m_port = _port;
-	}
-
-	/**
-	 * Cassandra.Clientを保持するスレッドオブジェクトを新しく作成します。
-	 */
-	@Override
-	public Thread newThread(Runnable _runnable)
-	{
-		ClientThread client = null;
-		try{
-			client = ClientThread.newInstance(m_host,m_port,_runnable);
-		}catch(TTransportException _e) {
-			_e.printStackTrace();
-			throw new RuntimeException(_e);
-		}
-		return client;
-	}
-}
--- a/src/treecms/tree/cassandra/v1/ClientWrapper.java	Thu Mar 31 02:08:44 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,203 +0,0 @@
-package treecms.tree.cassandra.v1;
-
-import java.util.List;
-import java.util.Map;
-import org.apache.cassandra.thrift.Cassandra;
-import org.apache.cassandra.thrift.ColumnOrSuperColumn;
-import org.apache.cassandra.thrift.ColumnParent;
-import org.apache.cassandra.thrift.ColumnPath;
-import org.apache.cassandra.thrift.ConsistencyLevel;
-import org.apache.cassandra.thrift.InvalidRequestException;
-import org.apache.cassandra.thrift.KeyRange;
-import org.apache.cassandra.thrift.KeySlice;
-import org.apache.cassandra.thrift.Mutation;
-import org.apache.cassandra.thrift.NotFoundException;
-import org.apache.cassandra.thrift.SlicePredicate;
-import org.apache.cassandra.thrift.TimedOutException;
-import org.apache.cassandra.thrift.UnavailableException;
-import org.apache.thrift.TException;
-import org.apache.thrift.protocol.TBinaryProtocol;
-import org.apache.thrift.transport.TSocket;
-import org.apache.thrift.transport.TTransport;
-import org.apache.thrift.transport.TTransportException;
-
-/**
- * Cassandra.Clientのラッパークラスです。Cassandra.Clientを使いやすくするための機能を提供します。
- * 接続済みのCassandra.Clientの接続が切断されたときに再接続を行います。このクラスはスレッドセーフでは有りません。
- * 
- * このラッパークラスはCassandra 0.6.xのAPIをベースに作成します。
- * @author shoshi
- */
-final class ClientWrapper
-{
-	private String m_host;
-	private int m_port;
-	private int m_retryCount;
-	
-	private TTransport m_tr;
-	private Cassandra.Client m_client;
-	
-	/**
-	 * コンストラクタです。初期化して接続します。
-	 * @param _host Cassandraのホスト名
-	 * @param _port Cassandraのポート番号
-	 * @param _retryCount リクエストが失敗した場合リトライする回数
-	 * @throws TTransportException
-	 */
-	public ClientWrapper(String _host,int _port,int _retryCount) throws TTransportException
-	{
-		m_host = _host;
-		m_port = _port;
-		m_retryCount = _retryCount;
-		
-		connect();
-	}
-	
-	/**
-	 * Cassandraに接続します。
-	 * @throws TTransportException
-	 */
-	private void connect() throws TTransportException
-	{
-		Cassandra.Client client;
-		TTransport tr = new TSocket(m_host,m_port);
-		client = new Cassandra.Client(new TBinaryProtocol(tr));	
-		
-		tr.open();
-		
-		m_tr = tr;
-		m_client = client;
-	}
-	
-	/**
-	 * Cassandraへの接続を切ります。
-	 */
-	private void disconnect()
-	{
-		m_tr.close();
-		m_client = null;
-	}
-	
-	/**
-	 * Cassandraへ再接続を行います。
-	 * @return 再接続が成功した場合true
-	 */
-	private boolean reconnect()
-	{
-		disconnect();
-		try {
-			connect();
-		}catch(TTransportException _e){
-			_e.printStackTrace();
-		}
-		return true;
-	}
-	
-	/**
-	 * ここで共通する例外処理(再接続など)を行う
-	 * @param _e 例外
-	 */
-	private void exceptionHandler(Exception _e)
-	{
-		_e.printStackTrace();
-	}
-	
-	/**
-	 * Cassandra.Client.getのラッパーメソッド
-	 */
-	public ColumnOrSuperColumn get(String _ks,String _key,ColumnPath _path,ConsistencyLevel _level) throws InvalidRequestException, NotFoundException, UnavailableException, TimedOutException, TException
-	{
-		for(int i = 0;i < m_retryCount;i ++){
-			if(m_client == null){
-				throw new IllegalStateException("Cassandra.Client is disconnected. "+m_host+":"+m_port);
-			}
-			
-			try {
-				ColumnOrSuperColumn cors = m_client.get(_ks,_key,_path,_level);
-				return cors;
-			}catch(Exception _e){
-				exceptionHandler(_e);
-			}
-		}
-		return m_client.get(_ks,_key,_path,_level);
-	}
-	
-	/**
-	 * Cassandra.Client.get_sliceのラッパーメソッド
-	 */
-	public List<ColumnOrSuperColumn> get_slice(String _ks,String _key,ColumnParent _parent,SlicePredicate _predicate,ConsistencyLevel _level) throws InvalidRequestException, UnavailableException, TimedOutException, TException
-	{
-		for(int i = 0;i < m_retryCount;i ++){
-			if(m_client == null){
-				throw new IllegalStateException("Cassandra.Client is disconnected. "+m_host+":"+m_port);
-			}
-			
-			try {
-				List<ColumnOrSuperColumn> list = m_client.get_slice(_ks,_key,_parent,_predicate,_level);
-				return list;
-			}catch(Exception _e){
-				exceptionHandler(_e);
-			}
-		}
-		return m_client.get_slice(_ks,_key,_parent,_predicate,_level);
-	}
-	
-	/**
-	 * Cassandra.Client.get_range_slicesのラッパーメソッド
-	 */
-	public List<KeySlice> get_range_slices(String _ks,ColumnParent _parent,SlicePredicate _predicate,KeyRange _range,ConsistencyLevel _level) throws InvalidRequestException, UnavailableException, TimedOutException, TException
-	{
-		for(int i = 0;i < m_retryCount;i ++){
-			if(m_client == null){
-				throw new IllegalStateException("Cassandra.Client is disconnected. "+m_host+":"+m_port);
-			}
-			
-			try {
-				return m_client.get_range_slices(_ks,_parent,_predicate,_range,_level);
-			}catch(Exception _e){
-				exceptionHandler(_e);
-			}
-		}
-		return m_client.get_range_slices(_ks,_parent,_predicate,_range,_level);
-	}
-	
-	/**
-	 * Cassandra.Client.insertのラッパーメソッド
-	 */
-	public void insert(String _ks,String _key,ColumnPath _path,byte[] _value,long _timestamp,ConsistencyLevel _level) throws InvalidRequestException, UnavailableException, TimedOutException, TException
-	{
-		for(int i = 1;i < m_retryCount;i ++){
-			if(m_client == null){
-				throw new IllegalStateException("Cassandra.Client is disconnected. "+m_host+":"+m_port);
-			}
-			
-			try{
-				m_client.insert(_ks,_key,_path,_value,_timestamp,_level);
-			}catch(Exception _e){
-				exceptionHandler(_e);
-			}
-		}
-		m_client.insert(_ks,_key,_path,_value,_timestamp,_level);
-		return;
-	}
-	
-	/**
-	 * Cassandra.Client.batch_mutateのラッパーメソッド
-	 */
-	public void batch_mutate(String _ks,Map<String,Map<String,List<Mutation>>> _mutation_map,ConsistencyLevel _level) throws InvalidRequestException, UnavailableException, TimedOutException, TException
-	{
-		for(int i = 1;i < m_retryCount;i ++){
-			if(m_client == null){
-				throw new IllegalStateException("Cassandra.Client is disconnected. "+m_host+":"+m_port);
-			}
-			
-			try{
-				m_client.batch_mutate(_ks,_mutation_map,_level);
-			}catch(Exception _e){
-				exceptionHandler(_e);
-			}
-		}
-		m_client.batch_mutate(_ks,_mutation_map,_level);
-		return;
-	}
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/tree/cassandra/v1/util/CassandraClientThread.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,48 @@
+package treecms.tree.cassandra.v1.util;
+
+import org.apache.thrift.transport.TTransportException;
+
+/**
+ * CassandraのClientを保持したスレッドオブジェクトです。
+ * @author shoshi
+ */
+final class CassandraClientThread extends Thread
+{
+	private CassandraClientWrapper m_wrapper;
+	
+	/**
+	 * コンストラクタです。
+	 * @param _host Cassandraのホスト名
+	 * @param _port Cassandraのポート番号
+	 * @param _runnable このスレッドで動作するRunnable
+	 * @throws TTransportException 
+	 */
+	private CassandraClientThread(String _host,int _port,Runnable _runnable) throws TTransportException
+	{
+		super(_runnable);
+		m_wrapper = new CassandraClientWrapper(_host,_port,2);
+	}
+	
+	/**
+	 * ファクトリメソッドです。
+	 * @param _host Cassandraのホスト名
+	 * @param _port Cassandraのポート番号
+	 * @param _runnable このスレッドで動作するRunnable
+	 * @return 新しいインスタンス
+	 * @throws TTransportException Cassandraへの接続が失敗したとき
+	 */
+	public static CassandraClientThread newInstance(String _host,int _port,Runnable _runnable) throws TTransportException
+	{
+		CassandraClientThread thread = new CassandraClientThread(_host,_port,_runnable);
+		return thread;
+	}
+	
+	/**
+	 * ClientWrapperを取得します
+	 * @return CassandraへのClientWrapper
+	 */
+	public CassandraClientWrapper getClientWrapper()
+	{
+		return m_wrapper;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/tree/cassandra/v1/util/CassandraClientThreadFactory.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,42 @@
+package treecms.tree.cassandra.v1.util;
+
+import java.util.concurrent.ThreadFactory;
+
+import org.apache.thrift.transport.TTransportException;
+
+/**
+ * ローカル変数としてCassandra.Clientを保持するスレッドオブジェクトを生成するスレッドファクトリーです。
+ * @author shoshi
+ */
+final class CassandraClientThreadFactory implements ThreadFactory
+{
+	private String m_host;
+	private int m_port;
+	
+	/**
+	 * コンストラクタです。
+	 * @param _host Cassandraのアドレス・ホスト名
+	 * @param _port Thriftポート番号
+	 */
+	public CassandraClientThreadFactory(String _host,int _port)
+	{
+		m_host = _host;
+		m_port = _port;
+	}
+
+	/**
+	 * Cassandra.Clientを保持するスレッドオブジェクトを新しく作成します。
+	 */
+	@Override
+	public Thread newThread(Runnable _runnable)
+	{
+		CassandraClientThread client = null;
+		try{
+			client = CassandraClientThread.newInstance(m_host,m_port,_runnable);
+		}catch(TTransportException _e) {
+			_e.printStackTrace();
+			throw new RuntimeException(_e);
+		}
+		return client;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/tree/cassandra/v1/util/CassandraClientWrapper.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,203 @@
+package treecms.tree.cassandra.v1.util;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.cassandra.thrift.Cassandra;
+import org.apache.cassandra.thrift.ColumnOrSuperColumn;
+import org.apache.cassandra.thrift.ColumnParent;
+import org.apache.cassandra.thrift.ColumnPath;
+import org.apache.cassandra.thrift.ConsistencyLevel;
+import org.apache.cassandra.thrift.InvalidRequestException;
+import org.apache.cassandra.thrift.KeyRange;
+import org.apache.cassandra.thrift.KeySlice;
+import org.apache.cassandra.thrift.Mutation;
+import org.apache.cassandra.thrift.NotFoundException;
+import org.apache.cassandra.thrift.SlicePredicate;
+import org.apache.cassandra.thrift.TimedOutException;
+import org.apache.cassandra.thrift.UnavailableException;
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.transport.TSocket;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportException;
+
+/**
+ * Cassandra.Clientのラッパークラスです。Cassandra.Clientを使いやすくするための機能を提供します。
+ * 接続済みのCassandra.Clientの接続が切断されたときに再接続を行います。このクラスはスレッドセーフでは有りません。
+ * 
+ * このラッパークラスはCassandra 0.6.xのAPIをベースに作成します。
+ * @author shoshi
+ */
+final class CassandraClientWrapper
+{
+	private String m_host;
+	private int m_port;
+	private int m_retryCount;
+	
+	private TTransport m_tr;
+	private Cassandra.Client m_client;
+	
+	/**
+	 * コンストラクタです。初期化して接続します。
+	 * @param _host Cassandraのホスト名
+	 * @param _port Cassandraのポート番号
+	 * @param _retryCount リクエストが失敗した場合リトライする回数
+	 * @throws TTransportException
+	 */
+	public CassandraClientWrapper(String _host,int _port,int _retryCount) throws TTransportException
+	{
+		m_host = _host;
+		m_port = _port;
+		m_retryCount = _retryCount;
+		
+		connect();
+	}
+	
+	/**
+	 * Cassandraに接続します。
+	 * @throws TTransportException
+	 */
+	private void connect() throws TTransportException
+	{
+		Cassandra.Client client;
+		TTransport tr = new TSocket(m_host,m_port);
+		client = new Cassandra.Client(new TBinaryProtocol(tr));	
+		
+		tr.open();
+		
+		m_tr = tr;
+		m_client = client;
+	}
+	
+	/**
+	 * Cassandraへの接続を切ります。
+	 */
+	private void disconnect()
+	{
+		m_tr.close();
+		m_client = null;
+	}
+	
+	/**
+	 * Cassandraへ再接続を行います。
+	 * @return 再接続が成功した場合true
+	 */
+	private boolean reconnect()
+	{
+		disconnect();
+		try {
+			connect();
+		}catch(TTransportException _e){
+			_e.printStackTrace();
+		}
+		return true;
+	}
+	
+	/**
+	 * ここで共通する例外処理(再接続など)を行う
+	 * @param _e 例外
+	 */
+	private void exceptionHandler(Exception _e)
+	{
+		_e.printStackTrace();
+	}
+	
+	/**
+	 * Cassandra.Client.getのラッパーメソッド
+	 */
+	public ColumnOrSuperColumn get(String _ks,String _key,ColumnPath _path,ConsistencyLevel _level) throws InvalidRequestException, NotFoundException, UnavailableException, TimedOutException, TException
+	{
+		for(int i = 0;i < m_retryCount;i ++){
+			if(m_client == null){
+				throw new IllegalStateException("Cassandra.Client is disconnected. "+m_host+":"+m_port);
+			}
+			
+			try {
+				ColumnOrSuperColumn cors = m_client.get(_ks,_key,_path,_level);
+				return cors;
+			}catch(Exception _e){
+				exceptionHandler(_e);
+			}
+		}
+		return m_client.get(_ks,_key,_path,_level);
+	}
+	
+	/**
+	 * Cassandra.Client.get_sliceのラッパーメソッド
+	 */
+	public List<ColumnOrSuperColumn> get_slice(String _ks,String _key,ColumnParent _parent,SlicePredicate _predicate,ConsistencyLevel _level) throws InvalidRequestException, UnavailableException, TimedOutException, TException
+	{
+		for(int i = 0;i < m_retryCount;i ++){
+			if(m_client == null){
+				throw new IllegalStateException("Cassandra.Client is disconnected. "+m_host+":"+m_port);
+			}
+			
+			try {
+				List<ColumnOrSuperColumn> list = m_client.get_slice(_ks,_key,_parent,_predicate,_level);
+				return list;
+			}catch(Exception _e){
+				exceptionHandler(_e);
+			}
+		}
+		return m_client.get_slice(_ks,_key,_parent,_predicate,_level);
+	}
+	
+	/**
+	 * Cassandra.Client.get_range_slicesのラッパーメソッド
+	 */
+	public List<KeySlice> get_range_slices(String _ks,ColumnParent _parent,SlicePredicate _predicate,KeyRange _range,ConsistencyLevel _level) throws InvalidRequestException, UnavailableException, TimedOutException, TException
+	{
+		for(int i = 0;i < m_retryCount;i ++){
+			if(m_client == null){
+				throw new IllegalStateException("Cassandra.Client is disconnected. "+m_host+":"+m_port);
+			}
+			
+			try {
+				return m_client.get_range_slices(_ks,_parent,_predicate,_range,_level);
+			}catch(Exception _e){
+				exceptionHandler(_e);
+			}
+		}
+		return m_client.get_range_slices(_ks,_parent,_predicate,_range,_level);
+	}
+	
+	/**
+	 * Cassandra.Client.insertのラッパーメソッド
+	 */
+	public void insert(String _ks,String _key,ColumnPath _path,byte[] _value,long _timestamp,ConsistencyLevel _level) throws InvalidRequestException, UnavailableException, TimedOutException, TException
+	{
+		for(int i = 1;i < m_retryCount;i ++){
+			if(m_client == null){
+				throw new IllegalStateException("Cassandra.Client is disconnected. "+m_host+":"+m_port);
+			}
+			
+			try{
+				m_client.insert(_ks,_key,_path,_value,_timestamp,_level);
+			}catch(Exception _e){
+				exceptionHandler(_e);
+			}
+		}
+		m_client.insert(_ks,_key,_path,_value,_timestamp,_level);
+		return;
+	}
+	
+	/**
+	 * Cassandra.Client.batch_mutateのラッパーメソッド
+	 */
+	public void batch_mutate(String _ks,Map<String,Map<String,List<Mutation>>> _mutation_map,ConsistencyLevel _level) throws InvalidRequestException, UnavailableException, TimedOutException, TException
+	{
+		for(int i = 1;i < m_retryCount;i ++){
+			if(m_client == null){
+				throw new IllegalStateException("Cassandra.Client is disconnected. "+m_host+":"+m_port);
+			}
+			
+			try{
+				m_client.batch_mutate(_ks,_mutation_map,_level);
+			}catch(Exception _e){
+				exceptionHandler(_e);
+			}
+		}
+		m_client.batch_mutate(_ks,_mutation_map,_level);
+		return;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/tree/cassandra/v1/util/ConcurrentCassandraClient.java	Mon Apr 18 01:07:27 2011 +0900
@@ -0,0 +1,46 @@
+package treecms.tree.cassandra.v1.util;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.apache.cassandra.thrift.ColumnOrSuperColumn;
+import org.apache.cassandra.thrift.ColumnPath;
+import org.apache.cassandra.thrift.ConsistencyLevel;
+
+/**
+ * 
+ * @author shoshi
+ */
+public class ConcurrentCassandraClient
+{
+	private ExecutorService m_service;
+	
+	private ConcurrentCassandraClient(String _host,int _port,int _thCount)
+	{
+		m_service = Executors.newFixedThreadPool(_thCount,new CassandraClientThreadFactory(_host,_port));
+	}
+
+	public static ConcurrentCassandraClient newInstance(String _host,int _port,int _thCount)
+	{
+		ConcurrentCassandraClient client = new ConcurrentCassandraClient(_host,_port,_thCount);
+		return client;
+	}
+	
+	public Future<ColumnOrSuperColumn> get(final String _ks,final String _key,final ColumnPath _path,final ConsistencyLevel _level)
+	{
+		Callable<ColumnOrSuperColumn> task = new Callable<ColumnOrSuperColumn>(){
+			@Override
+			public ColumnOrSuperColumn call() throws Exception
+			{
+				CassandraClientWrapper client = ((CassandraClientThread)Thread.currentThread()).getClientWrapper();
+				ColumnOrSuperColumn cors = client.get(_ks,_key,_path,_level);
+				return cors;
+			}
+		};
+		
+		Future<ColumnOrSuperColumn> ret = m_service.submit(task);
+		return ret;	
+	}
+}