001/* 002 * (C) Copyright 2006-20012 Nuxeo SAS (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 * Nuxeo - initial API and implementation 016 * 017 */ 018 019package org.nuxeo.template.samples.importer; 020 021import java.io.File; 022import java.io.FileFilter; 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.Calendar; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029 030import org.apache.commons.logging.Log; 031import org.apache.commons.logging.LogFactory; 032import org.dom4j.Attribute; 033import org.dom4j.CDATA; 034import org.dom4j.Comment; 035import org.dom4j.Document; 036import org.dom4j.DocumentType; 037import org.dom4j.Element; 038import org.dom4j.Entity; 039import org.dom4j.Namespace; 040import org.dom4j.ProcessingInstruction; 041import org.dom4j.Text; 042import org.dom4j.Visitor; 043import org.nuxeo.common.utils.Path; 044import org.nuxeo.ecm.core.api.CoreSession; 045import org.nuxeo.ecm.core.api.DocumentModel; 046import org.nuxeo.ecm.core.api.DocumentModelList; 047import org.nuxeo.ecm.core.api.DocumentRef; 048import org.nuxeo.ecm.core.api.NuxeoException; 049import org.nuxeo.ecm.core.api.PathRef; 050import org.nuxeo.ecm.core.io.DocumentPipe; 051import org.nuxeo.ecm.core.io.DocumentReader; 052import org.nuxeo.ecm.core.io.DocumentTransformer; 053import org.nuxeo.ecm.core.io.DocumentTranslationMap; 054import org.nuxeo.ecm.core.io.DocumentWriter; 055import org.nuxeo.ecm.core.io.ExportedDocument; 056import org.nuxeo.ecm.core.io.impl.DocumentPipeImpl; 057import org.nuxeo.ecm.core.io.impl.plugins.DocumentModelWriter; 058import org.nuxeo.ecm.platform.audit.api.AuditLogger; 059import org.nuxeo.ecm.platform.audit.api.AuditReader; 060import org.nuxeo.ecm.platform.audit.api.LogEntry; 061import org.nuxeo.runtime.api.Framework; 062 063/** 064 * Imports models and samples from resources or filesystem via CoreIO. 065 * <p> 066 * The association between template and document is translated during the IO import (because UUIDs change). 067 * 068 * @author <a href="mailto:tdelprat@nuxeo.com">Tiry</a> 069 */ 070public class ModelImporter { 071 072 protected final static Log log = LogFactory.getLog(ModelImporter.class); 073 074 private static final String TEMPLATE_SAMPLE_INIT_EVENT = "TemplateSampleInit"; 075 076 private static final String[] IMPORT_ALREADY_DONE_EVENTS = { TEMPLATE_SAMPLE_INIT_EVENT }; 077 078 public static final String EXAMPLES_ROOT = "examples"; 079 080 public static final String RAW_EXAMPLES_ROOT = "rawexamples"; 081 082 public static final String TEMPLATE_ROOT = "template"; 083 084 protected static final String RESOURCES_ROOT = "templatesamples"; 085 086 protected static final String RAW_RESOURCES_ROOT = "rawsamples"; 087 088 protected static final String DOMAIN_QUERY = "select * from Domain where ecm:isCheckedInVersion=0 AND ecm:currentLifeCycleState != 'deleted' order by dc:created ASC"; 089 090 protected final CoreSession session; 091 092 public ModelImporter(CoreSession session) { 093 this.session = session; 094 } 095 096 protected String getTemplateResourcesRootPath() { 097 return RESOURCES_ROOT; 098 } 099 100 protected String getRawTemplateResourcesRootPath() { 101 return RAW_RESOURCES_ROOT; 102 } 103 104 protected DocumentModel getTargetDomain() { 105 return getTargetDomain(true); 106 } 107 108 protected DocumentModel getTargetDomain(boolean canRetry) { 109 DocumentModelList domains = session.query(DOMAIN_QUERY); 110 if (domains.size() > 0) { 111 return domains.get(0); 112 } 113 // no domain, that's strange 114 // may be a session flush issue 115 if (canRetry) { 116 session.save(); 117 return getTargetDomain(false); 118 } 119 return null; 120 } 121 122 protected DocumentModel getOrCreateTemplateContainer() { 123 DocumentModel rootDomain = getTargetDomain(); 124 125 if (rootDomain != null) { 126 DocumentModelList roots = session.getChildren(rootDomain.getRef(), "TemplateRoot"); 127 if (roots.size() > 0) { 128 return roots.get(0); 129 } 130 } 131 return null; 132 } 133 134 protected DocumentModel getWSRoot(DocumentModel rootDomain) { 135 if (rootDomain != null) { 136 DocumentModelList roots = session.getChildren(rootDomain.getRef(), "WorkspaceRoot"); 137 if (roots.size() > 0) { 138 DocumentModel WSRoot = roots.get(0); 139 return WSRoot; 140 } 141 } 142 return null; 143 } 144 145 protected DocumentModel getOrCreateSampleContainer() { 146 DocumentModel rootDomain = getTargetDomain(); 147 DocumentModel container = null; 148 149 DocumentModel WSRoot = getWSRoot(rootDomain); 150 if (WSRoot != null) { 151 PathRef targetPath = new PathRef(WSRoot.getPathAsString() + "/" + getTemplateResourcesRootPath()); 152 if (!session.exists(targetPath)) { 153 container = session.createDocumentModel(WSRoot.getPathAsString(), getTemplateResourcesRootPath(), 154 "nxtrSamplesContainer"); 155 container.setPropertyValue("dc:title", "Discover Customization Examples"); 156 container.setPropertyValue("nxtplsamplescontainer:instructions", 157 "<span class=\"nxtrExplanations\">The BigCorp company uses Nuxeo Studio and template rendering to generate custom project portfolios that showcase relevant expertise to potential new clients.<br /><br /><strong>It's your turn now! Open the \"BigCorp Transforms GreatBank Customer Service\" project</strong> and follow the instructions.</span>"); 158 container = session.createDocument(container); 159 } else { 160 container = session.getDocument(targetPath); 161 } 162 } 163 return container; 164 } 165 166 private DocumentModel getOrCreateRawSampleContainer() { 167 DocumentModel container = null; 168 DocumentModel parentContainer = getOrCreateSampleContainer(); 169 170 PathRef targetPath = new PathRef(getOrCreateSampleContainer().getPathAsString() + "/" + getRawTemplateResourcesRootPath()); 171 if (!session.exists(targetPath)) { 172 container = session.createDocumentModel(parentContainer.getPathAsString(), "rawsamples", 173 "Workspace"); 174 container.setPropertyValue("dc:title", "More (Raw) Examples"); 175 container.setPropertyValue("dc:description", 176 "This space contains raw examples to demonstrate the Nuxeo template rendering add-on's advanced possibilities. Go to the \"Discover Customization Samples\" folder first if you did not follow its instructions yet."); 177 container = session.createDocument(container); 178 } else { 179 container = session.getDocument(targetPath); 180 } 181 return container; 182 } 183 184 protected boolean isImportAlreadyDone() { 185 if (Framework.isTestModeSet()) { 186 return false; 187 } 188 189 AuditReader reader = Framework.getService(AuditReader.class); 190 List<LogEntry> entries = reader.queryLogs(IMPORT_ALREADY_DONE_EVENTS, null); 191 return !entries.isEmpty(); 192 } 193 194 protected void markImportDone() { 195 if (Framework.isTestModeSet()) { 196 return; 197 } 198 199 AuditLogger writer = Framework.getLocalService(AuditLogger.class); 200 201 LogEntry entry = writer.newLogEntry(); 202 entry.setEventId(TEMPLATE_SAMPLE_INIT_EVENT); 203 entry.setEventDate(Calendar.getInstance().getTime()); 204 205 List<LogEntry> entries = new ArrayList<LogEntry>(); 206 entries.add(entry); 207 writer.addLogEntries(entries); 208 209 } 210 211 public int importModels() { 212 213 if (isImportAlreadyDone()) { 214 return 0; 215 } 216 217 int nbImportedDocs = 0; 218 Path path = TemplateBundleActivator.getDataDirPath(); 219 path = path.append(getTemplateResourcesRootPath()); 220 File root = new File(path.toString()); 221 if (root.exists()) { 222 File[] modelRoots = root.listFiles(new FileFilter() { 223 @Override 224 public boolean accept(File pathname) { 225 if (!pathname.isDirectory()) { 226 return false; 227 } 228 return true; 229 } 230 }); 231 232 if (modelRoots != null && modelRoots.length > 0) { 233 for (File modelRoot : modelRoots) { 234 log.info("Importing template from " + modelRoot.getAbsolutePath()); 235 try { 236 nbImportedDocs += importModelAndExamples(modelRoot); 237 } catch (IOException e) { 238 throw new NuxeoException("Failed to import from template: " + modelRoot.getAbsolutePath(), e); 239 } 240 } 241 markImportDone(); 242 } 243 } 244 245 return nbImportedDocs; 246 } 247 248 public int importModelAndExamples(File root) throws IOException { 249 250 int nbImportedDocs = 0; 251 final Map<String, File> roots = new HashMap<String, File>(); 252 root.listFiles(new FileFilter() { 253 @Override 254 public boolean accept(File file) { 255 if (!file.isDirectory()) { 256 return false; 257 } 258 if (file.getName().equals(TEMPLATE_ROOT)) { 259 roots.put(TEMPLATE_ROOT, file); 260 return true; 261 } else if (file.getName().equals(EXAMPLES_ROOT)) { 262 roots.put(EXAMPLES_ROOT, file); 263 return true; 264 } else if (file.getName().equals(RAW_EXAMPLES_ROOT)) { 265 roots.put(RAW_EXAMPLES_ROOT, file); 266 return true; 267 } 268 269 return false; 270 } 271 }); 272 273 if (roots.size() >= 1) { 274 if (roots.get(TEMPLATE_ROOT) != null) { 275 DocumentModel templatesContainer = getOrCreateTemplateContainer(); 276 DocumentModel samplesContainer = getOrCreateSampleContainer(); 277 DocumentModel rawSamplesContainer = getOrCreateRawSampleContainer(); 278 if (templatesContainer != null) { 279 DocumentRef modelRef = importModel(root.getName(), roots.get(TEMPLATE_ROOT), templatesContainer); 280 nbImportedDocs++; 281 if (samplesContainer != null) { 282 if (roots.get(EXAMPLES_ROOT) != null) { 283 nbImportedDocs = nbImportedDocs 284 + importSamples(roots.get(EXAMPLES_ROOT), modelRef, samplesContainer); 285 } 286 if (roots.get(RAW_EXAMPLES_ROOT) != null) { 287 nbImportedDocs = nbImportedDocs 288 + importSamples(roots.get(RAW_EXAMPLES_ROOT), modelRef, rawSamplesContainer); 289 } 290 } 291 } 292 } 293 } 294 295 return nbImportedDocs; 296 297 } 298 299 protected DocumentRef importModel(String modelName, File source, DocumentModel root) throws IOException { 300 301 // import 302 DocumentReader reader = new XMLModelReader(source, modelName); 303 DocumentWriter writer = new DocumentModelWriter(session, root.getPathAsString()); 304 305 DocumentPipe pipe = new DocumentPipeImpl(10); 306 pipe.setReader(reader); 307 pipe.setWriter(writer); 308 DocumentTranslationMap map = pipe.run(); 309 310 DocumentRef ref = map.getDocRefMap().values().iterator().next(); 311 session.save(); 312 313 return ref; 314 } 315 316 protected int importSamples(File root, DocumentRef modelRef, DocumentModel rootDoc) throws IOException { 317 318 int nbImportedDocs = 0; 319 for (File exampleDir : root.listFiles()) { 320 if (!exampleDir.isDirectory()) { 321 continue; 322 } 323 324 // import 325 DocumentReader reader = new XMLModelReader(exampleDir, exampleDir.getName()); 326 DocumentWriter writer = new DocumentModelWriter(session, rootDoc.getPathAsString()); 327 328 DocumentPipe pipe = new DocumentPipeImpl(10); 329 330 final String targetUUID = modelRef.toString(); 331 332 pipe.addTransformer(new DocumentTransformer() { 333 334 @Override 335 public boolean transform(ExportedDocument xdoc) throws IOException { 336 xdoc.getDocument().accept(new Visitor() { 337 338 @Override 339 public void visit(Text node) { 340 } 341 342 @Override 343 public void visit(ProcessingInstruction node) { 344 } 345 346 @Override 347 public void visit(Namespace namespace) { 348 } 349 350 @Override 351 public void visit(Entity node) { 352 } 353 354 @Override 355 public void visit(Comment node) { 356 } 357 358 @Override 359 public void visit(CDATA node) { 360 } 361 362 @Override 363 public void visit(Attribute node) { 364 } 365 366 @Override 367 public void visit(Element node) { 368 if ("templateId".equalsIgnoreCase(node.getName()) 369 && "templateEntry".equalsIgnoreCase(node.getParent().getName())) { 370 log.debug("Translating uuid to " + targetUUID); 371 node.setText(targetUUID); 372 } 373 } 374 375 @Override 376 public void visit(DocumentType documentType) { 377 } 378 379 @Override 380 public void visit(Document document) { 381 } 382 }); 383 return true; 384 } 385 }); 386 pipe.setReader(reader); 387 pipe.setWriter(writer); 388 pipe.run(); 389 nbImportedDocs++; 390 391 } 392 session.save(); 393 return nbImportedDocs; 394 } 395}