Source Code Cross Referenced for AbstractNode.java in  » Inversion-of-Control » carbon » org » sape » carbon » core » config » node » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Inversion of Control » carbon » org.sape.carbon.core.config.node 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * The contents of this file are subject to the Sapient Public License
003:         * Version 1.0 (the "License"); you may not use this file except in compliance
004:         * with the License. You may obtain a copy of the License at
005:         * http://carbon.sf.net/License.html.
006:         *
007:         * Software distributed under the License is distributed on an "AS IS" basis,
008:         * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
009:         * the specific language governing rights and limitations under the License.
010:         *
011:         * The Original Code is The Carbon Component Framework.
012:         *
013:         * The Initial Developer of the Original Code is Sapient Corporation
014:         *
015:         * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
016:         */
017:
018:        package org.sape.carbon.core.config.node;
019:
020:        import java.util.Collection;
021:        import java.util.Collections;
022:        import java.util.HashMap;
023:        import java.util.HashSet;
024:        import java.util.Iterator;
025:        import java.util.Map;
026:        import java.util.Set;
027:
028:        import org.apache.commons.logging.Log;
029:        import org.apache.commons.logging.LogFactory;
030:
031:        import org.sape.carbon.core.config.InvalidConfigurationException;
032:        import org.sape.carbon.core.config.node.event.NodeEventListener;
033:        import org.sape.carbon.core.exception.ExceptionUtility;
034:        import org.sape.carbon.core.exception.InvalidParameterException;
035:
036:        /**
037:         * Abstract implementation of the <code>Node</code> interface.  This
038:         * implementation is designed to be extended with the logic specific to the
039:         * backing data store placed in the <code>destroyBackingData</code> method.
040:         *
041:         * Copyright 2002 Sapient
042:         * @since carbon 1.0
043:         * @author Douglas Voet, February 2002
044:         * @version $Revision: 1.28 $($Author: dvoet $ / $Date: 2003/10/17 14:40:55 $)
045:         */
046:        public abstract class AbstractNode implements  Node {
047:
048:            /**
049:             * Provides a handle to Apache-commons logger
050:             */
051:            private Log log = LogFactory.getLog(this .getClass());
052:
053:            /** this Node's name */
054:            private final String name;
055:
056:            /** link to the Node's parent */
057:            private final Node parent;
058:
059:            /** this node's removed status */
060:            private boolean removed = false;
061:
062:            /** 
063:             * This lock is used to ensure that nodes are not added or 
064:             * loaded concurrently. Any time a node is put into childNodes, this lock
065:             * guarantees that the node is constructed once and only once. 
066:             * addOrLoadChildLock is usually used in conjunction with a lock on this. 
067:             * A lock on addOrLoadChildLock keeps other threads from loading or adding
068:             * new nodes while a lock on this ensures that access to childNodes is
069:             * thread-safe.
070:             */
071:            private final Object addOrLoadChildLock;
072:
073:            /**
074:             * This object is syncrhonized upon whenever this node's state is read
075:             * or altered. The state of this node includes the removed flag and
076:             * the childNodes map, but not children themselves. State does not
077:             * include this node's name or parent as these references are final.
078:             * State also does not include the nodeListeners set as it synchronizes
079:             * itself. 
080:             */
081:            private final Object readOrAlterNodeLock;
082:
083:            /**
084:             * Map of child Nodes keyed by the Node's name (String).  Final because
085:             * the reference to the map should never change for this node.
086:             *
087:             * @link aggregation
088:             *      @associates <{Node}>
089:             */
090:            protected final Map childNodes = new HashMap();
091:
092:            /**
093:             * Contains the list of node listeners for this node.
094:             */
095:            protected final Set nodeListeners = Collections
096:                    .synchronizedSet(new HashSet());
097:
098:            /**
099:             * Constructor
100:             *
101:             * @param parent the node's parent
102:             * @param name the node's name
103:             *
104:             * @throws InvalidParameterException if name is null
105:             */
106:            public AbstractNode(Node parent, String name) {
107:                this (parent, name, new Object(), new Object());
108:            }
109:
110:            /**
111:             * Constructor
112:             *
113:             * @param parent the node's parent
114:             * @param name the node's name
115:             * @param readOrAlterNodeLock the lock object that will be synchronized
116:             * upon for all operations that access or modify this node's state
117:             *
118:             * @throws InvalidParameterException if name is null or readOrAlterNodeLock
119:             * is null
120:             */
121:            public AbstractNode(Node parent, String name,
122:                    Object readOrAlterNodeLock, Object addOrLoadChildLock) {
123:
124:                // parameter check
125:                if (name == null) {
126:                    throw new InvalidParameterException(this .getClass(),
127:                            "name parameter cannot be null");
128:                }
129:
130:                if (readOrAlterNodeLock == null) {
131:                    throw new InvalidParameterException(this .getClass(),
132:                            "readOrAlterNodeLock parameter cannot be null");
133:                }
134:
135:                if (addOrLoadChildLock == null) {
136:                    throw new InvalidParameterException(this .getClass(),
137:                            "addOrLoadChildLock parameter cannot be null");
138:                }
139:
140:                if (addOrLoadChildLock == readOrAlterNodeLock) {
141:                    throw new InvalidParameterException(this .getClass(),
142:                            "readOrAlterNodeLock and addOrLoadChildLock cannot be the same object");
143:                }
144:
145:                this .parent = parent;
146:                this .name = name;
147:                this .readOrAlterNodeLock = readOrAlterNodeLock;
148:                this .addOrLoadChildLock = addOrLoadChildLock;
149:            }
150:
151:            /**
152:             * @see Node#getName()
153:             */
154:            public String getName() {
155:                return this .name;
156:            }
157:
158:            /**
159:             * @see Node#getParent()
160:             */
161:            public Node getParent() {
162:                return this .parent;
163:            }
164:
165:            /**
166:             * This implementation is recursive.
167:             * If the parent is not null, it returns the parent's absolute name
168:             * followed by Node.DELIMITER and this node's name
169:             *
170:             * @see Node#getAbsoluteName()
171:             */
172:            public String getAbsoluteName() {
173:
174:                if (getParent() == null) {
175:                    return this .name;
176:                } else {
177:                    return getParent().getAbsoluteName() + Node.DELIMITER
178:                            + this .name;
179:                }
180:            }
181:
182:            /**
183:             * @see Node#getAllowsChildren()
184:             */
185:            public boolean getAllowsChildren() {
186:                return true;
187:            }
188:
189:            /**
190:             * @see Node#remove()
191:             *
192:             * synchronized to ensure no one is fetching or adding children while
193:             * this method is removing them
194:             */
195:            public int remove() throws NodeRemovalException {
196:                int removedNodes = 0;
197:
198:                if (!isRemoved()) {
199:                    // remove all the children
200:                    // note that this is not synchronized because that would cause
201:                    // a cascade of locks down the config hierachy and open a deadlock
202:                    // vulnerability
203:                    Node[] children = fetchChildren();
204:
205:                    for (int i = 0; i < children.length; i++) {
206:                        removedNodes += children[i].remove();
207:                    }
208:
209:                    synchronized (getReadOrAlterNodeLock()) {
210:                        if (backingDataExists()) {
211:                            // remove this node
212:                            destroyBackingData();
213:                            removedNodes++;
214:                        }
215:                        this .childNodes.clear();
216:                        setRemoved();
217:                    }
218:                }
219:
220:                return removedNodes;
221:            }
222:
223:            /**
224:             * Returns the name of the node.
225:             *
226:             * @return the name of the node
227:             */
228:            public String toString() {
229:                return getName();
230:            }
231:
232:            /**
233:             * @see Node#isRemoved()
234:             */
235:            public boolean isRemoved() {
236:                synchronized (getReadOrAlterNodeLock()) {
237:                    return this .removed;
238:                }
239:            }
240:
241:            /**
242:             * This implementation refreshes all children and toggles the isRemoved
243:             * flag depending on the return value of the backingDataExists method.
244:             *
245:             * @see Node#refresh()
246:             */
247:            public void refresh() {
248:                if (!isRemoved()) {
249:
250:                    // localChildNodesMap is a local copy of this.childNodes that is
251:                    // used so that children can be refreshed in an unsynchronized
252:                    // context
253:                    Map localChildNodesMap;
254:
255:                    synchronized (getReadOrAlterNodeLock()) {
256:                        localChildNodesMap = new HashMap(this .childNodes);
257:
258:                        if (!backingDataExists()) {
259:                            // backing data is not there anymore!
260:                            setRemoved();
261:                        }
262:
263:                    }
264:                    removeRemovedChildren();
265:
266:                    // tell all the children to refresh themselves
267:                    // note that if this node has been removed, refreshing all
268:                    // the children should cause them to be marked as removed
269:                    // as well
270:                    Iterator childIterator = localChildNodesMap.values()
271:                            .iterator();
272:                    while (childIterator.hasNext()) {
273:                        ((Node) childIterator.next()).refresh();
274:                    }
275:                }
276:            }
277:
278:            /**
279:             * synchronized to ensure no one is adding or removing children
280:             * while childName is being fetched
281:             *
282:             * @see Node#fetchChild(String)
283:             */
284:            public Node fetchChild(String childName)
285:                    throws NodeNotFoundException {
286:
287:                if (isRemoved()) {
288:                    throw new NodeRemovedException(this .getClass(), this );
289:                }
290:
291:                if (childName == null) {
292:                    throw new InvalidParameterException(this .getClass(),
293:                            "childName cannot be null");
294:                }
295:
296:                Node child;
297:                synchronized (getReadOrAlterNodeLock()) {
298:                    child = (Node) this .childNodes.get(childName);
299:                }
300:
301:                // note that the call to child.isRemoved() requires a lock on child
302:                // which means that we cannot call it while synchronized on this without
303:                // openning a deadlock vulnerability
304:                while (child != null && child.isRemoved()) {
305:                    synchronized (getReadOrAlterNodeLock()) {
306:                        Node doubleCheckChild = (Node) this .childNodes
307:                                .get(childName);
308:                        if (child == doubleCheckChild) {
309:                            // doubleCheckChild is the same child that we checked was
310:                            // removed, this means that no one else loaded it behind
311:                            // our back so we can go ahead and remove it and continue
312:                            // as if the child did not exist in the first place
313:                            this .childNodes.remove(childName);
314:                            child = null;
315:                        } else {
316:                            // the doubleCheckChild is different than child so someone
317:                            // must have loaded it behind our back, so, continue with
318:                            // child = doubleCheckChild and recheck
319:                            child = doubleCheckChild;
320:                        }
321:                    }
322:
323:                }
324:
325:                if (child == null) {
326:                    // we must synchronize on an object other than this here because
327:                    // we must ensure that the child is loaded only once, but we
328:                    // cannot maintain a lock on this while loading the child because
329:                    // loading a child might require traversing the config hierachy
330:                    // which requires other locks and opens a deadlock vulnerability 
331:                    synchronized (getAddOrLoadChildLock()) {
332:                        synchronized (getReadOrAlterNodeLock()) {
333:                            // need to check if childName exists in childNodes one more
334:                            // time because another thread could loaded childName
335:                            // before we synchronized on loadChildLock
336:                            child = (Node) this .childNodes.get(childName);
337:                        }
338:
339:                        if (child == null) {
340:                            // OK, the child still has not been loaded, we are safe to 
341:                            // load it
342:                            child = loadChild(childName);
343:
344:                            // add it to childNodes
345:                            synchronized (getReadOrAlterNodeLock()) {
346:                                this .childNodes.put(childName, child);
347:                            }
348:                        }
349:                    }
350:                }
351:
352:                return child;
353:            }
354:
355:            /**
356:             * @see Node#fetchChildren()
357:             */
358:            public Node[] fetchChildren() {
359:                if (isRemoved()) {
360:                    throw new NodeRemovedException(this .getClass(), this );
361:                }
362:
363:                removeRemovedChildren();
364:                Collection cachedChildNames;
365:                synchronized (getReadOrAlterNodeLock()) {
366:                    cachedChildNames = this .childNodes.keySet();
367:                }
368:
369:                // fetch non-cached children
370:                Set childNamesToLoad = getAllChildNames();
371:                childNamesToLoad.removeAll(cachedChildNames);
372:                Iterator childNameIterator = childNamesToLoad.iterator();
373:                while (childNameIterator.hasNext()) {
374:                    try {
375:                        fetchChild((String) childNameIterator.next());
376:                    } catch (NodeNotFoundException nnfe) {
377:                        // This would happen only if the impl of getAllChildNames()
378:                        // returns invalid data or if the node is invalid
379:                        if (log.isInfoEnabled()) {
380:                            log.info("Caught NodeNotFoundException: "
381:                                    + ExceptionUtility
382:                                            .printStackTracesToString(nnfe));
383:                        }
384:                        // continue to load other children
385:                    } catch (InvalidConfigurationException ice) {
386:                        // This would happen only if the node is invalid
387:                        if (log.isInfoEnabled()) {
388:                            log.info("Caught InvalidConfigurationException: "
389:                                    + ExceptionUtility
390:                                            .printStackTracesToString(ice));
391:                        }
392:                        // continue to load other children
393:                    }
394:                }
395:
396:                // create return array, but be sure not to return any removed nodes
397:                Collection children = new HashSet(this .childNodes.values());
398:                Iterator childrenIterator = children.iterator();
399:
400:                while (childrenIterator.hasNext()) {
401:                    Node child = (Node) childrenIterator.next();
402:                    if (child.isRemoved()) {
403:                        childrenIterator.remove();
404:                    }
405:                }
406:
407:                return (Node[]) children.toArray(new Node[children.size()]);
408:            }
409:
410:            /**
411:             * @see Folder#containsChild(String)
412:             *
413:             * synchronized to ensure no one is adding or removing children
414:             * while childName is being checking to see if childName exists
415:             */
416:            public boolean containsChild(String childName) {
417:                if (isRemoved()) {
418:                    throw new NodeRemovedException(this .getClass(), this );
419:                }
420:
421:                removeRemovedChildren();
422:                boolean containsChild;
423:                synchronized (getReadOrAlterNodeLock()) {
424:                    containsChild = this .childNodes.containsKey(childName);
425:                }
426:
427:                if (!containsChild) {
428:                    containsChild = getAllChildNames().contains(childName);
429:                }
430:
431:                return containsChild;
432:            }
433:
434:            /**
435:             * @see org.sape.carbon.core.config.node.Node#addNodeListener(NodeEventListener)
436:             */
437:            public void addNodeListener(NodeEventListener listener) {
438:                this .nodeListeners.add(listener);
439:            }
440:
441:            /**
442:             * This method is called by <code>remove</code> to destroy the data
443:             * backing this node in the data source.  Implementations of this method
444:             * should remove all traces of the <code>Node</code>'s existence
445:             *
446:             * @throws NodeRemovalException indicates an error removing the node
447:             */
448:            protected abstract void destroyBackingData()
449:                    throws NodeRemovalException;
450:
451:            /**
452:             * Called by fetchChildren to get the names of all the children from
453:             * the backing data store.  If a name appears in the returned Set,
454:             * calling fetchChild with the name can not result in a
455:             * NodeNotFoundException being thrown.
456:             *
457:             * @return Set of <code>String</code>s, the names of all the children
458:             */
459:            protected abstract Set getAllChildNames();
460:
461:            /**
462:             * Loads the child specified by nodeName from the backing data store.
463:             * Called by fetchChild when the child has not yet been cached.
464:             *
465:             * @param nodeName the name of the node to load
466:             * @return Node the loaded node
467:             *
468:             * @throws NodeNotFoundException if the backing data for the specifed
469:             * node could not be found in the data store.
470:             */
471:            protected abstract Node loadChild(String nodeName)
472:                    throws NodeNotFoundException;
473:
474:            /**
475:             * Method called from the refresh method to see if the backing data
476:             * still exists.  If it exists, the isRemoved flag is set to false, if
477:             * it does not, the isRemoved flag is set to true.
478:             * @return boolean true if the backing date exists, false otherwise
479:             */
480:            protected abstract boolean backingDataExists();
481:
482:            /**
483:             * Removes all nodes from this.childNodes for which isRemoved returns true.
484:             */
485:            protected void removeRemovedChildren() {
486:                Map childNodesLocalCopy;
487:                synchronized (getReadOrAlterNodeLock()) {
488:                    childNodesLocalCopy = new HashMap(this .childNodes);
489:                }
490:
491:                Iterator childIterator = childNodesLocalCopy.values()
492:                        .iterator();
493:                while (childIterator.hasNext()) {
494:                    Node child = (Node) childIterator.next();
495:                    if (child.isRemoved()) {
496:                        childIterator.remove();
497:                    }
498:                }
499:
500:                synchronized (getReadOrAlterNodeLock()) {
501:                    this .childNodes.keySet().retainAll(
502:                            childNodesLocalCopy.keySet());
503:                }
504:            }
505:
506:            /**
507:             * Sets the node state to be removed.
508:             * Note that this method is not synchronized but must be called from
509:             * section of code synchronized on getReadOrAlterNodeLock() object.
510:             */
511:            protected void setRemoved() {
512:                this .removed = true;
513:            }
514:
515:            /**
516:             * Notifies all listenters that this node has been changed.
517:             * Note that this should be not be called from a synchronized context.
518:             *
519:             * @since carbon 1.1
520:             */
521:            protected void issueNodeChangedEvent() {
522:                // get the listeners as an array in order to get a local copy and
523:                // not have to synchronize during the iteration and calls to the
524:                // nodeChanged method
525:                NodeEventListener[] listeners = (NodeEventListener[]) this .nodeListeners
526:                        .toArray(new NodeEventListener[0]);
527:
528:                for (int i = 0; i < listeners.length; i++) {
529:                    listeners[i].nodeChanged(this );
530:                }
531:            }
532:
533:            /**
534:             * Notifies all listenters that this node has been removed.
535:             * Note that this should be not be called from a synchronized context.
536:             *
537:             * @since carbon 1.1
538:             */
539:            protected void issueNodeRemovedEvent() {
540:                // get the listeners as an array in order to get a local copy and
541:                // not have to synchronize during the iteration and calls to the
542:                // nodeRemoved method
543:                NodeEventListener[] listeners = (NodeEventListener[]) this .nodeListeners
544:                        .toArray(new NodeEventListener[0]);
545:
546:                for (int i = 0; i < listeners.length; i++) {
547:                    listeners[i].nodeRemoved(getAbsoluteName());
548:                }
549:            }
550:
551:            /**
552:             * Gets the lock object to synchronize upon when reading or altering the 
553:             * removed flag or the childNodes Map.
554:             * <p>
555:             * This method is final because the design of the abstract node 
556:             * implementations requires that the return value of this method never
557:             * change once this object is constructed. The return value can be set
558:             * in one of the constructors.
559:             * 
560:             * @see AbstractNode#AbstractNode(Node, String, Object, Object)
561:             * 
562:             * @return the lock object
563:             * 
564:             * @since carbon 2.1
565:             */
566:            protected final Object getReadOrAlterNodeLock() {
567:                return this .readOrAlterNodeLock;
568:            }
569:
570:            /**
571:             * Gets the lock object to synchronized upon when creating new children
572:             * or loading existing children. This lock ensures that child nodes are
573:             * created only once.
574:             * <p>
575:             * This method is final because the design of the abstract node 
576:             * implementations requires that the return value of this method never
577:             * change once this object is constructed. The return value can be set
578:             * in one of the constructors.
579:             * 
580:             * @see AbstractNode#AbstractNode(Node, String, Object, Object)
581:             * 
582:             * @return the lock object
583:             * 
584:             * @since carbon 2.1
585:             */
586:            protected final Object getAddOrLoadChildLock() {
587:                return this.addOrLoadChildLock;
588:            }
589:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.