/* * Copyright (C) 2005-2012 BetaCONCEPT Limited * * This file is part of Astroboa. * * Astroboa is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Astroboa is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Astroboa. If not, see <http://www.gnu.org/licenses/>. */ package org.betaconceptframework.astroboa.engine.jcr.io; import java.io.InputStream; import java.util.Calendar; import java.util.List; import javax.jcr.Session; import javax.xml.stream.XMLStreamReader; import javax.xml.transform.sax.SAXSource; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.time.DateFormatUtils; import org.apache.commons.lang.time.DurationFormatUtils; import org.betaconceptframework.astroboa.api.model.BinaryChannel; import org.betaconceptframework.astroboa.api.model.CmsRepositoryEntity; import org.betaconceptframework.astroboa.api.model.ContentObject; import org.betaconceptframework.astroboa.api.model.RepositoryUser; import org.betaconceptframework.astroboa.api.model.Space; import org.betaconceptframework.astroboa.api.model.Taxonomy; import org.betaconceptframework.astroboa.api.model.Topic; import org.betaconceptframework.astroboa.api.model.exception.CmsException; import org.betaconceptframework.astroboa.api.model.io.ImportConfiguration; import org.betaconceptframework.astroboa.api.model.io.ImportConfiguration.PersistMode; import org.betaconceptframework.astroboa.api.model.io.ImportReport; import org.betaconceptframework.astroboa.api.service.ContentService; import org.betaconceptframework.astroboa.engine.definition.RepositoryEntityResolver; import org.betaconceptframework.astroboa.engine.jcr.dao.RepositoryUserDao; import org.betaconceptframework.astroboa.engine.jcr.dao.SpaceDao; import org.betaconceptframework.astroboa.engine.jcr.dao.TaxonomyDao; import org.betaconceptframework.astroboa.engine.jcr.dao.TopicDao; import org.betaconceptframework.astroboa.engine.jcr.io.contenthandler.ImportContentHandler; import org.betaconceptframework.astroboa.engine.jcr.io.contenthandler.JsonImportContentHandler; import org.betaconceptframework.astroboa.engine.jcr.query.CmsQueryHandler; import org.betaconceptframework.astroboa.engine.jcr.util.BinaryChannelUtils; import org.betaconceptframework.astroboa.engine.jcr.util.CmsRepositoryEntityUtils; import org.betaconceptframework.astroboa.engine.jcr.util.Context; import org.betaconceptframework.astroboa.engine.model.jaxb.Repository; import org.betaconceptframework.astroboa.engine.service.jcr.ContentServiceImpl; import org.betaconceptframework.astroboa.model.impl.io.ImportReportImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sun.istack.XMLStreamReaderToContentHandler; /** * @author Gregory Chomatas (gchomatas@betaconcept.com) * @author Savvas Triantafyllou (striantafyllou@betaconcept.com) * */ public class Deserializer { private final Logger logger = LoggerFactory.getLogger(getClass()); private RepositoryEntityResolver repositoryEntityResolver; private TaxonomyDao taxonomyDao; private TopicDao topicDao; private RepositoryUserDao repositoryUserDao; private ContentService contentService; private ImportReport importReport; private SpaceDao spaceDao; private PersistMode persistMode; private boolean version; private boolean updateLastModificationDate; private Context context; private CmsRepositoryEntityUtils cmsRepositoryEntityUtils; private CmsQueryHandler cmsQueryHandler; private Session session; private BinaryChannelUtils binaryChannelUtils; public void setRepositoryEntityResolver( RepositoryEntityResolver repositoryEntityResolver) { this.repositoryEntityResolver = repositoryEntityResolver; } public void setSpaceDao(SpaceDao spaceDao) { this.spaceDao = spaceDao; } public void setTaxonomyDao(TaxonomyDao taxonomyDao) { this.taxonomyDao = taxonomyDao; } public void setTopicDao(TopicDao topicDao) { this.topicDao = topicDao; } public void setRepositoryUserDao(RepositoryUserDao repositoryUserDao) { this.repositoryUserDao = repositoryUserDao; } public void setContentService(ContentService contentService) { this.contentService = contentService; } public void setCmsRepositoryEntityUtils( CmsRepositoryEntityUtils cmsRepositoryEntityUtils) { this.cmsRepositoryEntityUtils = cmsRepositoryEntityUtils; } public void setCmsQueryHandler(CmsQueryHandler cmsQueryHandler) { this.cmsQueryHandler = cmsQueryHandler; } public void setSession(Session session) { this.session = session; } public void setBinaryChannelUtils(BinaryChannelUtils binaryChannelUtils){ this.binaryChannelUtils = binaryChannelUtils; } public <T> T deserializeContent(InputStream source, boolean jsonSource, Class<T> classWhoseContentIsImported,ImportConfiguration configuration){ if (configuration != null){ persistMode = configuration.getPersistMode(); version = configuration.isVersion(); updateLastModificationDate = configuration.isUpdateLastModificationTime(); } if (persistMode == null){ if (classWhoseContentIsImported == Repository.class || List.class.isAssignableFrom(classWhoseContentIsImported)){ persistMode = PersistMode.PERSIST_ENTITY_TREE; } else{ persistMode = PersistMode.DO_NOT_PERSIST; } } context = new Context(cmsRepositoryEntityUtils, cmsQueryHandler, session, configuration); XMLStreamReader xmlStreamReader = null; SAXSource saxSource = null; try{ Object entity = null; long start = System.currentTimeMillis(); if (jsonSource){ xmlStreamReader = CmsEntityDeserialization.Context.createJSONReader(source, true, classWhoseContentIsImported); JsonImportContentHandler<T> handler = new JsonImportContentHandler(classWhoseContentIsImported, this); XMLStreamReaderToContentHandler xmlStreamReaderToContentHandler = new XMLStreamReaderToContentHandler(xmlStreamReader, handler, false, false); xmlStreamReaderToContentHandler.bridge(); entity = handler.getResult(); logger.debug(" Unmarshal json to {} took {}", classWhoseContentIsImported.getSimpleName(), DurationFormatUtils.formatDuration(System.currentTimeMillis() - start, "HH:mm:ss.SSSSSS")); } else{ saxSource = CmsEntityDeserialization.Context.createSAXSource(source, repositoryEntityResolver, false); //we explicitly disable validation because partial saves are allowed ImportContentHandler<T> handler = new ImportContentHandler(classWhoseContentIsImported, this); saxSource.getXMLReader().setContentHandler(handler); saxSource.getXMLReader().parse(saxSource.getInputSource()); entity = handler.getImportResult(); logger.debug("Unmarshal xml to {} took {}",classWhoseContentIsImported.getSimpleName(), DurationFormatUtils.formatDuration(System.currentTimeMillis() - start, "HH:mm:ss.SSSSSS")); } //If entity is not of type T then a class cast exception is thrown. if (entity instanceof CmsRepositoryEntity){ entity = save((CmsRepositoryEntity) entity); } return (T) entity; } catch(CmsException e){ logger.error("",e); throw e; } catch(Exception e){ logger.error("",e); throw new CmsException(e.getMessage()); } finally{ if (xmlStreamReader != null){ try { xmlStreamReader.close(); } catch (Exception e) { //Ignore exception } } if (saxSource != null && saxSource.getInputSource() != null){ IOUtils.closeQuietly(saxSource.getInputSource().getByteStream()); IOUtils.closeQuietly(saxSource.getInputSource().getCharacterStream()); } if (context != null){ context.dispose(); context = null; } } } public CmsRepositoryEntity save(CmsRepositoryEntity entity) { if (entity != null && shouldSaveEntity()){ if (entity instanceof ContentObject){ if (logger.isDebugEnabled()){ logger.debug("\n\nStarting ContentObject Save...{}", DateFormatUtils.format(Calendar.getInstance(), "dd/MM/yyyy HH:mm:ss.SSS")); } long start = System.currentTimeMillis(); //We call ContentServiceImpl as security aspect must run //prior to saving a content object entity = ((ContentServiceImpl)contentService).saveContentObjectInBatchMode((ContentObject)entity, version, updateLastModificationDate, context); if (logger.isDebugEnabled()){ logger.debug("Saving ContentObject from import took {}, {}", DurationFormatUtils.formatDuration(System.currentTimeMillis() - start, "HH:mm:ss.SSSSSS"), DateFormatUtils.format(Calendar.getInstance(), "dd/MM/yyyy HH:mm:ss.SSSSSS")); } if (importReport != null){ ((ImportReportImpl)importReport).addContentObjectsImported(1); } } else if (entity instanceof Topic){ if (shouldSaveEntityTree()){ entity = topicDao.saveTopicTree((Topic)entity, null); } else{ entity = topicDao.saveTopic((Topic)entity, null); } if (importReport != null){ ((ImportReportImpl)importReport).addTopicsImported(1); } } else if (entity instanceof Taxonomy){ if (shouldSaveEntityTree()){ entity = taxonomyDao.saveTaxonomyTree((Taxonomy)entity); } else{ entity = taxonomyDao.saveTaxonomy((Taxonomy)entity); } if (importReport != null){ ((ImportReportImpl)importReport).addTaxonomiesImported(1); } } else if (entity instanceof RepositoryUser){ entity = repositoryUserDao.saveRepositoryUser((RepositoryUser)entity); if (importReport != null){ ((ImportReportImpl)importReport).addRepositoryUsersImported(1); } } else if (entity instanceof Space){ if (shouldSaveEntityTree()){ entity = spaceDao.saveSpaceTree((Space)entity); } else{ entity = spaceDao.saveSpace((Space)entity); } if (importReport != null){ ((ImportReportImpl)importReport).addSpacesImported(1); } } else{ logger.warn("Importing of entity {} is not yet supported", entity.toString()); } } return entity; } private boolean shouldSaveEntity() { return persistMode != null && persistMode != PersistMode.DO_NOT_PERSIST; } private boolean shouldSaveEntityTree(){ return persistMode != null && persistMode == PersistMode.PERSIST_ENTITY_TREE; } public void setImportReport(ImportReport importReport) { this.importReport = importReport; } public void loadBinaryChannelContent(BinaryChannel binaryChannel) { //In cases where entity is not to be saved load content of binary channel if necessary //so that it will be available. Otherwise, content will be loaded upon save if (!shouldSaveEntity()){ binaryChannelUtils.loadContentFromExternalLocation(context, binaryChannel); } } }