1 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 ; 35 import org.xml.sax.SAXException ; 36 import org.xml.sax.helpers.AttributesImpl ; 37 38 import java.util.*; 39 40 46 public class CommonNavigationManager extends AbstractLogEnabled implements Serviceable, 47 Configurable, Initializable, Disposable { 48 private String repoUser; 49 private String 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 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 { 77 RepositoryManager repositoryManager = (RepositoryManager)serviceManager.lookup("repository-manager"); 78 try { 79 repository = repositoryManager.getRepository(new Credentials(repoUser, repoPassword)); 80 } catch (Throwable e) { 81 throw new Exception ("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 createExtension(Repository repository) { 95 return new NavigationManagerImpl(repository, CommonNavigationManager.this); 96 } 97 } 98 99 public void generateNavigationTree(ContentHandler contentHandler, NavigationParams navigationParams, 100 VariantKey activeDocument, boolean handleErrors, long userId, long[] roleIds) 101 throws NavigationException, SAXException { 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 [] 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 for (int i = 0; i < foundPath.length; i++) { 126 if (foundPath[i] == null) { 127 foundPath = null; 128 break; 129 } 130 } 131 132 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 if (activeNodePath != null) { 158 try { 159 Iterator activeNodePathIt = activeNodePath.iterator(); 160 while (activeNodePathIt.hasNext()) { 161 Object 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 e) { 172 throw new NavigationException("Error checking document permissions.", e); 173 } 174 } 175 176 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 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 arrayToString(long[] values) { 197 StringBuffer buffer = new StringBuffer (); 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 contentHandler, String navigationTreeXml, long branchId, long languageId, long userId, long[] roleIds) throws NavigationException, SAXException { 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 e) { 211 throw new NavigationException("Error generating navigation tree.", e); 212 } 213 } 214 215 public NavigationLookupResult lookup(String navigationPath, long requestedBranchId, long requestedLanguageId, 216 LookupAlternative[] lookupAlternatives, long userId, long[] activeRoleIds) throws NavigationException { 217 if (lookupAlternatives.length < 1) 218 throw new IllegalArgumentException ("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 [] path = splitPath(navigationPath); 225 if (path.length > 0) { 226 Exception 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 e) { 235 navException = e; 236 } 237 238 for (int i = 0; i < foundPath.length; i++) { 240 if (foundPath[i] == null) { 241 foundPath = null; 242 break; 243 } 244 } 245 246 if (navigationPath.charAt(0) != '/') 249 navigationPath = "/" + navigationPath; 250 251 if (foundPath == null) { 252 String 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 node = foundPath[foundPath.length - 1]; 264 if (node instanceof GroupNode) { 265 String 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 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 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 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 e) { 308 } 310 311 NavigationLookupResult navigationLookupResult = null; 312 if (collectionIds == null) { 313 String redirectPath = null; 315 try { 316 NavigationTree navigationTree = getNavigation(lookupAlternatives[0].getNavigationDoc(), lookupAlternatives[0].getVersionMode()); 317 redirectPath = navigationTree.lookupNode(variantKey); 318 } catch (Throwable e) { 319 } 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 redirectPath = null; 332 try { 333 NavigationTree altNavigationTree = getNavigation(lookupAlternative.getNavigationDoc(), lookupAlternative.getVersionMode()); 334 redirectPath = altNavigationTree.lookupNode(variantKey); 335 } catch (Throwable e) { 336 } 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 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 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 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 contentHandler, Exception e) throws SAXException { 378 contentHandler.startDocument(); 379 contentHandler.startPrefixMapping("", Node.NAVIGATION_NS); 380 contentHandler.startElement(Node.NAVIGATION_NS, "navigationTree", "navigationTree", new AttributesImpl ()); 381 contentHandler.startElement(Node.NAVIGATION_NS, "navigationTreeError", "navigationTreeError", new AttributesImpl ()); 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 [] splitPath(String path) { 389 StringTokenizer tokenizer = new StringTokenizer(path, "/"); 390 String [] parts = new String [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 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 toString() { 423 return asString; 424 } 425 426 public int hashCode() { 427 return asString.hashCode(); 428 } 429 430 public boolean equals(Object 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 if (eventType != DocumentVariantEventType.LOCK_CHANGE) 457 invalidateCachedTrees(); 458 } 459 } 460 461 public class Context { 462 private Context() { 463 } 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 getBranchName(long branchId) throws RepositoryException { 481 return variantManager.getBranch(branchId, false).getName(); 482 } 483 484 public String getLanguageName(long languageId) throws RepositoryException { 485 return variantManager.getLanguage(languageId, false).getName(); 486 } 487 488 public long getBranchId(String name) throws RepositoryException { 489 return variantManager.getBranch(name, false).getId(); 490 } 491 492 public long getLanguageId(String 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 |