# HG changeset patch # User shoshi # Date 1303056447 -32400 # Node ID 17ed97ca9960ea8c6282b56b76ca5ecaa2155969 # Parent f96193babac0c63b0035d3ebb42958b323774aad commit diff -r f96193babac0 -r 17ed97ca9960 src/treecms/api/Forest.java --- 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 diff -r f96193babac0 -r 17ed97ca9960 src/treecms/api/MonotonicTree.java --- /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; +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/api/MonotonicTreeNode.java --- /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(); +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/api/SingleNode.java --- /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 +{ +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/api/TreeEditor.java --- 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; -} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/api/TreeNode.java --- /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(); +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/gui/GUIEditor.java --- 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); } } diff -r f96193babac0 -r 17ed97ca9960 src/treecms/gui/NodeViewerTree.java --- 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) { diff -r f96193babac0 -r 17ed97ca9960 src/treecms/memory/OnMemoryForest.java --- 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); } /** diff -r f96193babac0 -r 17ed97ca9960 src/treecms/memory/OnMemoryTree.java --- 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 m_ref; + private static final AtomicReferenceFieldUpdater 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(_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 _children) { - m_ref.get().addAll(_children); + m_root.addAll(_children); } /** @@ -96,7 +98,7 @@ @Override public List 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 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 _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; } diff -r f96193babac0 -r 17ed97ca9960 src/treecms/memory/OnMemoryTreeEditor.java --- 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 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 findAndClone(OnMemoryNode _parent,OnMemoryNode _target,NodeData _newData) { OnMemoryForest forest = (OnMemoryForest)_target.getForest(); @@ -140,6 +181,7 @@ return null; } + */ /** * ルートNodeを取得します。 diff -r f96193babac0 -r 17ed97ca9960 src/treecms/memory/test/OnMemoryForestTest.java --- /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; + } +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/memory/test/OnMemoryNodeTest.java --- /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(); + } + +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/test/AbstractForestTest.java --- /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); + } +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/test/AbstractNodeTest.java --- /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 list = new LinkedList(); + list.add(ch1); + list.add(ch2); + list.add(ch3); + + node.addAll(list); + + List 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))); + } +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/test/AbstractTreeEditorTest.java --- /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() + { + } +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/test/AbstractTreeTest.java --- /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 list = new LinkedList(); + list.add(ch1); + list.add(ch2); + + m_tree.getRoot().getData().add(list); + + LinkedList ch1list = new LinkedList(); + ch1list.add(ch11); + ch1.getData().add(ch1list); + + LinkedList ch2list = new LinkedList(); + ch2list.add(ch12); + ch2.getData().add(ch2list); + + LinkedList before = findPath(m_tree.getRoot(),ch11); + NodeData newData = ch11.getData().deepCopy(); + m_tree.updateTree(ch11,newData); + LinkedList 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 findPath(Node _from,Node _target) + { + if(_from.getID().isFamily(_target.getID())){ + LinkedList path = new LinkedList(); + path.add(_from); + return path; + } + + for(Node child : _from.children()){ + LinkedList path = findPath(child,_target); + if(path != null){ + path.add(_from); + } + } + + return null; + } + */ +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/test/ForestTest.java --- 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 { - -} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/test/NodeTest.java --- 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 list = new LinkedList(); - list.add(ch1); - list.add(ch2); - list.add(ch3); - - m_node.getData().add(list); - - List 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)); - } -} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/test/OrderedSetTest.java --- /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 set = new LinkedHashSet(); + set.add(1); + set.add(2); + set.add(3); + + for(Integer i : set){ + System.out.println(i); + } + } +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/test/TreeEditorTest.java --- 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 rootChildren = new LinkedList(); - rootChildren.add(ch1); - rootChildren.add(ch2); - root.getData().add(rootChildren); - - ch1.getData().add(ch11); - - LinkedList ch2Children = new LinkedList(); - 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 oldPathList = oldPath.list(); - List 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() - { - - } -} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/test/TreeTest.java --- 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 list = new LinkedList(); - list.add(ch1); - list.add(ch2); - - m_tree.getRoot().getData().add(list); - - LinkedList ch1list = new LinkedList(); - ch1list.add(ch11); - ch1.getData().add(ch1list); - - LinkedList ch2list = new LinkedList(); - ch2list.add(ch12); - ch2.getData().add(ch2list); - - LinkedList before = findPath(m_tree.getRoot(),ch11); - NodeData newData = ch11.getData().deepCopy(); - m_tree.updateTree(ch11,newData); - LinkedList 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 findPath(Node _from,Node _target) - { - if(_from.getID().isFamily(_target.getID())){ - LinkedList path = new LinkedList(); - path.add(_from); - return path; - } - - for(Node child : _from.getData().list()){ - LinkedList path = findPath(child,_target); - if(path != null){ - path.add(_from); - } - } - - return null; - } -} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/tree/cassandra/v1/CassandraForest.java --- 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(); m_tipCache = new ConcurrentHashMap(); diff -r f96193babac0 -r 17ed97ca9960 src/treecms/tree/cassandra/v1/ClientThread.java --- 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; - } -} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/tree/cassandra/v1/ClientThreadFactory.java --- 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; - } -} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/tree/cassandra/v1/ClientWrapper.java --- 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 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 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 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>> _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; - } -} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/tree/cassandra/v1/util/CassandraClientThread.java --- /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; + } +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/tree/cassandra/v1/util/CassandraClientThreadFactory.java --- /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; + } +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/tree/cassandra/v1/util/CassandraClientWrapper.java --- /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 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 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 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>> _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; + } +} diff -r f96193babac0 -r 17ed97ca9960 src/treecms/tree/cassandra/v1/util/ConcurrentCassandraClient.java --- /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 get(final String _ks,final String _key,final ColumnPath _path,final ConsistencyLevel _level) + { + Callable task = new Callable(){ + @Override + public ColumnOrSuperColumn call() throws Exception + { + CassandraClientWrapper client = ((CassandraClientThread)Thread.currentThread()).getClientWrapper(); + ColumnOrSuperColumn cors = client.get(_ks,_key,_path,_level); + return cors; + } + }; + + Future ret = m_service.submit(task); + return ret; + } +}