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.IOException; 022import java.io.Serializable; 023import java.util.Arrays; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029import org.nuxeo.apidoc.adapters.BundleGroupDocAdapter; 030import org.nuxeo.apidoc.adapters.BundleInfoDocAdapter; 031import org.nuxeo.apidoc.adapters.ComponentInfoDocAdapter; 032import org.nuxeo.apidoc.adapters.ExtensionInfoDocAdapter; 033import org.nuxeo.apidoc.adapters.ExtensionPointInfoDocAdapter; 034import org.nuxeo.apidoc.adapters.OperationInfoDocAdapter; 035import org.nuxeo.apidoc.adapters.SeamComponentInfoDocAdapter; 036import org.nuxeo.apidoc.adapters.ServiceInfoDocAdapter; 037import org.nuxeo.apidoc.api.BundleGroup; 038import org.nuxeo.apidoc.api.BundleInfo; 039import org.nuxeo.apidoc.api.ComponentInfo; 040import org.nuxeo.apidoc.api.DocumentationItem; 041import org.nuxeo.apidoc.api.ExtensionInfo; 042import org.nuxeo.apidoc.api.ExtensionPointInfo; 043import org.nuxeo.apidoc.api.NuxeoArtifact; 044import org.nuxeo.apidoc.api.OperationInfo; 045import org.nuxeo.apidoc.api.SeamComponentInfo; 046import org.nuxeo.apidoc.api.ServiceInfo; 047import org.nuxeo.apidoc.documentation.DocumentationItemDocAdapter; 048import org.nuxeo.apidoc.documentation.DocumentationService; 049import org.nuxeo.apidoc.documentation.ResourceDocumentationItem; 050import org.nuxeo.apidoc.introspection.BundleGroupImpl; 051import org.nuxeo.apidoc.introspection.BundleInfoImpl; 052import org.nuxeo.apidoc.introspection.OperationInfoImpl; 053import org.nuxeo.apidoc.snapshot.DistributionSnapshot; 054import org.nuxeo.apidoc.snapshot.SnapshotFilter; 055import org.nuxeo.ecm.automation.OperationException; 056import org.nuxeo.ecm.core.api.Blob; 057import org.nuxeo.ecm.core.api.Blobs; 058import org.nuxeo.ecm.core.api.CoreSession; 059import org.nuxeo.ecm.core.api.DocumentModel; 060import org.nuxeo.ecm.core.api.DocumentRef; 061import org.nuxeo.ecm.core.api.NuxeoException; 062import org.nuxeo.ecm.core.api.PathRef; 063import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 064import org.nuxeo.ecm.core.api.security.ACE; 065import org.nuxeo.ecm.core.api.security.ACL; 066import org.nuxeo.ecm.core.api.security.ACP; 067import org.nuxeo.ecm.core.api.security.impl.ACLImpl; 068import org.nuxeo.runtime.api.Framework; 069 070public class SnapshotPersister { 071 072 public static final String Root_PATH = "/"; 073 074 public static final String Root_NAME = "nuxeo-distributions"; 075 076 public static final String Seam_Root_NAME = "Seam"; 077 078 public static final String Operation_Root_NAME = "Automation"; 079 080 public static final String Bundle_Root_NAME = "Bundles"; 081 082 public static final String Read_Grp = "Everyone"; 083 084 public static final String Write_Grp = "members"; 085 086 protected static final Log log = LogFactory.getLog(SnapshotPersister.class); 087 088 class UnrestrictedRootCreator extends UnrestrictedSessionRunner { 089 090 protected DocumentRef rootRef; 091 092 protected final String parentPath; 093 094 protected final String name; 095 096 protected final boolean setAcl; 097 098 public UnrestrictedRootCreator(CoreSession session, String parentPath, String name, boolean setAcl) { 099 super(session); 100 this.name = name; 101 this.parentPath = parentPath; 102 this.setAcl = setAcl; 103 } 104 105 public DocumentRef getRootRef() { 106 return rootRef; 107 } 108 109 @Override 110 public void run() { 111 112 DocumentModel root = session.createDocumentModel(parentPath, name, "Workspace"); 113 root.setProperty("dublincore", "title", name); 114 root = session.createDocument(root); 115 116 if (setAcl) { 117 ACL acl = new ACLImpl(); 118 acl.add(new ACE(Write_Grp, "Write", true)); 119 acl.add(new ACE(Read_Grp, "Read", true)); 120 ACP acp = root.getACP(); 121 acp.addACL(acl); 122 session.setACP(root.getRef(), acp, true); 123 } 124 125 rootRef = root.getRef(); 126 // flush caches 127 session.save(); 128 } 129 130 } 131 132 public DocumentModel getSubRoot(CoreSession session, DocumentModel root, String name) { 133 134 DocumentRef rootRef = new PathRef(root.getPathAsString() + name); 135 if (session.exists(rootRef)) { 136 return session.getDocument(rootRef); 137 } 138 UnrestrictedRootCreator creator = new UnrestrictedRootCreator(session, root.getPathAsString(), name, false); 139 creator.runUnrestricted(); 140 // flush caches 141 session.save(); 142 return session.getDocument(creator.getRootRef()); 143 } 144 145 public DocumentModel getDistributionRoot(CoreSession session) { 146 DocumentRef rootRef = new PathRef(Root_PATH + Root_NAME); 147 if (session.exists(rootRef)) { 148 return session.getDocument(rootRef); 149 } 150 UnrestrictedRootCreator creator = new UnrestrictedRootCreator(session, Root_PATH, Root_NAME, true); 151 creator.runUnrestricted(); 152 // flush caches 153 session.save(); 154 return session.getDocument(creator.getRootRef()); 155 } 156 157 public DistributionSnapshot persist(DistributionSnapshot snapshot, CoreSession session, String label, 158 SnapshotFilter filter) { 159 160 RepositoryDistributionSnapshot distribContainer = createDistributionDoc(snapshot, session, label); 161 162 if (filter == null) { 163 // If no filter, clean old entries 164 distribContainer.cleanPreviousArtifacts(); 165 } 166 167 DocumentModel bundleContainer = getSubRoot(session, distribContainer.getDoc(), Bundle_Root_NAME); 168 169 if (filter != null) { 170 // create VGroup that contain,s only the target bundles 171 BundleGroupImpl vGroup = new BundleGroupImpl(filter.getBundleGroupName(), snapshot.getVersion()); 172 for (String bundleId : snapshot.getBundleIds()) { 173 if (filter.includeBundleId(bundleId)) { 174 vGroup.add(bundleId); 175 } 176 } 177 persistBundleGroup(snapshot, vGroup, session, label + "-bundles", bundleContainer); 178 } else { 179 List<BundleGroup> bundleGroups = snapshot.getBundleGroups(); 180 for (BundleGroup bundleGroup : bundleGroups) { 181 persistBundleGroup(snapshot, bundleGroup, session, label, bundleContainer); 182 } 183 } 184 185 DocumentModel seamContainer = getSubRoot(session, distribContainer.getDoc(), Seam_Root_NAME); 186 persistSeamComponents(snapshot, snapshot.getSeamComponents(), session, label, seamContainer, filter); 187 188 DocumentModel opContainer = getSubRoot(session, distribContainer.getDoc(), Operation_Root_NAME); 189 persistOperations(snapshot, snapshot.getOperations(), session, label, opContainer, filter); 190 191 return distribContainer; 192 } 193 194 public void persistSeamComponents(DistributionSnapshot snapshot, List<SeamComponentInfo> seamComponents, 195 CoreSession session, String label, DocumentModel parent, SnapshotFilter filter) { 196 for (SeamComponentInfo seamComponent : seamComponents) { 197 if (filter == null || filter.includeSeamComponent(seamComponent)) { 198 persistSeamComponent(snapshot, seamComponent, session, label, parent); 199 } 200 } 201 } 202 203 public void persistSeamComponent(DistributionSnapshot snapshot, SeamComponentInfo seamComponent, 204 CoreSession session, String label, DocumentModel parent) { 205 SeamComponentInfoDocAdapter.create(seamComponent, session, parent.getPathAsString()); 206 } 207 208 public void persistOperations(DistributionSnapshot snapshot, List<OperationInfo> operations, CoreSession session, 209 String label, DocumentModel parent, SnapshotFilter filter) { 210 for (OperationInfo op : operations) { 211 if (filter == null || (op instanceof OperationInfoImpl && filter.includeOperation((OperationInfoImpl) op))) { 212 persistOperation(snapshot, op, session, label, parent); 213 } 214 } 215 } 216 217 public void persistOperation(DistributionSnapshot snapshot, OperationInfo op, CoreSession session, String label, 218 DocumentModel parent) { 219 OperationInfoDocAdapter.create(op, session, parent.getPathAsString()); 220 } 221 222 public void persistBundleGroup(DistributionSnapshot snapshot, BundleGroup bundleGroup, CoreSession session, 223 String label, DocumentModel parent) { 224 log.info("Persist bundle group " + bundleGroup.getId()); 225 226 DocumentModel bundleGroupDoc = createBundleGroupDoc(bundleGroup, session, label, parent); 227 228 // save GitHub doc 229 if (bundleGroup instanceof BundleGroupImpl) { 230 Map<String, ResourceDocumentationItem> liveDoc = ((BundleGroupImpl) bundleGroup).getLiveDoc(); 231 if (liveDoc != null && liveDoc.size() > 0) { 232 persistLiveDoc(liveDoc, bundleGroupDoc.getAdapter(BundleGroup.class), session); 233 } 234 } 235 236 for (String bundleId : bundleGroup.getBundleIds()) { 237 BundleInfo bi = snapshot.getBundle(bundleId); 238 persistBundle(snapshot, bi, session, label, bundleGroupDoc); 239 } 240 241 for (BundleGroup subGroup : bundleGroup.getSubGroups()) { 242 persistBundleGroup(snapshot, subGroup, session, label, bundleGroupDoc); 243 } 244 } 245 246 protected void persistLiveDoc(Map<String, ResourceDocumentationItem> liveDoc, NuxeoArtifact item, 247 CoreSession session) { 248 DocumentationService ds = Framework.getLocalService(DocumentationService.class); 249 List<DocumentationItem> existingDocs = ds.findDocumentItems(session, item); 250 for (String cat : liveDoc.keySet()) { 251 ResourceDocumentationItem docItem = liveDoc.get(cat); 252 DocumentationItem previousDocItem = null; 253 for (DocumentationItem exiting : existingDocs) { 254 if (exiting.getTitle().equals(docItem.getTitle()) && exiting.getTarget().equals(docItem.getTarget())) { 255 previousDocItem = exiting; 256 break; 257 } 258 } 259 if (previousDocItem == null) { 260 ds.createDocumentationItem(session, item, docItem.getTitle(), docItem.getContent(), cat, 261 Arrays.asList(item.getVersion()), docItem.isApproved(), docItem.getRenderingType()); 262 } else { 263 if (previousDocItem instanceof DocumentationItemDocAdapter) { 264 DocumentationItemDocAdapter existingDoc = (DocumentationItemDocAdapter) previousDocItem; 265 Blob blob = Blobs.createBlob(docItem.getContent()); 266 Blob oldBlob = (Blob) existingDoc.getDocumentModel().getPropertyValue("file:content"); 267 blob.setFilename(oldBlob.getFilename()); 268 existingDoc.getDocumentModel().setPropertyValue("file:content", (Serializable) blob); 269 ds.updateDocumentationItem(session, existingDoc); 270 } 271 272 } 273 } 274 } 275 276 public void persistBundle(DistributionSnapshot snapshot, BundleInfo bundleInfo, CoreSession session, String label, 277 DocumentModel parent) { 278 log.info("Persist bundle " + bundleInfo.getId()); 279 280 DocumentModel bundleDoc = createBundleDoc(snapshot, session, label, bundleInfo, parent); 281 282 // save GitHub doc 283 if (bundleInfo instanceof BundleInfoImpl) { 284 Map<String, ResourceDocumentationItem> liveDoc = ((BundleInfoImpl) bundleInfo).getLiveDoc(); 285 if (liveDoc != null && liveDoc.size() > 0) { 286 persistLiveDoc(liveDoc, bundleDoc.getAdapter(BundleInfo.class), session); 287 } 288 } 289 290 for (ComponentInfo ci : bundleInfo.getComponents()) { 291 persistComponent(snapshot, ci, session, label, bundleDoc); 292 } 293 } 294 295 public void persistComponent(DistributionSnapshot snapshot, ComponentInfo ci, CoreSession session, String label, 296 DocumentModel parent) { 297 298 DocumentModel componentDoc = createComponentDoc(snapshot, session, label, ci, parent); 299 300 for (ExtensionPointInfo epi : ci.getExtensionPoints()) { 301 createExtensionPointDoc(snapshot, session, label, epi, componentDoc); 302 } 303 for (ExtensionInfo ei : ci.getExtensions()) { 304 createContributionDoc(snapshot, session, label, ei, componentDoc); 305 } 306 307 for (ServiceInfo si : ci.getServices()) { 308 createServiceDoc(snapshot, session, label, si, componentDoc); 309 } 310 } 311 312 protected DocumentModel createContributionDoc(DistributionSnapshot snapshot, CoreSession session, String label, 313 ExtensionInfo ei, DocumentModel parent) { 314 return ExtensionInfoDocAdapter.create(ei, session, parent.getPathAsString()).getDoc(); 315 } 316 317 protected DocumentModel createServiceDoc(DistributionSnapshot snapshot, CoreSession session, String label, 318 ServiceInfo si, DocumentModel parent) { 319 return ServiceInfoDocAdapter.create(si, session, parent.getPathAsString()).getDoc(); 320 } 321 322 protected DocumentModel createExtensionPointDoc(DistributionSnapshot snapshot, CoreSession session, String label, 323 ExtensionPointInfo epi, DocumentModel parent) { 324 return ExtensionPointInfoDocAdapter.create(epi, session, parent.getPathAsString()).getDoc(); 325 } 326 327 protected DocumentModel createComponentDoc(DistributionSnapshot snapshot, CoreSession session, String label, 328 ComponentInfo ci, DocumentModel parent) { 329 try { 330 return ComponentInfoDocAdapter.create(ci, session, parent.getPathAsString()).getDoc(); 331 } catch (IOException e) { 332 throw new NuxeoException("Unable to create Component Doc", e); 333 } 334 } 335 336 protected DocumentModel createBundleDoc(DistributionSnapshot snapshot, CoreSession session, String label, 337 BundleInfo bi, DocumentModel parent) { 338 return BundleInfoDocAdapter.create(bi, session, parent.getPathAsString()).getDoc(); 339 } 340 341 protected RepositoryDistributionSnapshot createDistributionDoc(DistributionSnapshot snapshot, CoreSession session, 342 String label) { 343 return RepositoryDistributionSnapshot.create(snapshot, session, getDistributionRoot(session).getPathAsString(), 344 label); 345 } 346 347 protected DocumentModel createBundleGroupDoc(BundleGroup bundleGroup, CoreSession session, String label, 348 DocumentModel parent) { 349 return BundleGroupDocAdapter.create(bundleGroup, session, parent.getPathAsString()).getDoc(); 350 } 351 352}