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.repository; 020 021import java.io.Serializable; 022import java.util.ArrayList; 023import java.util.Calendar; 024import java.util.Date; 025import java.util.HashSet; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029import java.util.function.Function; 030import java.util.stream.Collectors; 031 032import org.nuxeo.apidoc.adapters.BaseNuxeoArtifactDocAdapter; 033import org.nuxeo.apidoc.api.BundleGroup; 034import org.nuxeo.apidoc.api.BundleInfo; 035import org.nuxeo.apidoc.api.ComponentInfo; 036import org.nuxeo.apidoc.api.ExtensionInfo; 037import org.nuxeo.apidoc.api.ExtensionPointInfo; 038import org.nuxeo.apidoc.api.NuxeoArtifact; 039import org.nuxeo.apidoc.api.OperationInfo; 040import org.nuxeo.apidoc.api.QueryHelper; 041import org.nuxeo.apidoc.api.SeamComponentInfo; 042import org.nuxeo.apidoc.api.ServiceInfo; 043import org.nuxeo.apidoc.documentation.JavaDocHelper; 044import org.nuxeo.apidoc.introspection.ServerInfo; 045import org.nuxeo.apidoc.snapshot.DistributionSnapshot; 046import org.nuxeo.common.utils.Path; 047import org.nuxeo.ecm.core.api.CoreSession; 048import org.nuxeo.ecm.core.api.DocumentModel; 049import org.nuxeo.ecm.core.api.DocumentModelList; 050import org.nuxeo.ecm.core.api.DocumentRef; 051import org.nuxeo.ecm.core.api.PathRef; 052import org.nuxeo.ecm.core.api.PropertyException; 053import org.nuxeo.ecm.core.query.sql.NXQL; 054 055public class RepositoryDistributionSnapshot extends BaseNuxeoArtifactDocAdapter implements DistributionSnapshot { 056 057 protected JavaDocHelper jdocHelper = null; 058 059 public static RepositoryDistributionSnapshot create(DistributionSnapshot distrib, CoreSession session, 060 String containerPath, String label, Map<String, Serializable> properties) { 061 DocumentModel doc = session.createDocumentModel(TYPE_NAME); 062 String name = computeDocumentName(distrib.getKey()); 063 if (label != null) { 064 name = computeDocumentName(label); 065 } 066 String targetPath = new Path(containerPath).append(name).toString(); 067 068 boolean exist = false; 069 if (session.exists(new PathRef(targetPath))) { 070 exist = true; 071 doc = session.getDocument(new PathRef(targetPath)); 072 } 073 074 // Set first properties passed by parameter to not override default 075 // behavior 076 if (properties != null) { 077 properties.forEach(doc::setPropertyValue); 078 } 079 080 doc.setPathInfo(containerPath, name); 081 if (label == null) { 082 doc.setPropertyValue("dc:title", distrib.getKey()); 083 doc.setPropertyValue(PROP_KEY, distrib.getKey()); 084 doc.setPropertyValue(PROP_NAME, distrib.getName()); 085 } else { 086 doc.setPropertyValue("dc:title", label); 087 doc.setPropertyValue(PROP_KEY, label + "-" + distrib.getVersion()); 088 doc.setPropertyValue(PROP_NAME, label); 089 } 090 doc.setPropertyValue(PROP_LATEST_FT, distrib.isLatestFT()); 091 doc.setPropertyValue(PROP_LATEST_LTS, distrib.isLatestLTS()); 092 doc.setPropertyValue(PROP_VERSION, distrib.getVersion()); 093 094 DocumentModel ret; 095 if (exist) { 096 ret = session.saveDocument(doc); 097 } else { 098 ret = session.createDocument(doc); 099 } 100 return new RepositoryDistributionSnapshot(ret); 101 } 102 103 public static List<DistributionSnapshot> readPersistentSnapshots(CoreSession session) { 104 List<DistributionSnapshot> result = new ArrayList<>(); 105 String query = "SELECT * FROM " + TYPE_NAME + " where ecm:isTrashed = 0 AND ecm:isVersion = 0"; 106 DocumentModelList docs = session.query(query); 107 for (DocumentModel child : docs) { 108 DistributionSnapshot ob = child.getAdapter(DistributionSnapshot.class); 109 if (ob != null) { 110 result.add(ob); 111 } 112 } 113 return result; 114 } 115 116 public RepositoryDistributionSnapshot(DocumentModel doc) { 117 super(doc); 118 } 119 120 protected <T> List<T> getChildren(Class<T> adapter, String docType) { 121 List<T> result = new ArrayList<>(); 122 String query = QueryHelper.select(docType, doc); 123 DocumentModelList docs = getCoreSession().query(query); 124 for (DocumentModel child : docs) { 125 T ob = child.getAdapter(adapter); 126 if (ob != null) { 127 result.add(ob); 128 } 129 } 130 return result; 131 } 132 133 protected <T> T getChild(Class<T> adapter, String docType, String idField, String id) { 134 String query = QueryHelper.select(docType, doc) + " AND " + idField + " = " + NXQL.escapeString(id); 135 DocumentModelList docs = getCoreSession().query(query); 136 if (docs.isEmpty()) { 137 log.error("Unable to find " + docType + " for id " + id); 138 } else if (docs.size() == 1) { 139 return docs.get(0).getAdapter(adapter); 140 } else { 141 log.error("multiple match for " + docType + " for id " + id); 142 return docs.get(0).getAdapter(adapter); 143 } 144 return null; 145 } 146 147 @Override 148 public BundleInfo getBundle(String id) { 149 return getChild(BundleInfo.class, BundleInfo.TYPE_NAME, BundleInfo.PROP_BUNDLE_ID, id); 150 } 151 152 @Override 153 public BundleGroup getBundleGroup(String groupId) { 154 return getChild(BundleGroup.class, BundleGroup.TYPE_NAME, BundleGroup.PROP_KEY, groupId); 155 } 156 157 protected DocumentModel getBundleContainer() { 158 DocumentRef ref = new PathRef(doc.getPathAsString(), SnapshotPersister.Bundle_Root_NAME); 159 if (getCoreSession().exists(ref)) { 160 return getCoreSession().getDocument(ref); 161 } else { 162 // for compatibility with the previous persistence model 163 return doc; 164 } 165 } 166 167 @Override 168 public List<BundleGroup> getBundleGroups() { 169 List<BundleGroup> grps = new ArrayList<>(); 170 String query = QueryHelper.select(BundleGroup.TYPE_NAME, doc, NXQL.ECM_PARENTID, getBundleContainer().getId()); 171 DocumentModelList docs = getCoreSession().query(query); 172 for (DocumentModel child : docs) { 173 BundleGroup bg = child.getAdapter(BundleGroup.class); 174 if (bg != null) { 175 grps.add(bg); 176 } 177 } 178 return grps; 179 } 180 181 @Override 182 public List<String> getBundleIds() { 183 return getChildren(BundleInfo.class, BundleInfo.TYPE_NAME).stream().map(NuxeoArtifact::getId).collect( 184 Collectors.toList()); 185 } 186 187 @Override 188 public ComponentInfo getComponent(String id) { 189 return getChild(ComponentInfo.class, ComponentInfo.TYPE_NAME, ComponentInfo.PROP_COMPONENT_ID, id); 190 } 191 192 @Override 193 public List<String> getComponentIds() { 194 return getChildren(ComponentInfo.class, ComponentInfo.TYPE_NAME).stream().map(NuxeoArtifact::getId).collect( 195 Collectors.toList()); 196 } 197 198 @Override 199 public ExtensionInfo getContribution(String id) { 200 return getChild(ExtensionInfo.class, ExtensionInfo.TYPE_NAME, ExtensionInfo.PROP_CONTRIB_ID, id); 201 } 202 203 @Override 204 public List<String> getContributionIds() { 205 return getChildren(ExtensionInfo.class, ExtensionInfo.TYPE_NAME).stream().map(NuxeoArtifact::getId).collect( 206 Collectors.toList()); 207 } 208 209 @Override 210 public List<ExtensionInfo> getContributions() { 211 return getChildren(ExtensionInfo.class, ExtensionInfo.TYPE_NAME); 212 } 213 214 @Override 215 public ExtensionPointInfo getExtensionPoint(String id) { 216 return getChild(ExtensionPointInfo.class, ExtensionPointInfo.TYPE_NAME, ExtensionPointInfo.PROP_EP_ID, id); 217 } 218 219 @Override 220 public List<String> getExtensionPointIds() { 221 return getChildren(ExtensionPointInfo.class, ExtensionPointInfo.TYPE_NAME).stream() 222 .map(NuxeoArtifact::getId) 223 .collect(Collectors.toList()); 224 } 225 226 @Override 227 public List<String> getBundleGroupChildren(String groupId) { 228 BundleGroup bg = getChild(BundleGroup.class, BundleGroup.TYPE_NAME, BundleGroup.PROP_KEY, groupId); 229 return bg.getBundleIds(); 230 } 231 232 public List<String> getBundleGroupIds() { 233 return getChildren(BundleGroup.class, BundleGroup.TYPE_NAME).stream().map(NuxeoArtifact::getId).collect( 234 Collectors.toList()); 235 } 236 237 @Override 238 public List<String> getServiceIds() { 239 Set<String> ids = new HashSet<>(); 240 String query = QueryHelper.select(ComponentInfo.TYPE_NAME, doc); 241 DocumentModelList components = getCoreSession().query(query); 242 for (DocumentModel componentDoc : components) { 243 ComponentInfo ci = componentDoc.getAdapter(ComponentInfo.class); 244 if (ci != null) { 245 ids.addAll(ci.getServiceNames()); 246 } 247 } 248 return new ArrayList<>(ids); 249 } 250 251 @Override 252 public String getName() { 253 try { 254 return (String) doc.getPropertyValue(PROP_NAME); 255 } catch (PropertyException e) { 256 log.error("Error while reading nxdistribution:name", e); 257 return "!unknown!"; 258 } 259 } 260 261 @Override 262 public String getVersion() { 263 try { 264 return (String) doc.getPropertyValue(PROP_VERSION); 265 } catch (PropertyException e) { 266 log.error("Error while reading nxdistribution:version", e); 267 return "!unknown!"; 268 } 269 } 270 271 @Override 272 public String getKey() { 273 try { 274 return (String) doc.getPropertyValue(PROP_KEY); 275 } catch (PropertyException e) { 276 log.error("Error while reading nxdistribution:key", e); 277 return "!unknown!"; 278 } 279 } 280 281 @Override 282 public List<Class<?>> getSpi() { 283 return null; 284 } 285 286 @Override 287 public String getId() { 288 return getKey(); 289 } 290 291 @Override 292 public String getArtifactType() { 293 return TYPE_NAME; 294 } 295 296 @Override 297 public ServiceInfo getService(String id) { 298 // Select only not overriden ticket and old imported NXService without overriden value 299 String query = QueryHelper.select(ServiceInfo.TYPE_NAME, getDoc()) + " AND " + ServiceInfo.PROP_CLASS_NAME 300 + " = " + NXQL.escapeString(id) + " AND (" + ServiceInfo.PROP_OVERRIDEN + " = 0 OR " 301 + ServiceInfo.PROP_OVERRIDEN + " is NULL)"; 302 DocumentModelList docs = getCoreSession().query(query); 303 if (docs.size() > 1) { 304 throw new AssertionError("Multiple services found for " + id); 305 } 306 return docs.get(0).getAdapter(ServiceInfo.class); 307 } 308 309 @Override 310 public List<String> getJavaComponentIds() { 311 return getChildren(ComponentInfo.class, ComponentInfo.TYPE_NAME).stream() 312 .filter(ci -> !ci.isXmlPureComponent()) 313 .map(NuxeoArtifact::getId) 314 .collect(Collectors.toList()); 315 } 316 317 @Override 318 public List<String> getXmlComponentIds() { 319 return getChildren(ComponentInfo.class, ComponentInfo.TYPE_NAME).stream() 320 .filter(ComponentInfo::isXmlPureComponent) 321 .map(NuxeoArtifact::getId) 322 .collect(Collectors.toList()); 323 } 324 325 @Override 326 public Date getCreationDate() { 327 try { 328 Calendar cal = (Calendar) getDoc().getPropertyValue("dc:created"); 329 return cal == null ? null : cal.getTime(); 330 } catch (PropertyException e) { 331 return null; 332 } 333 } 334 335 @Override 336 public Date getReleaseDate() { 337 try { 338 Calendar cal = (Calendar) getDoc().getPropertyValue("nxdistribution:released"); 339 return cal == null ? getCreationDate() : cal.getTime(); 340 } catch (PropertyException e) { 341 return null; 342 } 343 } 344 345 @Override 346 public boolean isLive() { 347 return false; 348 } 349 350 @Override 351 public SeamComponentInfo getSeamComponent(String id) { 352 String name = id.replace("seam:", ""); 353 String query = QueryHelper.select(SeamComponentInfo.TYPE_NAME, getDoc()) + " AND " 354 + SeamComponentInfo.PROP_COMPONENT_NAME + " = " + NXQL.escapeString(name); 355 DocumentModelList docs = getCoreSession().query(query); 356 return docs.isEmpty() ? null : docs.get(0).getAdapter(SeamComponentInfo.class); 357 } 358 359 @Override 360 public List<String> getSeamComponentIds() { 361 List<String> result = new ArrayList<>(); 362 String query = QueryHelper.select(SeamComponentInfo.TYPE_NAME, getDoc()); 363 DocumentModelList docs = getCoreSession().query(query); 364 for (DocumentModel doc : docs) { 365 result.add(doc.getAdapter(SeamComponentInfo.class).getId()); 366 } 367 return result; 368 } 369 370 @Override 371 public List<SeamComponentInfo> getSeamComponents() { 372 List<SeamComponentInfo> result = new ArrayList<>(); 373 String query = QueryHelper.select(SeamComponentInfo.TYPE_NAME, getDoc()); 374 DocumentModelList docs = getCoreSession().query(query); 375 for (DocumentModel doc : docs) { 376 result.add(doc.getAdapter(SeamComponentInfo.class)); 377 } 378 return result; 379 } 380 381 @Override 382 public boolean containsSeamComponents() { 383 return getSeamComponentIds().size() > 0; 384 } 385 386 @Override 387 public OperationInfo getOperation(String id) { 388 if (id.startsWith(OperationInfo.ARTIFACT_PREFIX)) { 389 id = id.substring(OperationInfo.ARTIFACT_PREFIX.length()); 390 } 391 String query = QueryHelper.select(OperationInfo.TYPE_NAME, getDoc()) + " AND " + OperationInfo.PROP_NAME + " = " 392 + NXQL.escapeString(id) + " OR " + OperationInfo.PROP_ALIASES + " = " + NXQL.escapeString(id); 393 DocumentModelList docs = getCoreSession().query(query); 394 return docs.isEmpty() ? null : docs.get(0).getAdapter(OperationInfo.class); 395 } 396 397 @Override 398 public List<OperationInfo> getOperations() { 399 List<OperationInfo> result = new ArrayList<>(); 400 String query = QueryHelper.select(OperationInfo.TYPE_NAME, getDoc()); 401 DocumentModelList docs = getCoreSession().query(query); 402 for (DocumentModel doc : docs) { 403 result.add(doc.getAdapter(OperationInfo.class)); 404 } 405 // TODO sort 406 return result; 407 } 408 409 public JavaDocHelper getJavaDocHelper() { 410 if (jdocHelper == null) { 411 jdocHelper = JavaDocHelper.getHelper(getName(), getVersion()); 412 } 413 return jdocHelper; 414 } 415 416 @Override 417 public void cleanPreviousArtifacts() { 418 String query = QueryHelper.select("Document", getDoc()); 419 List<DocumentRef> refs = new ArrayList<>(); 420 DocumentModelList docs = getCoreSession().query(query); 421 for (DocumentModel doc : docs) { 422 refs.add(doc.getRef()); 423 } 424 getCoreSession().removeDocuments(refs.toArray(new DocumentRef[refs.size()])); 425 } 426 427 @Override 428 public boolean isLatestFT() { 429 try { 430 return (Boolean) doc.getPropertyValue(PROP_LATEST_FT); 431 } catch (PropertyException e) { 432 log.error("Error while reading nxdistribution:latestFT", e); 433 return false; 434 } 435 } 436 437 @Override 438 public boolean isLatestLTS() { 439 try { 440 return (Boolean) doc.getPropertyValue(PROP_LATEST_LTS); 441 } catch (PropertyException e) { 442 log.error("Error while reading nxdistribution:latestLTS", e); 443 return false; 444 } 445 } 446 447 @Override 448 public List<String> getAliases() { 449 @SuppressWarnings("unchecked") 450 List<String> aliases = (List<String>) doc.getPropertyValue(PROP_ALIASES); 451 if (isLatestLTS()) { 452 aliases.add("latestLTS"); 453 } else if (isLatestFT()) { 454 aliases.add("latestFT"); 455 } 456 return aliases; 457 } 458 459 @Override 460 public boolean isHidden() { 461 return Boolean.TRUE.equals(doc.getPropertyValue(PROP_HIDE)); 462 } 463 464 @Override 465 public ServerInfo getServerInfo() { 466 throw new UnsupportedOperationException(); 467 } 468}