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