KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > outerj > daisy > navigation > impl > CommonNavigationManager


1 /*
2  * Copyright 2004 Outerthought bvba and Schaubroeck nv
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.outerj.daisy.navigation.impl;
17
18 import org.outerj.daisy.navigation.*;
19 import org.outerj.daisy.repository.*;
20 import org.outerj.daisy.repository.variant.VariantManager;
21 import org.outerj.daisy.repository.acl.AclResultInfo;
22 import org.outerj.daisy.repository.acl.AclPermission;
23 import org.outerj.daisy.xmlutil.SaxBuffer;
24 import org.apache.avalon.framework.service.Serviceable;
25 import org.apache.avalon.framework.service.ServiceManager;
26 import org.apache.avalon.framework.service.ServiceException;
27 import org.apache.avalon.framework.configuration.Configurable;
28 import org.apache.avalon.framework.configuration.Configuration;
29 import org.apache.avalon.framework.configuration.ConfigurationException;
30 import org.apache.avalon.framework.activity.Initializable;
31 import org.apache.avalon.framework.activity.Disposable;
32 import org.apache.avalon.framework.logger.AbstractLogEnabled;
33 import org.apache.avalon.framework.logger.Logger;
34 import org.xml.sax.ContentHandler JavaDoc;
35 import org.xml.sax.SAXException JavaDoc;
36 import org.xml.sax.helpers.AttributesImpl JavaDoc;
37
38 import java.util.*;
39
40 /**
41  * Component providing the functionality to generate navigation trees. This component
42  * does not provide any service of itself, but registers an extension with the repository.
43  *
44  * @avalon.component version="1.0" name="navigation-manager" lifestyle="singleton"
45  */

46 public class CommonNavigationManager extends AbstractLogEnabled implements Serviceable,
47         Configurable, Initializable, Disposable {
48     private String JavaDoc repoUser;
49     private String JavaDoc repoPassword;
50     private Repository repository;
51     private VariantManager variantManager;
52     private ServiceManager serviceManager;
53     private ExtensionRegistrar extensionRegistrar;
54     private Map cachedNavigationTrees = new HashMap();
55     private Context context = new Context();
56     private CacheInvalidator cacheInvalidator = new CacheInvalidator();
57
58     public void configure(Configuration configuration) throws ConfigurationException {
59         Configuration repositoryUserConf = configuration.getChild("repositoryUser", false);
60         if (repositoryUserConf == null)
61             throw new ConfigurationException("Missing repositoryUser configuration element.");
62
63         repoUser = repositoryUserConf.getAttribute("login");
64         repoPassword = repositoryUserConf.getAttribute("password");
65     }
66
67     /**
68      * @avalon.dependency key="repository-manager" type="org.outerj.daisy.repository.RepositoryManager"
69      * @avalon.dependency key="extensionRegistrar" type="org.outerj.daisy.repository.ExtensionRegistrar"
70      */

71     public void service(ServiceManager serviceManager) throws ServiceException {
72         this.serviceManager = serviceManager;
73         this.extensionRegistrar = (ExtensionRegistrar)serviceManager.lookup("extensionRegistrar");
74     }
75
76     public void initialize() throws Exception JavaDoc {
77         RepositoryManager repositoryManager = (RepositoryManager)serviceManager.lookup("repository-manager");
78         try {
79             repository = repositoryManager.getRepository(new Credentials(repoUser, repoPassword));
80         } catch (Throwable JavaDoc e) {
81             throw new Exception JavaDoc("Problem getting repository.", e);
82         } finally {
83             serviceManager.release(repositoryManager);
84         }
85         repository.addListener(cacheInvalidator);
86
87         MyExtensionProvider extensionProvider = new MyExtensionProvider();
88         extensionRegistrar.registerExtension("NavigationManager", extensionProvider);
89
90         variantManager = repository.getVariantManager();
91     }
92
93     private class MyExtensionProvider implements ExtensionProvider {
94         public Object JavaDoc createExtension(Repository repository) {
95             return new NavigationManagerImpl(repository, CommonNavigationManager.this);
96         }
97     }
98
99     public void generateNavigationTree(ContentHandler JavaDoc contentHandler, NavigationParams navigationParams,
100             VariantKey activeDocument, boolean handleErrors, long userId, long[] roleIds)
101             throws NavigationException, SAXException JavaDoc {
102         SaxBuffer buffer = new SaxBuffer();
103
104         try {
105             NavigationTree navigationTree = getNavigation(navigationParams.getNavigationDoc(), navigationParams.getVersionMode());
106             Node rootNode = navigationTree.getNode();
107
108             if (getLogger().isDebugEnabled()) {
109                 getLogger().debug("Generating navigation tree with following params: navigationDoc = "
110                         + navigationParams.getNavigationDoc() + ", activePath = "+ navigationParams.getActivePath()
111                         + ", activeDocument = " + activeDocument + ", contextualized = "
112                         + navigationParams.getContextualized() + ", userId = " + userId + ", roleIds = " + arrayToString(roleIds));
113             }
114
115             List activeNodePath = null;
116             if (navigationParams.getActivePath() != null) {
117                 String JavaDoc[] path = splitPath(navigationParams.getActivePath());
118                 if (path.length > 0) {
119                     Node[] foundPath = new Node[path.length];
120                     long searchBranchId = activeDocument != null ? activeDocument.getBranchId() : navigationParams.getNavigationDoc().getBranchId();
121                     long searchLanguageId = activeDocument != null ? activeDocument.getLanguageId() : navigationParams.getNavigationDoc().getLanguageId();
122                     rootNode.searchPath(path, 0, searchBranchId, searchLanguageId, foundPath);
123
124                     // check if a full path has been found
125
for (int i = 0; i < foundPath.length; i++) {
126                         if (foundPath[i] == null) {
127                             foundPath = null;
128                             break;
129                         }
130                     }
131
132                     // check that the last element in the foundPath is the activeDocument
133
if (foundPath != null && activeDocument != null) {
134                         if (foundPath[foundPath.length - 1] instanceof DocumentRepresentingNode) {
135                             DocumentRepresentingNode node = (DocumentRepresentingNode)foundPath[foundPath.length - 1];
136                             if (!node.getVariantKey().equals(activeDocument)) {
137                                 foundPath = null;
138                             }
139                         } else {
140                             foundPath = null;
141                         }
142                     }
143
144                     if (foundPath != null)
145                         activeNodePath = Arrays.asList(foundPath);
146                 }
147             }
148
149             if (activeNodePath == null && activeDocument != null) {
150                 activeNodePath = rootNode.searchDocument(activeDocument);
151                 if (activeNodePath != null)
152                     Collections.reverse(activeNodePath);
153             }
154
155             // check if the user has read permissions to all the document nodes on the activeNodePath
156
// if not, set the activeNodePath to null
157
if (activeNodePath != null) {
158                 try {
159                     Iterator activeNodePathIt = activeNodePath.iterator();
160                     while (activeNodePathIt.hasNext()) {
161                         Object JavaDoc object = activeNodePathIt.next();
162                         if (object instanceof DocumentNode) {
163                             Document document = ((DocumentNode)object).getDocument();
164                             AclResultInfo aclResultInfo = repository.getAccessManager().getAclInfoOnLive(userId, roleIds, document);
165                             if (!aclResultInfo.isAllowed(AclPermission.READ_LIVE)) {
166                                 activeNodePath = null;
167                                 break;
168                             }
169                         }
170                     }
171                 } catch (Throwable JavaDoc e) {
172                     throw new NavigationException("Error checking document permissions.", e);
173                 }
174             }
175
176             // if there is an activeNodePath, produce a contextualized tree, otherwise generate a default tree
177
if (activeNodePath != null) {
178                 rootNode.generateXml(buffer != null ? buffer : contentHandler, (Node[])activeNodePath.toArray(new Node[activeNodePath.size()]), 0, navigationParams.getContextualized(), "", userId, roleIds);
179             } else {
180                 rootNode.generateXml(buffer != null ? buffer : contentHandler, navigationParams.getContextualized() ? 1 : -1, "", userId, roleIds);
181             }
182         } catch (Exception JavaDoc e) {
183             buffer = null;
184             if (handleErrors) {
185                 streamError(contentHandler, e);
186                 getLogger().error("Error generating navigation tree", e);
187             } else {
188                 throw new NavigationException("Error generating navigation tree.", e);
189             }
190         }
191         if (buffer != null) {
192             buffer.toSAX(contentHandler);
193         }
194     }
195
196     private String JavaDoc arrayToString(long[] values) {
197         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
198         for (int i = 0; i < values.length; i++) {
199             if (i > 0)
200                 buffer.append(",");
201             buffer.append(values[i]);
202         }
203         return buffer.toString();
204     }
205
206     public void generatePreviewNavigationTree(ContentHandler JavaDoc contentHandler, String JavaDoc navigationTreeXml, long branchId, long languageId, long userId, long[] roleIds) throws NavigationException, SAXException JavaDoc {
207         try {
208             Node node = new RootNode(NavigationFactory.build(navigationTreeXml, branchId, languageId, NavigationVersionMode.LIVE, context, new Counter(), new Stack()));
209             node.generateXml(contentHandler, -1, "", userId, roleIds);
210         } catch (Exception JavaDoc e) {
211             throw new NavigationException("Error generating navigation tree.", e);
212         }
213     }
214
215     public NavigationLookupResult lookup(String JavaDoc navigationPath, long requestedBranchId, long requestedLanguageId,
216             LookupAlternative[] lookupAlternatives, long userId, long[] activeRoleIds) throws NavigationException {
217         if (lookupAlternatives.length < 1)
218             throw new IllegalArgumentException JavaDoc("lookupAlternatives array should contain at least one entry.");
219
220         try {
221             NavigationTree navigationTree = getNavigation(lookupAlternatives[0].getNavigationDoc(), lookupAlternatives[0].getVersionMode());
222             NavigationLookupResult navigationLookupResult;
223
224             String JavaDoc[] path = splitPath(navigationPath);
225             if (path.length > 0) {
226                 // Note: the special handling with the navException is to try to recover in case of navigation tree
227
// errors, so that lookups (and thus document access) continues to work.
228
Exception JavaDoc navException = null;
229                 Node[] foundPath = new Node[path.length];
230                 try {
231                     long searchBranchId = requestedBranchId != -1 ? requestedBranchId : lookupAlternatives[0].getNavigationDoc().getBranchId();
232                     long searchLanguageId = requestedLanguageId != -1 ? requestedLanguageId : lookupAlternatives[0].getNavigationDoc().getLanguageId();
233                     navigationTree.getNode().searchPath(path, 0, searchBranchId, searchLanguageId, foundPath);
234                 } catch (Exception JavaDoc e) {
235                     navException = e;
236                 }
237
238                 // check if a full path has been found
239
for (int i = 0; i < foundPath.length; i++) {
240                     if (foundPath[i] == null) {
241                         foundPath = null;
242                         break;
243                     }
244                 }
245
246                 // navigationPath is in some cases used as part of the returned result, and for
247
// consistency we want that to always start with a slash
248
if (navigationPath.charAt(0) != '/')
249                     navigationPath = "/" + navigationPath;
250
251                 if (foundPath == null) {
252                     String JavaDoc lastPathPart = path[path.length - 1];
253                     if (isNumeric(lastPathPart)) {
254                         long documentID = Long.parseLong(lastPathPart);
255                         navigationLookupResult = searchLookupAlternatives(documentID, requestedBranchId, requestedLanguageId, lookupAlternatives);
256                     } else {
257                         if (navException != null)
258                             throw navException;
259                         else
260                             navigationLookupResult = NavigationLookupResult.createNotFoundResult();
261                     }
262                 } else {
263                     Object JavaDoc node = foundPath[foundPath.length - 1];
264                     if (node instanceof GroupNode) {
265                         String JavaDoc docPath = ((GroupNode)node).findFirstDocumentNode(navigationPath, userId, activeRoleIds);
266                         if (docPath != null) {
267                             navigationLookupResult = NavigationLookupResult.createRedirectResult(lookupAlternatives[0].getName(), docPath, null);
268                         } else {
269                             navigationLookupResult = NavigationLookupResult.createNotFoundResult();
270                         }
271                     } else if (node instanceof DocumentRepresentingNode) {
272                         VariantKey variantKey = ((DocumentRepresentingNode)node).getVariantKey();
273                         if ((requestedBranchId != -1 && variantKey.getBranchId() != requestedBranchId)
274                                 || (requestedLanguageId != -1 && variantKey.getLanguageId() != requestedLanguageId)) {
275                             navigationLookupResult = searchLookupAlternatives(variantKey.getDocumentId(), requestedBranchId, requestedLanguageId, lookupAlternatives);
276                         } else {
277                             navigationLookupResult = NavigationLookupResult.createMatchResult(((DocumentRepresentingNode)node).getVariantKey(), navigationPath);
278                         }
279                     } else {
280                         navigationLookupResult = NavigationLookupResult.createNotFoundResult();
281                     }
282                 }
283             } else {
284                 navigationLookupResult = NavigationLookupResult.createNotFoundResult();
285             }
286
287             return navigationLookupResult;
288         } catch (Exception JavaDoc e) {
289             throw new NavigationException("Error in NavigationManager.", e);
290         }
291     }
292
293     private NavigationLookupResult searchLookupAlternatives(long docId, long requestedBranchId, long requestedLanguageId, LookupAlternative[] lookupAlternatives) {
294         // Determine branch and language
295
long branchId = requestedBranchId != -1 ? requestedBranchId : lookupAlternatives[0].getNavigationDoc().getBranchId();
296         long languageId = requestedLanguageId != -1 ? requestedLanguageId : lookupAlternatives[0].getNavigationDoc().getLanguageId();
297         VariantKey variantKey = new VariantKey(docId, branchId, languageId);
298
299         // Get the collections of the document
300
long[] collectionIds = null;
301         try {
302             DocumentCollection[] collections = repository.getDocument(variantKey, false).getCollections().getArray();
303             collectionIds = new long[collections.length];
304             for (int i = 0; i < collections.length; i++)
305                 collectionIds[i] = collections[i].getId();
306             Arrays.sort(collectionIds);
307         } catch (Throwable JavaDoc e) {
308             // ignore
309
}
310
311         NavigationLookupResult navigationLookupResult = null;
312         if (collectionIds == null) {
313             // If getting the collections failed.
314
String JavaDoc redirectPath = null;
315             try {
316                 NavigationTree navigationTree = getNavigation(lookupAlternatives[0].getNavigationDoc(), lookupAlternatives[0].getVersionMode());
317                 redirectPath = navigationTree.lookupNode(variantKey);
318             } catch (Throwable JavaDoc e) {
319                 // error in navtree, ignore
320
}
321             if (redirectPath != null)
322                 navigationLookupResult = NavigationLookupResult.createRedirectResult(lookupAlternatives[0].getName(), redirectPath, variantKey);
323             else
324                 navigationLookupResult = NavigationLookupResult.createMatchResult(variantKey, "");
325         } else {
326             LookupAlternative firstMatch = null;
327             for (int i = 0; i < lookupAlternatives.length; i++) {
328                 LookupAlternative lookupAlternative = lookupAlternatives[i];
329                 VariantKey altNavDoc = lookupAlternative.getNavigationDoc();
330
331                 String JavaDoc redirectPath = null;
332                 try {
333                     NavigationTree altNavigationTree = getNavigation(lookupAlternative.getNavigationDoc(), lookupAlternative.getVersionMode());
334                     redirectPath = altNavigationTree.lookupNode(variantKey);
335                 } catch (Throwable JavaDoc e) {
336                     // error in navtree, ignore
337
}
338                 if (redirectPath != null) {
339                     navigationLookupResult = NavigationLookupResult.createRedirectResult(lookupAlternative.getName(), redirectPath, variantKey);
340                     break;
341                 }
342
343                 if (branchId == altNavDoc.getBranchId() && languageId == altNavDoc.getLanguageId()
344                         && Arrays.binarySearch(collectionIds, lookupAlternative.getCollectionId()) >= 0) {
345                     if (firstMatch == null)
346                         firstMatch = lookupAlternative;
347                 }
348             }
349             if (navigationLookupResult == null) {
350                 if (firstMatch == null)
351                     firstMatch = lookupAlternatives[0];
352                 if (firstMatch.getName().equals(lookupAlternatives[0].getName())) {
353                     // we're already in the right lookup alternative
354
navigationLookupResult = NavigationLookupResult.createMatchResult(variantKey, "");
355                 } else {
356                     navigationLookupResult = NavigationLookupResult.createRedirectResult(firstMatch.getName(), "/" + variantKey.getDocumentId(), variantKey);
357                 }
358             }
359         }
360         return navigationLookupResult;
361     }
362
363     public String JavaDoc reverseLookup(VariantKey document, VariantKey navigationDoc, NavigationVersionMode versionMode) throws RepositoryException {
364         NavigationTree navigationTree = getNavigation(navigationDoc, versionMode);
365         return navigationTree.lookupNode(document);
366     }
367
368     private boolean isNumeric(String JavaDoc text) {
369         for (int i = 0; i < text.length(); i++) {
370             char c = text.charAt(i);
371             if (!(c >= '0' && c <= '9'))
372                 return false;
373         }
374         return true;
375     }
376
377     private void streamError(ContentHandler JavaDoc contentHandler, Exception JavaDoc e) throws SAXException JavaDoc {
378         contentHandler.startDocument();
379         contentHandler.startPrefixMapping("", Node.NAVIGATION_NS);
380         contentHandler.startElement(Node.NAVIGATION_NS, "navigationTree", "navigationTree", new AttributesImpl JavaDoc());
381         contentHandler.startElement(Node.NAVIGATION_NS, "navigationTreeError", "navigationTreeError", new AttributesImpl JavaDoc());
382         contentHandler.endElement(Node.NAVIGATION_NS, "navigationTreeError", "navigationTreeError");
383         contentHandler.endElement(Node.NAVIGATION_NS, "navigationTree", "navigationTree");
384         contentHandler.endPrefixMapping("");
385         contentHandler.endDocument();
386     }
387
388     private String JavaDoc[] splitPath(String JavaDoc path) {
389         StringTokenizer tokenizer = new StringTokenizer(path, "/");
390         String JavaDoc[] parts = new String JavaDoc[tokenizer.countTokens()];
391
392         for (int i = 0; i < parts.length; i++) {
393             parts[i] = tokenizer.nextToken();
394         }
395
396         return parts;
397     }
398
399     private NavigationTree getNavigation(VariantKey navigationDoc, NavigationVersionMode versionMode) {
400         NavigationCacheKey cacheKey = new NavigationCacheKey(navigationDoc, versionMode);
401         synchronized(cachedNavigationTrees) {
402             NavigationTree navigationTree = (NavigationTree)cachedNavigationTrees.get(cacheKey);
403             if (navigationTree == null) {
404                 navigationTree = new NavigationTree(new RootNode(new BuildingNode(navigationDoc, versionMode, context)));
405                 cachedNavigationTrees.put(cacheKey, navigationTree);
406             }
407             return navigationTree;
408         }
409     }
410
411     private static class NavigationCacheKey {
412         private final VariantKey navigationDoc;
413         private final NavigationVersionMode versionMode;
414         private final String JavaDoc asString;
415
416         public NavigationCacheKey(VariantKey navigationDoc, NavigationVersionMode versionMode) {
417             this.navigationDoc = navigationDoc;
418             this.versionMode = versionMode;
419             this.asString = navigationDoc.toString() + versionMode.toString();
420         }
421
422         public String JavaDoc toString() {
423             return asString;
424         }
425
426         public int hashCode() {
427             return asString.hashCode();
428         }
429
430         public boolean equals(Object JavaDoc obj) {
431             NavigationCacheKey other = (NavigationCacheKey)obj;
432             return this.versionMode == other.versionMode && this.navigationDoc.equals(other.navigationDoc);
433         }
434     }
435
436     private void invalidateCachedTrees() {
437         synchronized(cachedNavigationTrees) {
438             cachedNavigationTrees.clear();
439         }
440     }
441
442     public void dispose() {
443         repository.removeListener(cacheInvalidator);
444     }
445
446     private class CacheInvalidator implements RepositoryListener {
447         public void repositoryEvent(RepositoryEventType eventType, long id, long updateCount) {
448             if (eventType == RepositoryEventType.DOCUMENT_CREATED || eventType == RepositoryEventType.DOCUMENT_UPDATED
449                     || eventType == RepositoryEventType.DOCUMENT_DELETED) {
450                 invalidateCachedTrees();
451             }
452         }
453
454         public void variantEvent(DocumentVariantEventType eventType, long documentId, long branchId, long languageId, long updateCount) {
455             // lock changes don't invalidate the navigation trees, all other events do
456
if (eventType != DocumentVariantEventType.LOCK_CHANGE)
457                 invalidateCachedTrees();
458         }
459     }
460
461     public class Context {
462         private Context() {
463             // private constructor to prevent public construction
464
}
465
466         public Repository getRepository() {
467             return repository;
468         }
469
470         public boolean canReadLive(VariantKey document, long userId, long[] roleIds) throws RepositoryException {
471             AclResultInfo aclResultInfo = repository.getAccessManager().getAclInfoOnLive(userId, roleIds, document);
472             return aclResultInfo.isAllowed(AclPermission.READ_LIVE);
473         }
474
475         public boolean canRead(VariantKey document, long userId, long[] roleIds) throws RepositoryException {
476             AclResultInfo aclResultInfo = repository.getAccessManager().getAclInfoOnLive(userId, roleIds, document);
477             return aclResultInfo.isAllowed(AclPermission.READ_LIVE);
478         }
479
480         public String JavaDoc getBranchName(long branchId) throws RepositoryException {
481             return variantManager.getBranch(branchId, false).getName();
482         }
483
484         public String JavaDoc getLanguageName(long languageId) throws RepositoryException {
485             return variantManager.getLanguage(languageId, false).getName();
486         }
487
488         public long getBranchId(String JavaDoc name) throws RepositoryException {
489             return variantManager.getBranch(name, false).getId();
490         }
491
492         public long getLanguageId(String JavaDoc name) throws RepositoryException {
493             return variantManager.getLanguage(name, false).getId();
494         }
495
496         public Logger getLogger() {
497             return CommonNavigationManager.this.getLogger();
498         }
499     }
500 }
501
Popular Tags