f@0
|
1 /*
|
f@0
|
2 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
|
f@0
|
3
|
f@0
|
4 Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
|
f@0
|
5
|
f@0
|
6 This program is free software: you can redistribute it and/or modify
|
f@0
|
7 it under the terms of the GNU General Public License as published by
|
f@0
|
8 the Free Software Foundation, either version 3 of the License, or
|
f@0
|
9 (at your option) any later version.
|
f@0
|
10
|
f@0
|
11 This program is distributed in the hope that it will be useful,
|
f@0
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
f@0
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
f@0
|
14 GNU General Public License for more details.
|
f@0
|
15
|
f@0
|
16 You should have received a copy of the GNU General Public License
|
f@0
|
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
f@0
|
18 */
|
f@0
|
19
|
f@0
|
20 package uk.ac.qmul.eecs.ccmi.network;
|
f@0
|
21
|
f@0
|
22 import java.nio.channels.SocketChannel;
|
f@0
|
23 import java.util.Enumeration;
|
f@0
|
24 import java.util.HashMap;
|
f@0
|
25 import java.util.Iterator;
|
f@0
|
26 import java.util.LinkedList;
|
f@0
|
27 import java.util.List;
|
f@0
|
28 import java.util.Map;
|
f@0
|
29
|
f@0
|
30 import javax.swing.tree.TreeNode;
|
f@0
|
31
|
f@0
|
32 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramElement;
|
f@0
|
33 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramTreeNode;
|
f@0
|
34 import uk.ac.qmul.eecs.ccmi.diagrammodel.EdgeReferenceHolderMutableTreeNode;
|
f@0
|
35 import uk.ac.qmul.eecs.ccmi.diagrammodel.EdgeReferenceMutableTreeNode;
|
f@0
|
36 import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeReferenceMutableTreeNode;
|
f@0
|
37 import uk.ac.qmul.eecs.ccmi.gui.Edge;
|
f@0
|
38 import uk.ac.qmul.eecs.ccmi.gui.Lock;
|
f@0
|
39 import uk.ac.qmul.eecs.ccmi.gui.Node;
|
f@0
|
40
|
f@0
|
41 /**
|
f@0
|
42 *
|
f@0
|
43 * This class keeps track of the objects currently locked by the users.
|
f@0
|
44 * Locking is done by inserting a lock entry into a list of entries. All lock types have one or more dependencies.
|
f@0
|
45 * A dependency is a lock on the same or another tree node, which prevent the lock from being acquired.
|
f@0
|
46 * The dependency lock can also be of another type of the lock we're trying to acquire.
|
f@0
|
47 * For example if I want to change the arrow head of edge E, before inserting a arrow-head lock in the list
|
f@0
|
48 * I must be sure nobody else has already entered a delete-lock for E, as it means that user is about to
|
f@0
|
49 * delete the edge I want to change the arrow of. Likewise if I want to delete a node I must be sure
|
f@0
|
50 * nobody else inserted a note-lock on a edge reference laying within that node in the tree as that edge reference
|
f@0
|
51 * will go after the node is deleted. If a user tries to acquire a lock and one or more dependencies
|
f@0
|
52 * have been already inserted in the list by other users then the lock won't be granted. I no dependency
|
f@0
|
53 * exist in the list the lock entry is created and inserted.
|
f@0
|
54 *
|
f@0
|
55 *
|
f@0
|
56 * Lock dependencies (unless differently specified the dependency lock is on the same tree node):
|
f@0
|
57 * DELETE : DELETE,ARROW_HEAD,END_LABEL,MOVE,MUST_EXIST,NAME,NOTES, BOOKMARK,PROPERTIES,
|
f@0
|
58 * DELETE on two ended edges if this is a node, NOTES and BOOKMARK on those tree node
|
f@0
|
59 * that will be deleted as a result of this action.
|
f@0
|
60 * EDGE_END : EDGE_END, DELETE
|
f@0
|
61 * MOVE : MOVE, DELETE
|
f@0
|
62 * MUST_EXIST : DELETE
|
f@0
|
63 * NAME : NAME, DELETE
|
f@0
|
64 * NOTES : NOTES, DELETE, DELETE on Objects whose deletion would entail the deletion of this tree node
|
f@0
|
65 * PROPERTIES if this is a subtree of a property node.
|
f@0
|
66 * BOOKMARK : DELETE
|
f@0
|
67 * PROPERTIES : PROPERTIES, DELETE
|
f@0
|
68 *
|
f@0
|
69 *
|
f@0
|
70 */
|
f@0
|
71 class ServerLockManager {
|
f@0
|
72 public ServerLockManager(){
|
f@0
|
73 locksMap = new HashMap<String,List<LockEntry>>();
|
f@0
|
74 }
|
f@0
|
75
|
f@0
|
76 /* check if the specified lock is present in the lock list for the specified tree node */
|
f@0
|
77 private boolean lockExists(DiagramTreeNode treeNode, Lock lock, List<LockEntry> locks, SocketChannel channel){
|
f@0
|
78 for(LockEntry lockEntry : locks){
|
f@0
|
79 if(lockEntry.treeNode.equals(treeNode) && lockEntry.lock == lock && !lockEntry.channel.equals(channel))
|
f@0
|
80 return true;
|
f@0
|
81 }
|
f@0
|
82 return false;
|
f@0
|
83 }
|
f@0
|
84
|
f@0
|
85 /* check if either specified locks is present in the lock list for the specified tree node */
|
f@0
|
86 private boolean lockExists(DiagramTreeNode treeNode, Lock lock1, Lock lock2, List<LockEntry> locks, SocketChannel channel){
|
f@0
|
87 for(LockEntry lockEntry : locks){
|
f@0
|
88 if(lockEntry.treeNode.equals(treeNode) && (lockEntry.lock == lock1 || lockEntry.lock == lock2) && !lockEntry.channel.equals(channel) )
|
f@0
|
89 return true;
|
f@0
|
90 }
|
f@0
|
91 return false;
|
f@0
|
92 }
|
f@0
|
93
|
f@0
|
94 /* Check whether the lock can be granted as it does */
|
f@0
|
95 /* not clash with other locks owned by other clients */
|
f@0
|
96 private boolean checkLockDependencies(DiagramTreeNode treeNode, Lock lock, SocketChannel channel, List<LockEntry> locks){
|
f@0
|
97 /* bookmarks are not shared, we only check them against delete-lock, as editing a */
|
f@0
|
98 /* bookmark on a tree node that has just been would lead to an inconsistent state */
|
f@0
|
99 if(lock != Lock.BOOKMARK)
|
f@0
|
100 for(LockEntry lockEntry : locks){
|
f@0
|
101 /* if the two elements are different, there is no possible clash. Go ahead */
|
f@0
|
102 if(!lockEntry.treeNode.equals(treeNode))
|
f@0
|
103 continue;
|
f@0
|
104 /* locks are reentrant, that is if a client requests a lock he's already got *
|
f@0
|
105 * then he's automatically granted for that lock */
|
f@0
|
106 if(lockEntry.channel.equals(channel)){
|
f@0
|
107 if(lock.equals(lockEntry.lock)){
|
f@0
|
108 return true;
|
f@0
|
109 }
|
f@0
|
110 continue; //if the clients has ad different lock then just go ahead
|
f@0
|
111 }
|
f@0
|
112 /* DELETE depends on all the other locks and all the other locks depend on it */
|
f@0
|
113 /* (bear in mind that at this point of the code lockEntry.treeNode == treeNode) */
|
f@0
|
114 if(lock == Lock.DELETE||lockEntry.lock == Lock.DELETE)
|
f@0
|
115 return false;
|
f@0
|
116 /* unless the lock is of type MUST_EXIST, if someone else has the desired lock, then the client */
|
f@0
|
117 /* cannot get it that is, only MUST_EXIST can be shared by different clients at the same time */
|
f@0
|
118 if(lock != Lock.MUST_EXIST && lock == lockEntry.lock)
|
f@0
|
119 return false;
|
f@0
|
120 }
|
f@0
|
121
|
f@0
|
122 /* delete-locks on nodes and edges involved with treeNode will prevent from acquiring the lock as treeNode might */
|
f@0
|
123 /* no longer exist after the editing. See the following comments to figure out what "involved" means in this context */
|
f@0
|
124 if(lock == Lock.NOTES || lock == Lock.BOOKMARK){
|
f@0
|
125 /* no diagram element along the path from root to this treeNode must be delete-locked or properties-locked */
|
f@0
|
126 /* (as editing a property might mean delete some tree nodes and, maybe, this tree node) */
|
f@0
|
127 for(TreeNode tn : treeNode.getPath()){
|
f@0
|
128 if(tn instanceof DiagramElement)
|
f@0
|
129 if(lockExists((DiagramElement)tn,Lock.DELETE,Lock.PROPERTIES,locks,channel))
|
f@0
|
130 return false;
|
f@0
|
131 }
|
f@0
|
132 /* if note-locking a reference tree node, the referred diagram element must not be delete-locked */
|
f@0
|
133 /* as the reference will go as well after the diagram element will be deleted */
|
f@0
|
134 if(treeNode instanceof NodeReferenceMutableTreeNode){
|
f@0
|
135 NodeReferenceMutableTreeNode referenceNode = (NodeReferenceMutableTreeNode)treeNode;
|
f@0
|
136 if(lockExists(referenceNode.getNode(),Lock.DELETE,locks,channel))
|
f@0
|
137 return false;
|
f@0
|
138 }
|
f@0
|
139 if(treeNode instanceof EdgeReferenceMutableTreeNode){
|
f@0
|
140 EdgeReferenceMutableTreeNode referenceNode = (EdgeReferenceMutableTreeNode)treeNode;
|
f@0
|
141 if(lockExists(referenceNode.getEdge(),Lock.DELETE,locks,channel))
|
f@0
|
142 return false;
|
f@0
|
143 }
|
f@0
|
144 /* if note locking an edge reference tree holder which has only one child, we cannot grant *
|
f@0
|
145 * the lock if the referred edge is delete-locked as the holder will be deleted as well after *
|
f@0
|
146 * the eventual deletion of the edge */
|
f@0
|
147 if(treeNode instanceof EdgeReferenceHolderMutableTreeNode && treeNode.getChildCount() == 1){
|
f@0
|
148 EdgeReferenceMutableTreeNode referenceNode = (EdgeReferenceMutableTreeNode)treeNode.getChildAt(0);
|
f@0
|
149 if(lockExists(referenceNode.getEdge(),Lock.DELETE,locks,channel))
|
f@0
|
150 return false;
|
f@0
|
151 }
|
f@0
|
152 }
|
f@0
|
153
|
f@0
|
154 if(lock == Lock.DELETE){
|
f@0
|
155 /* all the descendants of the element must be non notes-locked or bookmark-locked */
|
f@0
|
156 for(@SuppressWarnings("rawtypes")
|
f@0
|
157 Enumeration enumeration = treeNode.breadthFirstEnumeration(); enumeration.hasMoreElements();){
|
f@0
|
158 if(lockExists((DiagramTreeNode)enumeration.nextElement(),Lock.NOTES,Lock.BOOKMARK,locks,channel))
|
f@0
|
159 return false;
|
f@0
|
160 }
|
f@0
|
161
|
f@0
|
162 if(treeNode instanceof Node){
|
f@0
|
163 Node n = (Node)treeNode;
|
f@0
|
164 /* if we want to delete a Node we must get the lock on each attached *
|
f@0
|
165 * edge as they will be deleted as well */
|
f@0
|
166 for(int i =0; i< n.getEdgesNum();i++){
|
f@0
|
167 Edge e = n.getEdgeAt(i);
|
f@0
|
168 if(lockExists(e,Lock.DELETE,locks,channel))
|
f@0
|
169 return false;
|
f@0
|
170 /* In order to delete-lock a Node, no referee must be bookmark/notes-locked. The referees *
|
f@0
|
171 * are the NodeReferenceTreeNode's pointing to this Node */
|
f@0
|
172 for(int j=0;j<e.getChildCount();j++){
|
f@0
|
173 NodeReferenceMutableTreeNode nodeRef = (NodeReferenceMutableTreeNode)e.getChildAt(j);
|
f@0
|
174 if(nodeRef.getNode().equals(n))
|
f@0
|
175 if(lockExists(nodeRef,Lock.NOTES,Lock.BOOKMARK,locks,channel))
|
f@0
|
176 return false;
|
f@0
|
177 }
|
f@0
|
178 }
|
f@0
|
179 }
|
f@0
|
180
|
f@0
|
181 if(treeNode instanceof Edge){
|
f@0
|
182 /* for each node check whether the reference to this edge is notes-locked */
|
f@0
|
183 Edge e = (Edge)treeNode;
|
f@0
|
184 for(int i=0;i<e.getNodesNum();i++){
|
f@0
|
185 Node n = e.getNodeAt(i);
|
f@0
|
186 for(int j=0;j<n.getChildCount();j++){
|
f@0
|
187 if(n.getChildAt(j) instanceof EdgeReferenceHolderMutableTreeNode){
|
f@0
|
188 EdgeReferenceHolderMutableTreeNode refHolder = (EdgeReferenceHolderMutableTreeNode)n.getChildAt(j);
|
f@0
|
189 /* someone else is editing notes on the reference holder and it has only one child */
|
f@0
|
190 /* which means it will be deleted after the edge deletion, the lock cannot be granted then */
|
f@0
|
191 if((refHolder.getChildCount() == 1) && lockExists(refHolder,Lock.NOTES,Lock.BOOKMARK,locks,channel))
|
f@0
|
192 return false;
|
f@0
|
193 /* if a reference tree node pointing to this edge is notes-locked, the edge can't be deleted */
|
f@0
|
194 for(int k=0;k<refHolder.getChildCount();k++){
|
f@0
|
195 EdgeReferenceMutableTreeNode edgeRef = (EdgeReferenceMutableTreeNode)refHolder.getChildAt(k);
|
f@0
|
196 if(lockExists(edgeRef,Lock.NOTES,Lock.BOOKMARK,locks,channel))
|
f@0
|
197 return false;
|
f@0
|
198 }
|
f@0
|
199 }
|
f@0
|
200 }
|
f@0
|
201 }
|
f@0
|
202 }
|
f@0
|
203 }
|
f@0
|
204
|
f@0
|
205 /* all the checks have been passed, the client definitely deserves the lock now */
|
f@0
|
206 return true;
|
f@0
|
207 }
|
f@0
|
208
|
f@0
|
209 /**
|
f@0
|
210 * Request an editing lock for a tree node
|
f@0
|
211 *
|
f@0
|
212 * @param treeNode the treeNode the caller is trying to lock
|
f@0
|
213 * @param lock the type of lock requested
|
f@0
|
214 * @param channel the channel works as a unique identifier for the clients
|
f@0
|
215 * @param diagramName the name of the diagram the lock is requested on
|
f@0
|
216 * @return true if the lock is successfully granted, or false otherwise (because of another client
|
f@0
|
217 * holding a lock which clashes with this request)
|
f@0
|
218 */
|
f@0
|
219 public boolean requestLock(DiagramTreeNode treeNode, Lock lock, SocketChannel channel,String diagramName){
|
f@0
|
220 // System.out.println("lock before request:"+lockStatusDescription(diagramName)+"\n----");
|
f@0
|
221 List<LockEntry> locks = locksMap.get(diagramName);
|
f@0
|
222 if(locks == null){
|
f@0
|
223 /* if no object in the diagram has ever been locked */
|
f@0
|
224 /* there is no entry in the map and one must be created */
|
f@0
|
225 locks = new LinkedList<LockEntry>();
|
f@0
|
226 locksMap.put(diagramName,locks);
|
f@0
|
227 }
|
f@0
|
228 /* deleting a node will cause all the attached two-ended edges to *
|
f@0
|
229 * be deleted, therefore we need to lock all those edges too, before */
|
f@0
|
230 if(lock == Lock.DELETE && treeNode instanceof Node){
|
f@0
|
231 Node n = (Node)treeNode;
|
f@0
|
232 for(int i=0; i<n.getEdgesNum();i++){
|
f@0
|
233 if(n.getEdgeAt(i).getNodesNum() > 2)
|
f@0
|
234 continue;
|
f@0
|
235 boolean succeeded = requestLock(n.getEdgeAt(i),Lock.DELETE,channel,diagramName);
|
f@0
|
236 if(!succeeded){
|
f@0
|
237 /* release the previously acquired locks and return a failure */
|
f@0
|
238 for(int j=0;j<i;j++){
|
f@0
|
239 if(n.getEdgeAt(j).getNodesNum() == 2)
|
f@0
|
240 releaseLock(n.getEdgeAt(j),Lock.DELETE,channel,diagramName);
|
f@0
|
241 }
|
f@0
|
242 return false;
|
f@0
|
243 }
|
f@0
|
244 }
|
f@0
|
245 }
|
f@0
|
246
|
f@0
|
247 if(!checkLockDependencies(treeNode,lock,channel,locks)){
|
f@0
|
248 if(lock == Lock.DELETE && treeNode instanceof Node){
|
f@0
|
249 Node n = (Node)treeNode;
|
f@0
|
250 for(int j=0;j<n.getEdgesNum();j++){
|
f@0
|
251 if(n.getEdgeAt(j).getNodesNum() == 2)
|
f@0
|
252 releaseLock(n.getEdgeAt(j),Lock.DELETE,channel,diagramName);
|
f@0
|
253 }
|
f@0
|
254 }
|
f@0
|
255 return false;
|
f@0
|
256 }
|
f@0
|
257
|
f@0
|
258 /* adds the lock only if it doesn't already exist */
|
f@0
|
259 boolean add = true;
|
f@0
|
260 for(LockEntry l : locks){
|
f@0
|
261 if(l.channel.equals(channel) && l.lock.equals(lock) && l.treeNode.equals(treeNode)){
|
f@0
|
262 add = false;
|
f@0
|
263 break;
|
f@0
|
264 }
|
f@0
|
265 }
|
f@0
|
266 if(add)
|
f@0
|
267 locks.add(new LockEntry(treeNode,lock,channel));
|
f@0
|
268 // System.out.println("lock after request:"+lockStatusDescription(diagramName)+"\n----");
|
f@0
|
269 return true;
|
f@0
|
270 }
|
f@0
|
271 /**
|
f@0
|
272 * Release a lock previously acquired
|
f@0
|
273 * @param treeNode the tree node, whose lock is getting released
|
f@0
|
274 * @param lock the lock type
|
f@0
|
275 * @param channel the channel of the client releasing the lock
|
f@0
|
276 * @param diagramName the diagram whose tree node is affected by this call
|
f@0
|
277 *
|
f@0
|
278 * @return true if a lock was really yielded as a result of the call
|
f@0
|
279 */
|
f@0
|
280 public boolean releaseLock(DiagramTreeNode treeNode, Lock lock, SocketChannel channel, String diagramName){
|
f@0
|
281 List<LockEntry> locks = locksMap.get(diagramName);
|
f@0
|
282 Iterator<LockEntry> iterator = locks.iterator();
|
f@0
|
283 boolean lockReleased = false;
|
f@0
|
284 while(iterator.hasNext()){
|
f@0
|
285 LockEntry entry = iterator.next();
|
f@0
|
286 if(entry.treeNode.equals(treeNode) && entry.lock == lock && entry.channel == channel){
|
f@0
|
287 iterator.remove();
|
f@0
|
288 lockReleased = true;
|
f@0
|
289 if(lock == Lock.DELETE && treeNode instanceof Node)
|
f@0
|
290 continue; // we have to check for attached edges which must be unlocked too
|
f@0
|
291 else
|
f@0
|
292 break;// if ain't a delete lock, we found what looking for and we can stop
|
f@0
|
293 }
|
f@0
|
294 /* if a delete lock, we have to check for attached edges which must be unlocked too */
|
f@0
|
295 if(lock == Lock.DELETE && entry.treeNode instanceof Edge && treeNode instanceof Node){
|
f@0
|
296 Edge e = (Edge)entry.treeNode;
|
f@0
|
297 if(e.getNodesNum() == 2){
|
f@0
|
298 if(e.getNodeAt(0).equals(treeNode) || e.getNodeAt(1).equals(treeNode)){
|
f@0
|
299 iterator.remove();
|
f@0
|
300 }
|
f@0
|
301 }
|
f@0
|
302 }
|
f@0
|
303 }
|
f@0
|
304 // System.out.println("lock release:"+lockStatusDescription(diagramName)+"\n----");
|
f@0
|
305 return lockReleased;
|
f@0
|
306 }
|
f@0
|
307
|
f@0
|
308 /**
|
f@0
|
309 * Removes all the locks related to a diagram element, to be called when a diagram element is deleted.
|
f@0
|
310 * If the diagram element is a Node all the locks of the two-ended edges attached to it will be
|
f@0
|
311 * removed as well.
|
f@0
|
312 * @param element the diagram element whose locks must be removed
|
f@0
|
313 * @param diagramName the diagram the element has to be removed from
|
f@0
|
314 */
|
f@0
|
315 public void removeLocks(DiagramElement element, String diagramName){
|
f@0
|
316 List<LockEntry> locks = locksMap.get(diagramName);
|
f@0
|
317 if(locks == null)
|
f@0
|
318 return;
|
f@0
|
319 Iterator<LockEntry> iterator = locks.iterator();
|
f@0
|
320 boolean isNode = (element instanceof Node);
|
f@0
|
321 while(iterator.hasNext()){
|
f@0
|
322 LockEntry entry = iterator.next();
|
f@0
|
323 if(entry.treeNode.equals(element)){
|
f@0
|
324 iterator.remove();
|
f@0
|
325 }
|
f@0
|
326 /* remove the lock id it's a two ended edges locks attached to this Node */
|
f@0
|
327 if(isNode && entry.treeNode instanceof Edge){
|
f@0
|
328 Edge e = (Edge)entry.treeNode;
|
f@0
|
329 if(e.getNodesNum() > 2)
|
f@0
|
330 continue;
|
f@0
|
331 if(e.getNodeAt(0).equals(element) || e.getNodeAt(1).equals(element)){
|
f@0
|
332 iterator.remove();
|
f@0
|
333 }
|
f@0
|
334 }
|
f@0
|
335 }
|
f@0
|
336 }
|
f@0
|
337
|
f@0
|
338 /**
|
f@0
|
339 * remove all the locks acquired by a client, this is normally called when a client disconnects
|
f@0
|
340 * @param channel the channel uniquely identifying the client
|
f@0
|
341 * @param diagramName the name of the diagram the channel is related to
|
f@0
|
342 */
|
f@0
|
343 public void removeLocks(SocketChannel channel, String diagramName){
|
f@0
|
344 if(!locksMap.containsKey(diagramName))
|
f@0
|
345 /* this can happen if a client connects and downloads the diagram list *
|
f@0
|
346 * and then disconnects without opening any diagram */
|
f@0
|
347 return;
|
f@0
|
348 Iterator<LockEntry> itr = locksMap.get(diagramName).iterator();
|
f@0
|
349 while(itr.hasNext())
|
f@0
|
350 if(itr.next().channel.equals(channel))
|
f@0
|
351 itr.remove();
|
f@0
|
352 }
|
f@0
|
353
|
f@0
|
354 public String lockStatusDescription(String diagramName){
|
f@0
|
355 StringBuilder builder = new StringBuilder();
|
f@0
|
356 if(locksMap.containsKey(diagramName)){
|
f@0
|
357 List<LockEntry> locks = locksMap.get(diagramName);
|
f@0
|
358 builder.append(diagramName).append('\n');
|
f@0
|
359 for(LockEntry entry : locks){
|
f@0
|
360 builder.append(entry.channel.socket().getInetAddress().getHostAddress()).append(' ').
|
f@0
|
361 append(entry.lock).append(' ').
|
f@0
|
362 append(entry.treeNode.getName()).append('\n');
|
f@0
|
363 }
|
f@0
|
364 }
|
f@0
|
365 return builder.toString();
|
f@0
|
366 }
|
f@0
|
367
|
f@0
|
368 public void clearAllLocks(){ // should not be used unless in a debugging session
|
f@0
|
369 locksMap = new HashMap<String,List<LockEntry>>();
|
f@0
|
370 }
|
f@0
|
371
|
f@0
|
372 private Map<String,List<LockEntry>> locksMap;
|
f@0
|
373
|
f@0
|
374 private static class LockEntry {
|
f@0
|
375 public LockEntry(DiagramTreeNode treeNode,Lock lock,SocketChannel channel) {
|
f@0
|
376 this.channel = channel;
|
f@0
|
377 this.lock = lock;
|
f@0
|
378 this.treeNode = treeNode;
|
f@0
|
379 }
|
f@0
|
380
|
f@0
|
381 public SocketChannel channel;
|
f@0
|
382 public Lock lock;
|
f@0
|
383 public DiagramTreeNode treeNode;
|
f@0
|
384 }
|
f@0
|
385 }
|