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