001/* 002 * (C) Copyright 2006-2010 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Thierry Delprat 018 */ 019package org.nuxeo.apidoc.introspection; 020 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Collections; 024import java.util.Date; 025import java.util.HashMap; 026import java.util.LinkedList; 027import java.util.List; 028import java.util.Map; 029 030import javax.servlet.http.HttpServletRequest; 031 032import org.nuxeo.apidoc.api.BaseNuxeoArtifact; 033import org.nuxeo.apidoc.api.BundleGroup; 034import org.nuxeo.apidoc.api.BundleGroupFlatTree; 035import org.nuxeo.apidoc.api.BundleGroupTreeHelper; 036import org.nuxeo.apidoc.api.BundleInfo; 037import org.nuxeo.apidoc.api.ComponentInfo; 038import org.nuxeo.apidoc.api.ExtensionInfo; 039import org.nuxeo.apidoc.api.ExtensionPointInfo; 040import org.nuxeo.apidoc.api.OperationInfo; 041import org.nuxeo.apidoc.api.SeamComponentInfo; 042import org.nuxeo.apidoc.api.ServiceInfo; 043import org.nuxeo.apidoc.documentation.JavaDocHelper; 044import org.nuxeo.apidoc.seam.SeamRuntimeIntrospector; 045import org.nuxeo.apidoc.snapshot.DistributionSnapshot; 046import org.nuxeo.ecm.automation.AutomationService; 047import org.nuxeo.ecm.automation.OperationDocumentation; 048import org.nuxeo.ecm.automation.OperationException; 049import org.nuxeo.ecm.automation.OperationType; 050import org.nuxeo.ecm.core.api.NuxeoException; 051import org.nuxeo.runtime.api.Framework; 052 053import com.fasterxml.jackson.annotation.JsonCreator; 054import com.fasterxml.jackson.annotation.JsonIgnore; 055import com.fasterxml.jackson.annotation.JsonProperty; 056 057public class RuntimeSnapshot extends BaseNuxeoArtifact implements DistributionSnapshot { 058 059 public static final String VIRTUAL_BUNDLE_GROUP = "grp:org.nuxeo.misc"; 060 061 protected ServerInfo serverInfo; 062 063 protected Date created; 064 065 protected final List<String> bundleIds = new ArrayList<>(); 066 067 protected final List<String> javaComponentsIds = new ArrayList<>(); 068 069 protected final Map<String, String> components2Bundles = new HashMap<>(); 070 071 protected final Map<String, String> services2Components = new HashMap<>(); 072 073 protected final Map<String, ExtensionPointInfo> extensionPoints = new HashMap<>(); 074 075 protected final Map<String, ExtensionInfo> contributions = new HashMap<>(); 076 077 protected final Map<String, List<String>> mavenGroups = new HashMap<>(); 078 079 protected final Map<String, List<String>> mavenSubGroups = new HashMap<>(); 080 081 protected final List<BundleGroup> bundleGroups = new ArrayList<>(); 082 083 protected boolean seamInitialized = false; 084 085 protected List<SeamComponentInfo> seamComponents = new ArrayList<>(); 086 087 protected boolean opsInitialized = false; 088 089 protected final List<OperationInfo> operations = new ArrayList<>(); 090 091 protected JavaDocHelper jdocHelper; 092 093 protected final List<Class<?>> spi = new ArrayList<>(); 094 095 public static RuntimeSnapshot build() { 096 return new RuntimeSnapshot(); 097 } 098 099 @JsonCreator 100 private RuntimeSnapshot(@JsonProperty("serverInfo") ServerInfo serverInfo, @JsonProperty("creationDate") Date created, 101 @JsonProperty("seamComponents") List<SeamComponentInfo> seamComponents, 102 @JsonProperty("operations") List<OperationInfo> operations) { 103 this.serverInfo = serverInfo; 104 this.created = created; 105 index(); 106 this.seamComponents.addAll(seamComponents); 107 this.operations.addAll(operations); 108 } 109 110 protected RuntimeSnapshot() { 111 serverInfo = ServerInfo.build(); 112 created = new Date(); 113 114 index(); 115 } 116 117 @Override 118 public ServerInfo getServerInfo() { 119 return serverInfo; 120 } 121 122 @Override 123 @JsonIgnore 124 public String getVersion() { 125 return serverInfo.getVersion(); 126 } 127 128 @Override 129 @JsonIgnore 130 public String getName() { 131 return serverInfo.getName(); 132 } 133 134 protected void index() { 135 spi.addAll(serverInfo.getAllSpi()); 136 for (BundleInfo bInfo : serverInfo.getBundles()) { 137 bundleIds.add(bInfo.getId()); 138 139 String groupId = bInfo.getGroupId(); 140 if (groupId != null) { 141 groupId = "grp:" + groupId; 142 } 143 String artifactId = bInfo.getArtifactId(); 144 145 if (groupId == null || artifactId == null) { 146 groupId = VIRTUAL_BUNDLE_GROUP; 147 ((BundleInfoImpl) bInfo).setGroupId(groupId); 148 } 149 if (!mavenGroups.containsKey(groupId)) { 150 mavenGroups.put(groupId, new ArrayList<String>()); 151 } 152 mavenGroups.get(groupId).add(bInfo.getId()); 153 154 for (ComponentInfo cInfo : bInfo.getComponents()) { 155 components2Bundles.put(cInfo.getId(), bInfo.getId()); 156 if (!cInfo.isXmlPureComponent()) { 157 javaComponentsIds.add(cInfo.getId()); 158 } 159 160 for (ServiceInfo sInfo : cInfo.getServices()) { 161 if (sInfo.isOverriden()) { 162 continue; 163 } 164 services2Components.put(sInfo.getId(), cInfo.getId()); 165 } 166 167 for (ExtensionPointInfo epi : cInfo.getExtensionPoints()) { 168 extensionPoints.put(epi.getId(), epi); 169 } 170 171 for (ExtensionInfo ei : cInfo.getExtensions()) { 172 contributions.put(ei.getId(), ei); 173 } 174 } 175 } 176 // post process bundle groups 177 List<String> mvnGroupNames = new ArrayList<>(); 178 mvnGroupNames.addAll(mavenGroups.keySet()); 179 180 for (String mvnGroupName : mvnGroupNames) { 181 List<String> artifactIds = mavenGroups.get(mvnGroupName); 182 Collections.sort(artifactIds); 183 184 List<String> subGroups = new ArrayList<>(); 185 186 for (String id : artifactIds) { 187 if (id.endsWith(".api")) { 188 String grp = "grp:" + id.substring(0, id.length() - 4); 189 190 if (grp.equals(mvnGroupName)) { 191 continue; 192 } 193 194 subGroups.add(grp); 195 } 196 } 197 198 if (subGroups.size() < 2) { 199 // no need to split the maven group into subGroups 200 } else { 201 for (String grp : subGroups) { 202 List<String> grpArtifactIds = new ArrayList<>(); 203 for (String aid : artifactIds) { 204 if (aid.startsWith(grp) || ("grp:" + aid).startsWith(grp)) { 205 grpArtifactIds.add(aid); 206 } 207 } 208 if (grpArtifactIds.size() > 0) { 209 for (String aid : grpArtifactIds) { 210 artifactIds.remove(aid); 211 } 212 mavenSubGroups.put(grp, grpArtifactIds); 213 artifactIds.add(grp); 214 } 215 } 216 } 217 } 218 219 for (String grpId : mavenGroups.keySet()) { 220 BundleGroupImpl bGroup = buildBundleGroup(grpId, serverInfo.getVersion()); 221 bundleGroups.add(bGroup); 222 } 223 224 } 225 226 protected BundleGroupImpl buildBundleGroup(String id, String version) { 227 BundleGroupImpl bGroup = new BundleGroupImpl(id, version); 228 for (String aid : getBundleGroupChildren(id)) { 229 if (aid.startsWith("grp:")) { 230 BundleGroupImpl newGroup = buildBundleGroup(aid, version); 231 bGroup.add(newGroup); 232 newGroup.addParent(bGroup.getId()); 233 } else { 234 bGroup.add(aid); 235 ((BundleInfoImpl) getBundle(aid)).setBundleGroup(bGroup); 236 BundleInfo bi = getBundle(aid); 237 bGroup.addLiveDoc(bi.getParentLiveDoc()); 238 } 239 } 240 return bGroup; 241 } 242 243 @Override 244 @JsonIgnore 245 public List<BundleGroup> getBundleGroups() { 246 return bundleGroups; 247 } 248 249 @Override 250 public BundleGroup getBundleGroup(String groupId) { 251 BundleGroupTreeHelper bgth = new BundleGroupTreeHelper(this); 252 List<BundleGroupFlatTree> tree = bgth.getBundleGroupTree(); 253 254 for (BundleGroupFlatTree info : tree) { 255 if (info.getGroup().getId().equals(groupId)) { 256 return info.getGroup(); 257 } 258 } 259 if (!groupId.startsWith("grp:")) { 260 return getBundleGroup("grp:" + groupId); 261 } 262 return null; 263 } 264 265 protected void browseBundleGroup(BundleGroup group, int level, List<BundleGroupFlatTree> tree) { 266 BundleGroupFlatTree info = new BundleGroupFlatTree(group, level); 267 tree.add(info); 268 269 for (BundleGroup subGroup : group.getSubGroups()) { 270 browseBundleGroup(subGroup, level + 1, tree); 271 } 272 } 273 274 @Override 275 @JsonIgnore 276 public List<String> getBundleIds() { 277 List<String> bundlesIds = new ArrayList<>(); 278 279 for (BundleInfo info : serverInfo.getBundles()) { 280 bundlesIds.add(info.getId()); 281 282 } 283 Collections.sort(bundlesIds); 284 return bundlesIds; 285 } 286 287 @Override 288 public BundleInfo getBundle(String id) { 289 return serverInfo.getBundle(id); 290 } 291 292 @Override 293 @JsonIgnore 294 public List<String> getComponentIds() { 295 List<String> componentsIds = new ArrayList<>(); 296 componentsIds.addAll(components2Bundles.keySet()); 297 Collections.sort(componentsIds); 298 return componentsIds; 299 } 300 301 @Override 302 public ComponentInfo getComponent(String id) { 303 String bundleId = components2Bundles.get(id); 304 if (bundleId == null) { 305 return null; 306 } 307 BundleInfo bi = getBundle(bundleId); 308 309 for (ComponentInfo ci : bi.getComponents()) { 310 if (ci.getId().equals(id)) { 311 return ci; 312 } 313 } 314 return null; 315 } 316 317 @Override 318 @JsonIgnore 319 public List<String> getServiceIds() { 320 List<String> serviceIds = new ArrayList<>(); 321 serviceIds.addAll(services2Components.keySet()); 322 Collections.sort(serviceIds); 323 return serviceIds; 324 } 325 326 @Override 327 @JsonIgnore 328 public List<String> getExtensionPointIds() { 329 List<String> epIds = new ArrayList<>(); 330 epIds.addAll(extensionPoints.keySet()); 331 Collections.sort(epIds); 332 return epIds; 333 } 334 335 @Override 336 @JsonIgnore 337 public ExtensionPointInfo getExtensionPoint(String id) { 338 return extensionPoints.get(id); 339 } 340 341 @Override 342 @JsonIgnore 343 public List<String> getContributionIds() { 344 List<String> contribIds = new ArrayList<>(); 345 contribIds.addAll(contributions.keySet()); 346 Collections.sort(contribIds); 347 return contribIds; 348 } 349 350 @Override 351 @JsonIgnore 352 public List<ExtensionInfo> getContributions() { 353 List<ExtensionInfo> contribs = new ArrayList<>(); 354 contribs.addAll(contributions.values()); 355 // TODO sort 356 return contribs; 357 } 358 359 @Override 360 public ExtensionInfo getContribution(String id) { 361 return contributions.get(id); 362 } 363 364 public List<String> getBundleGroupIds() { 365 List<String> grpIds = new ArrayList<>(); 366 grpIds.addAll(mavenGroups.keySet()); 367 Collections.sort(grpIds); 368 return grpIds; 369 } 370 371 @Override 372 public List<String> getBundleGroupChildren(String groupId) { 373 List<String> res = mavenSubGroups.get(groupId); 374 if (res == null) { 375 // String grpId = groupId.substring(4); 376 res = mavenGroups.get(groupId); 377 } 378 379 if (res != null) { 380 return res; 381 } else { 382 return new ArrayList<>(); 383 } 384 } 385 386 @Override 387 @JsonIgnore 388 public String getKey() { 389 return getName() + "-" + getVersion(); 390 } 391 392 @Override 393 @JsonIgnore 394 public List<Class<?>> getSpi() { 395 return spi; 396 } 397 398 @Override 399 @JsonIgnore 400 public String getId() { 401 return getKey(); 402 } 403 404 @Override 405 @JsonIgnore 406 public String getArtifactType() { 407 return TYPE_NAME; 408 } 409 410 @Override 411 public ServiceInfo getService(String id) { 412 String cId = services2Components.get(id); 413 if (cId == null) { 414 return null; 415 } 416 417 for (ServiceInfo si : getComponent(cId).getServices()) { 418 if (id.equals(si.getId())) { 419 return si; 420 } 421 } 422 return null; 423 } 424 425 @Override 426 @JsonIgnore 427 public List<String> getJavaComponentIds() { 428 return javaComponentsIds; 429 } 430 431 @Override 432 @JsonIgnore 433 public List<String> getXmlComponentIds() { 434 List<String> result = new ArrayList<>(); 435 436 for (String cId : getComponentIds()) { 437 if (!javaComponentsIds.contains(cId)) { 438 result.add(cId); 439 } 440 } 441 return result; 442 } 443 444 @Override 445 public Date getCreationDate() { 446 return created; 447 } 448 449 @Override 450 @JsonIgnore 451 public Date getReleaseDate() { 452 return null; 453 } 454 455 @Override 456 @JsonIgnore 457 public boolean isLive() { 458 return true; 459 } 460 461 @Override 462 @JsonIgnore 463 public String getHierarchyPath() { 464 return null; 465 } 466 467 public void initSeamComponents(HttpServletRequest request) { 468 if (seamInitialized) { 469 return; 470 } 471 seamComponents = SeamRuntimeIntrospector.listNuxeoComponents(request); 472 for (SeamComponentInfo seamComp : seamComponents) { 473 ((SeamComponentInfoImpl) seamComp).setVersion(getVersion()); 474 } 475 seamInitialized = true; 476 } 477 478 public void initOperations() { 479 if (opsInitialized) { 480 return; 481 } 482 AutomationService service = Framework.getService(AutomationService.class); 483 if (service == null) { 484 return; 485 } 486 OperationType[] ops = service.getOperations(); 487 for (OperationType op : ops) { 488 OperationDocumentation documentation; 489 try { 490 documentation = op.getDocumentation(); 491 } catch (OperationException e) { 492 throw new NuxeoException(e); 493 } 494 operations.add(new OperationInfoImpl(documentation, getVersion(), op.getType().getCanonicalName(), 495 op.getContributingComponent())); 496 } 497 opsInitialized = true; 498 } 499 500 @Override 501 public SeamComponentInfo getSeamComponent(String id) { 502 for (SeamComponentInfo sci : getSeamComponents()) { 503 if (sci.getId().equals(id)) { 504 return sci; 505 } 506 } 507 return null; 508 } 509 510 @Override 511 @JsonIgnore 512 public List<String> getSeamComponentIds() { 513 List<String> ids = new ArrayList<>(); 514 for (SeamComponentInfo sci : getSeamComponents()) { 515 ids.add(sci.getId()); 516 } 517 return ids; 518 } 519 520 @Override 521 public List<SeamComponentInfo> getSeamComponents() { 522 return seamComponents; 523 } 524 525 @Override 526 public boolean containsSeamComponents() { 527 return getSeamComponentIds().size() > 0; 528 } 529 530 @Override 531 public OperationInfo getOperation(String id) { 532 if (id.startsWith(OperationInfo.ARTIFACT_PREFIX)) { 533 id = id.substring(OperationInfo.ARTIFACT_PREFIX.length()); 534 } 535 for (OperationInfo op : getOperations()) { 536 if (op.getName().equals(id)) { 537 return op; 538 } 539 } 540 return null; 541 } 542 543 @Override 544 public List<OperationInfo> getOperations() { 545 initOperations(); 546 return operations; 547 } 548 549 public JavaDocHelper getJavaDocHelper() { 550 if (jdocHelper == null) { 551 jdocHelper = JavaDocHelper.getHelper(getName(), getVersion()); 552 } 553 return jdocHelper; 554 } 555 556 @Override 557 public void cleanPreviousArtifacts() { 558 // Can't delete anything in a runtime Snapshot 559 throw new UnsupportedOperationException(); 560 } 561 562 @Override 563 @JsonIgnore 564 public boolean isLatestFT() { 565 return false; 566 } 567 568 @Override 569 @JsonIgnore 570 public boolean isLatestLTS() { 571 return false; 572 } 573 574 final List<String> aliases = new LinkedList<>(Arrays.asList("current")); 575 576 @Override 577 @JsonIgnore 578 public List<String> getAliases() { 579 return aliases; 580 } 581 582 @Override 583 @JsonIgnore 584 public boolean isHidden() { 585 return false; 586 } 587}