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