001: /*
002: * Copyright 2001-2006 C:1 Financial Services GmbH
003: *
004: * This software is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License Version 2.1, as published by the Free Software Foundation.
007: *
008: * This software is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
011: * Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public
014: * License along with this library; if not, write to the Free Software
015: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
016: */
017:
018: package de.finix.contelligent.core;
019:
020: import java.util.ArrayList;
021: import java.util.Collections;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Map;
025:
026: import de.finix.contelligent.CallData;
027: import de.finix.contelligent.Component;
028: import de.finix.contelligent.ComponentCreationException;
029: import de.finix.contelligent.ComponentLink;
030: import de.finix.contelligent.ComponentManager;
031: import de.finix.contelligent.ComponentNotFoundException;
032: import de.finix.contelligent.ComponentPath;
033: import de.finix.contelligent.Container;
034: import de.finix.contelligent.Contelligent;
035: import de.finix.contelligent.core.security.AccessControlList;
036: import de.finix.contelligent.event.ComponentEvent;
037: import de.finix.contelligent.event.ContelligentEvent;
038: import de.finix.contelligent.exception.ComponentPersistenceException;
039: import de.finix.contelligent.exception.ContelligentException;
040: import de.finix.contelligent.exception.ContelligentExceptionID;
041: import de.finix.contelligent.exception.TypeException;
042: import de.finix.contelligent.logging.LoggingService;
043: import de.finix.contelligent.persistence.ComponentPersistenceAdapter;
044: import de.finix.contelligent.persistence.LocalFileAdapter;
045: import de.finix.contelligent.xml.elements.SecurityElement;
046:
047: /**
048: * A <code>EditManager</code> is a {@link ComponentManager} which uses a tree
049: * of {@link NamedNode nodes} to cache any component. Note that this class has
050: * not been maintained or tested for a long time and usage will likely result in
051: * errors. Use LRUMapComponentManager instead.
052: */
053: public class EditManager extends BasicComponentManager {
054: final static org.apache.log4j.Logger log = LoggingService
055: .getLogger(EditManager.class);
056:
057: RootNode cacheRoot;
058:
059: /**
060: * Creates a new <code>EditManager</code> instance and initializes all
061: * adapters.
062: *
063: * @exception ContelligentException
064: * if an error occurs
065: */
066: EditManager(String fullName, BasicComponentManager parent,
067: Contelligent contelligent,
068: ComponentLockManager componentLockManager,
069: SecurityElement securityConfig,
070: ComponentFactory componentFactory,
071: ComponentPersistenceAdapter componentAdapter,
072: LocalFileAdapter fileAdapter, AccessControlList acl,
073: Map properties) throws ContelligentException {
074: super (fullName, parent, contelligent, componentLockManager,
075: securityConfig, componentFactory, componentAdapter,
076: fileAdapter, acl, properties);
077: cacheRoot = createRootNode();
078: log.info("'" + this .name + "':<init> - done.");
079: }
080:
081: public String toString() {
082: return ("[EditManager '" + name + "']");
083: }
084:
085: private RootNode createRootNode() {
086: Container rootContainer = super .getRootComponent();
087: RootNode rootNode = new RootNode(
088: ComponentPath.SEPARATOR_STRING, rootContainer, this );
089: ((ComponentContextImpl) rootContainer.getComponentContext())
090: .setNode(rootNode);
091: return rootNode;
092: }
093:
094: /**
095: * Overwrites {@link BasicComponentManager#retrieveComponent}. <BR>
096: * This implementation uses a tree of {@link NamedNode nodes} which cache
097: * any component using soft-references. To find a specific node the tree has
098: * to be traversed from the root node so the number of single map lookups
099: * depends on the depth of the path. Therefor it may be faster to use
100: * relative paths instead if the container to start from is known.
101: */
102: Component retrieveComponent(ComponentPath path,
103: Container parentContainer, CallData callData,
104: boolean followLinks) throws ComponentNotFoundException,
105: ComponentCreationException {
106: final boolean debugEnabled = log.isDebugEnabled();
107: NamedNode node = null;
108: Component component = null;
109: // if callData is null we can not resolve paths with a category token!
110: Map categoryMap = (callData == null ? Collections.EMPTY_MAP
111: : callData.getCategoryMap());
112: if (path.isAbsolute()) {
113: if (isRootPath(path)) {
114: return getRootComponent();
115: }
116: component = getClone(path);
117: if (component == null) {
118: node = cacheRoot.resolveNode(path, categoryMap);
119: }
120: } else {
121: if (parentContainer == null) {
122: throw new ComponentNotFoundException(
123: ContelligentExceptionID.component_notFound_relativePathWithoutParent,
124: path);
125: }
126: ComponentContextImpl parentCtx = (ComponentContextImpl) parentContainer
127: .getComponentContext();
128: ComponentPath fullPath = parentCtx.getPath().append(path);
129: component = getClone(fullPath);
130: if (component == null) {
131: NamedNode parentNode = parentCtx.getNode();
132: // assert(parentCtx.getPersistenceManager()==parentNode.getManager())
133: if (parentNode == null
134: || (this != parentNode.getManager())) {
135: // we cannot make a direct downstep if we have different
136: // managers involved:
137: if (debugEnabled) {
138: log
139: .debug("'"
140: + this .name
141: + "':retrieveComponent() - parent-container '"
142: + parentContainer
143: + "' lives in different server than this => make a full downstep!");
144: }
145: node = cacheRoot.resolveNode(fullPath, categoryMap);
146: // resolveNode() throws ComponentNotFoundException if path
147: // does not exist in ANY manager!
148: } else {
149: node = parentNode.resolveNode(path, categoryMap);
150: }
151: }
152: }
153:
154: if (component != null) {
155: if (debugEnabled)
156: log
157: .debug("'"
158: + this .name
159: + "':retrieveComponent() - returning clone for path '"
160: + path + "'.");
161: } else {
162: component = (Component) node.getContent();
163: if (component == null) {
164: if (debugEnabled) {
165: log.debug("'" + this .name
166: + "':retrieveComponent() - node '" + node
167: + "' doesn't have content for path '"
168: + node + "' => loading component ...");
169: }
170: // use node.getPath() here so we get an absolute path without
171: // category tokens!
172: ComponentPath fullPath = node.getPath();
173: try {
174: component = loadRawComponent(fullPath, callData);
175: initComponent(component, parentContainer);
176: } catch (TypeException e) {
177: throw new ComponentCreationException(
178: ContelligentExceptionID.component_creation_invalidType,
179: path, e);
180: } catch (ComponentPersistenceException e) {
181: throw new ComponentCreationException(
182: ContelligentExceptionID.component_creation_persistence,
183: fullPath, e);
184: } catch (ComponentNotFoundException e) {
185: if (hasParent()) {
186: return parent.retrieveComponent(path,
187: parentContainer, callData, followLinks);
188: } else {
189: throw e;
190: }
191: }
192: try {
193: component.postCreate();
194: ((ComponentContextImpl) component
195: .getComponentContext()).setNode(node);
196: node.setContent(component);
197: if (debugEnabled) {
198: log
199: .debug("'"
200: + this .name
201: + "':retrieveComponent() - successfully loaded component '"
202: + node + "' ...");
203: }
204: } catch (Exception e) {
205: log
206: .error(
207: "'"
208: + this .name
209: + "':retrieveComponent() - Exception while calling postCreate() on '"
210: + component + "': ", e);
211: throw new ComponentCreationException(
212: ContelligentExceptionID.component_creation_persistence,
213: fullPath, e);
214: }
215: }
216: }
217: // followLinks states whether or not to return proxies on ComponentLink
218: // interfaced components
219: if (followLinks && (component instanceof ComponentLink)) {
220: component = ((ComponentLink) component).getProxyInstance();
221: if (debugEnabled)
222: log
223: .debug("'"
224: + this .name
225: + "':retrieveComponent() - detected link at path '"
226: + path
227: + "', returning target componen [="
228: + component + "] instead.");
229: }
230: if (debugEnabled) {
231: log.debug("'" + this .name
232: + "':retrieveComponent() - end with " + component
233: + " of class " + component.getClass().getName());
234: }
235: return component;
236: }
237:
238: /**
239: * For each event which occured during the transaction the component
240: * associated with the events target-path gets invalidated, no matter which
241: * kind of event occured. (XXX: maybe we can ignore add events, rs) <BR>
242: * To avoid synchronization this manager always invalidates the highest
243: * common path of all changed paths. (Setting one node to null is an atomic
244: * operation) XXX: but we must synchronize put/get calls on the map of each
245: * node (rs)
246: */
247: public void afterCompletion(int status) {
248: try {
249: final boolean debugEnabled = log.isDebugEnabled();
250: List eventList = getEventList();
251: if (eventList == null || eventList.isEmpty()) {
252: if (debugEnabled)
253: log
254: .debug("'"
255: + this .name
256: + "':afterCompletion() - nothing changed during transaction.");
257: return;
258: }
259:
260: List changedPaths = new ArrayList(eventList.size());
261: Iterator events = eventList.iterator();
262: while (events.hasNext()) {
263: ContelligentEvent event = (ContelligentEvent) events
264: .next();
265: if (event instanceof ComponentEvent)
266: changedPaths.add(((ComponentEvent) event)
267: .getTargetPath());
268: }
269:
270: ComponentPath invalidatePath = null;
271: if (changedPaths.size() > 1) {
272: if (debugEnabled)
273: log
274: .debug("'"
275: + this .name
276: + "':afterCompletion() - calculating largest changed path using list "
277: + changedPaths + " ...");
278: ComponentPath[] changedPathsArray = new ComponentPath[changedPaths
279: .size()];
280: changedPaths.toArray(changedPathsArray);
281: invalidatePath = ComponentPath
282: .calculateLargestCommonPath(changedPathsArray);
283: // returns ROOT_PATH if no common path can be determined or if
284: // an error occurs
285: } else {
286: invalidatePath = (ComponentPath) changedPaths.get(0);
287: }
288:
289: if (debugEnabled)
290: log.debug("'" + this .name
291: + "':afterCompletion() - invalidating path '"
292: + invalidatePath + "' ...");
293: invalidatePath(invalidatePath, true);
294: } catch (Throwable t) {
295: log
296: .error(
297: "'"
298: + this .name
299: + "':afterCompletion() - Exception occured (ignored): ",
300: t);
301: } finally {
302: super .afterCompletion(status);
303: }
304: }
305:
306: void invalidatePath(ComponentPath path, boolean includeSubtree) {
307: // assert(isAbsolutePath(path))
308: if (log.isDebugEnabled())
309: log
310: .debug("'"
311: + this .name
312: + "':invalidatePath() - trying to invalidate path '"
313: + path + "' ...");
314:
315: if (isRootPath(path)) {
316: if (log.isDebugEnabled()) {
317: log
318: .debug("'"
319: + this .name
320: + "':invalidatePath() - given path ('"
321: + path
322: + "') is root-path -> invalidating the whole tree!");
323: }
324: cacheRoot = createRootNode();
325: } else {
326: if (!path.isAbsolute()) {
327: log
328: .error("'"
329: + this .name
330: + "':invalidatePath() - cannot handle relative path '"
331: + path + "' !");
332: return;
333: }
334: try {
335: // we may use an empty map here because paths to invalidate may
336: // never contain a category token
337: NamedNode parentNode = cacheRoot.resolveNode(path
338: .parentPath(), Collections.EMPTY_MAP);
339: String name = path.getName();
340: if (log.isDebugEnabled()) {
341: log
342: .debug("'"
343: + this .name
344: + "':invalidatePath() - invalidating child '"
345: + name + "' of node '" + parentNode
346: + "' ...");
347: }
348: parentNode.removeChild(name);
349: } catch (ComponentNotFoundException e) {
350: log
351: .warn("'"
352: + this .name
353: + "':invalidateComponentTree() - ComponentNotFoundException (ignored): "
354: + e);
355: // return silently ...
356: }
357: }
358: }
359:
360: }
|