Java tutorial
/* Index ECM Engine - A system for managing the capture (when created * or received), classification (cataloguing), storage, retrieval, * revision, sharing, reuse and disposition of documents. * * Copyright (C) 2008 Regione Piemonte * Copyright (C) 2008 Provincia di Torino * Copyright (C) 2008 Comune di Torino * * This program 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 2, * or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package it.doqui.index.ecmengine.business.personalization.importer; import it.doqui.index.ecmengine.business.job.JobBusinessInterface; import it.doqui.index.ecmengine.business.job.dto.BatchJob; import it.doqui.index.ecmengine.business.job.dto.BatchJobParam; import it.doqui.index.ecmengine.business.job.util.EncryptionHelper; import it.doqui.index.ecmengine.business.job.util.JobStatus; import it.doqui.index.ecmengine.business.personalization.multirepository.RepositoryManager; import it.doqui.index.ecmengine.business.personalization.multirepository.Repository; import it.doqui.index.ecmengine.business.personalization.multirepository.ContentStoreDefinition; import it.doqui.index.ecmengine.dto.backoffice.DataArchive; import it.doqui.index.ecmengine.exception.EcmEngineException; import org.alfresco.service.cmr.security.AuthenticationService; import static it.doqui.index.ecmengine.util.EcmEngineConstants.*; import static it.doqui.index.ecmengine.business.personalization.importer.ArchiveImporterJobConstants.*; import it.doqui.index.ecmengine.exception.EcmEngineFoundationException; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.model.ContentModel; import org.alfresco.service.namespace.QName; import javax.transaction.RollbackException; import org.alfresco.util.TempFileProvider; import it.doqui.index.ecmengine.dto.OperationContext; import java.util.Map; import java.util.List; import java.io.FileReader; import java.io.Serializable; import java.util.HashMap; import javax.activation.MimetypesFileTypeMap; import javax.transaction.UserTransaction; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.FileInputStream; import java.io.InputStream; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.apache.tools.tar.TarEntry; import org.apache.tools.tar.TarInputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.nio.channels.FileChannel; import java.io.IOException; /** * Classe che implementa il job ... * * @author DoQui * */ public class ArchiveImporterJob implements Job { // Logger private static Log logger = LogFactory.getLog(ECMENGINE_ROOT_LOG_CATEGORY); // Variabile privata per verificare se il JOB e' in esecuzione // Occorre infatti l'uso in sigleton di archive importer private static boolean running = false; // Service da utilizzare per l'importazione dei contenuti private TransactionService transactionService; private ContentService contentService; private NodeService nodeService; private NamespaceService namespaceService; private AuthenticationService authenticationService; /** Tipo predefinito per i file. */ public static final String DEFAULT_CONTENT_TYPE = "cm:content"; /** Tipo predefinito per i folder. */ public static final String DEFAULT_CONTAINER_TYPE = "cm:folder"; /** Property contenente il nome dei file. */ public static final String DEFAULT_CONTENT_NAME_PROPERTY = "cm:name"; /** Property contenente il nome dei folder. */ public static final String DEFAULT_CONTAINER_NAME_PROPERTY = "cm:name"; /** Tipo di associazione predefinito per i folder. */ public static final String DEFAULT_CONTAINER_ASSOC_TYPE = "cm:contains"; /** Tipo di associazione predefinito per i file. */ public static final String DEFAULT_PARENT_ASSOC_TYPE = "cm:contains"; /* (non-Javadoc) * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) */ public void execute(JobExecutionContext context) throws JobExecutionException { logger.debug("[ArchiveImporterJob::execute] BEGIN"); if (!running) { synchronized (this) { if (!running) { running = true; } else { logger.debug("[ArchiveImporterJob::execute] job already running 1"); logger.debug("[ArchiveImporterJob::execute] END"); return; } } } else { logger.debug("[ArchiveImporterJob::execute] job already running 2"); logger.debug("[ArchiveImporterJob::execute] END"); return; } logger.debug("[ArchiveImporterJob::execute] START"); JobBusinessInterface jobManager = null; BatchJob batchJob = null; try { // Chiedo un'istanza di JobManager jobManager = (JobBusinessInterface) context.getJobDetail().getJobDataMap() .get(ECMENGINE_JOB_MANAGER_BEAN); // In caso di successo if (jobManager != null) { List<Repository> repositories = RepositoryManager.getInstance().getRepositories(); for (Repository repository : repositories) { logger.debug("[ArchiveImporterJob::execute] import archive on repository '" + repository.getId() + "'"); RepositoryManager.setCurrentRepository(repository.getId()); // Faccio la lista dei job di tipo ECMENGINE_ARCHIVE_IMPORTER_JOB_REF, attivi // Possono essere // Ready - da processare // Running - in esecuzione : sto importando il file // Finished - ho finito // In questo stato, essendo un job singleton, dovrei avere solo stati ready (nuovi job) o finished (job finiti) // Se icontro un RUNNING, sicuramente si tratta di una condizione di errore, riporto a ready il job, e faccio in // modo che l'algoritmo sottostante continui l'importazione in modo incrementale // Prendo tutti i job di un certo esecutore: in futuro, se la cosa dovesse dare problemi di performance // aggiungere un filtro sullo status RUNNING BatchJob[] bjs = jobManager.getJobsByExecutor(ECMENGINE_ARCHIVE_IMPORTER_JOB_REF); if (bjs != null) { // Se ho dei BatchJob for (BatchJob bj : bjs) { logger.debug("[ArchiveImporterJob::execute] job status " + bj.getId() + ":" + bj.getStatus()); // Se lo stato e' running if (bj.getStatus().equalsIgnoreCase(JobStatus.RUNNING)) { logger.debug("[ArchiveImporterJob::execute] update status to ready " + bj.getId()); // Reimposto lo stato a ready bj.setStatus(JobStatus.READY); jobManager.updateJob(bj); } } } else { logger.debug("[ArchiveImporterJob::execute] BatchJob NULL per " + ECMENGINE_ARCHIVE_IMPORTER_JOB_REF); } // A questo punto, sono in una situazione consistente, con dei job ready // I job ready possono essere sia nuovi job, che resume di situazioni precedenti while ((batchJob = jobManager.getNextJob(ECMENGINE_ARCHIVE_IMPORTER_JOB_REF)) != null) { // Prendo il prossimo job logger.debug("[ArchiveImporterJob::execute] start batchJob " + batchJob.getId()); if (batchJob != null) { try { // Estraggo i parametri di esecuzione BatchJobParam pName = batchJob.getParam(PARAM_NAME); BatchJobParam pFormat = batchJob.getParam(PARAM_FORMAT); BatchJobParam pStore = batchJob.getParam(PARAM_CONTENTSTORE_DIR); String importDirectory = (String) context.getJobDetail().getJobDataMap() .get(ECMENGINE_IMPORT_DIRECTORY); // verifico se sono corretti checkParam(batchJob, pName, "Archive name not found"); checkParam(batchJob, pFormat, "Archive format not found"); checkParam(batchJob, pStore, "Archive store not found"); checkParam(batchJob, importDirectory, "importDirectory null"); // Validati i parametri di estrazione del fie, procedo con lo // spostamento da TEMP alla directory di import, ed esplodo il file // Sposto il file dalla temp alla workdir del progetto String cFrom = pStore.getValue() + File.separator + pName.getValue(); File oFrom = new File(cFrom); logger.debug("[ArchiveImporterJob::execute] From:" + cFrom); // Crea la directory di output se non esiste File oDir = new File(importDirectory); if (!oDir.exists()) { oDir.mkdir(); } // File di importazione String cFile = importDirectory + File.separator + pName.getValue(); File oFile = new File(cFile); logger.debug("[ArchiveImporterJob::execute] Import:" + cFile); // Se esiste il file, lo sposto nella dir dove deve essere processato if (oFrom.exists()) { // Provo lo spostamento, perche' piu' veloce if (!oFrom.renameTo(oFile)) { // Se fallisce provo la copia, piu' lenta if (!copyFile(oFrom, oFile)) { batchJob.setMessage( "Unable to copy from (" + cFrom + ") to (" + cFile + ")"); throw new EcmEngineException( "ArchiveImporterJob: " + batchJob.getMessage()); } else { // Se la copia va a buon fine, cancello il file oFrom.delete(); } } } // Prima cosa, provo a caricare in RAM il file indicato. // Attenzione: vista la dimensione a 256 char dei parametri dei job, // ho preferito spezzare path e nome file in due variabili diverse, in modo // da all'ungare il piu' possibile la lunghezza del path utilizzabile // Una volta processato il file, lo rinomino in processed, per poterne tenere traccia // I processed andrebbero alimintati ogni tanto, per ridurne la coda // Alternativamente, si puo' variare il codice per rinominarli al posto di rinominare // Un'altra ragione della rinomina e' data dal fatto che, in caso di zip corrotti, puo' accadere che // il file risulti importato, ma non presente, senza nessun messaggio d'errore String cFileRenamed = cFile + ".processed"; File oFileRenamed = new File(cFileRenamed); // Il temp path lo formo col nome del file compresso +".extract", in modo da avere l'univocita' data gia // dal nome file String tempPath = cFile + ".extract"; File tmpWorkDir = new File(tempPath); // Se il file non esiste if (!oFile.exists()) { logger.debug( "[ArchiveImporterJob::execute] File di importazione non presente, controllo directory di esplosione"); // Se ho la dir di estrazione, vuol dire che stavo importando e c'e' stato un errore // Quindi do errore solo se non esiste manco la dir di output if (!tmpWorkDir.exists()) { batchJob.setMessage("Archive not found"); throw new EcmEngineException("ArchiveImporterJob: " + batchJob.getMessage() + " (" + cFile + ")"); } else { logger.debug( "[ArchiveImporterJob::execute] Directory di importazione presente, procedo con l'importazione"); } } // Se sono qui, posso avere il file o la dir di esplosione // Se ho il file, prendo il contenuto da disco e lo decomprimo if (oFile.exists()) { byte[] content = getBinary(cFile); logger.debug("[ArchiveImporterJob::execute] Content size: " + content.length); // La directory la creo solo se non esiste, devo infatti gestire il caso che sia gia' creata // e sto andando in aggiornamento if (!tmpWorkDir.exists()) { if (!tmpWorkDir.mkdirs()) { batchJob.setMessage("Cant' creare working dir"); throw new EcmEngineException("ArchiveImporterJob: " + batchJob.getMessage() + " (" + tempPath + ")"); } } // A questo punto, estraggo i file presenti nello zip String cFormat = pFormat.getValue(); logger.debug("[ArchiveImporterJob::execute] estrazione archivio (" + cFile + ")(" + cFormat + ") in " + tempPath); if (ECMENGINE_ARCHIVE_FORMAT_TAR.equals(cFormat)) { extractTar(new ByteArrayInputStream(content), tempPath); } else if (ECMENGINE_ARCHIVE_FORMAT_TAR_GZ.equals(cFormat)) { extractTarGz(new ByteArrayInputStream(content), tempPath); } else if (ECMENGINE_ARCHIVE_FORMAT_ZIP.equals(cFormat)) { extractZip(new ByteArrayInputStream(content), tempPath); } else { // In caso di formato non gestito, esco con errore batchJob.setMessage("Format not supported"); throw new EcmEngineException("ArchiveImporterJob: " + batchJob.getMessage() + " (" + cFormat + ")"); } // A questo punto, ho l'esplosione dello ZIP // Una volta esploso in modo corretto, cancello eventuali copie non previste // e rinomino il file oFileRenamed.delete(); oFile.renameTo(oFileRenamed); // A fine processo, cancello i file interessati dalla import // Commentare questa riga se si volesse verificare come mai non viene importato un file oFile.delete(); oFileRenamed.delete(); } // Creo i service e verifico siano presi in modo corretto transactionService = (TransactionService) context.getJobDetail().getJobDataMap() .get(ECMENGINE_TRANSACTION_SERVICE_BEAN); namespaceService = (NamespaceService) context.getJobDetail().getJobDataMap() .get(ECMENGINE_NAMESPACE_SERVICE_BEAN); contentService = (ContentService) context.getJobDetail().getJobDataMap() .get(ECMENGINE_CONTENT_SERVICE_BEAN); nodeService = (NodeService) context.getJobDetail().getJobDataMap() .get(ECMENGINE_NODE_SERVICE_BEAN); authenticationService = (AuthenticationService) context.getJobDetail() .getJobDataMap().get(ECMENGINE_AUTHENTICATION_SERVICE_BEAN); checkParam(batchJob, transactionService, "transactionService null"); checkParam(batchJob, namespaceService, "namespaceService null"); checkParam(batchJob, contentService, "contentService null"); checkParam(batchJob, nodeService, "nodeService null"); checkParam(batchJob, authenticationService, "authenticationService null"); // Vengono presi i parametri del batch e ne viene controllata la conguenza, uscendo in caso di errore BatchJobParam pUID = batchJob.getParam(PARAM_UID); BatchJobParam pStoreProtocol = batchJob.getParam(PARAM_STORE_PROTOCOL); BatchJobParam pStoreIdentifier = batchJob.getParam(PARAM_STORE_IDENTIFIER); BatchJobParam pUser = batchJob.getParam(PARAM_USER); BatchJobParam pPassword = batchJob.getParam(PARAM_PASSWORD); BatchJobParam pContentType = batchJob.getParam(PARAM_CONTENT_TYPE); BatchJobParam pNameProperty = batchJob.getParam(PARAM_CONTENT_NAME_PROPERTY); BatchJobParam pContainerType = batchJob.getParam(PARAM_CONTAINER_TYPE); BatchJobParam pContainerNameProperty = batchJob .getParam(PARAM_CONTAINER_NAME_PROPERTY); BatchJobParam pContainerAssocType = batchJob.getParam(PARAM_CONTAINER_ASSOC_TYPE); BatchJobParam pParentAssocType = batchJob.getParam(PARAM_PARENT_ASSOC_TYPE); checkParam(batchJob, pUID, "Node UID not found"); checkParam(batchJob, pStoreProtocol, "Store Protocol not found"); checkParam(batchJob, pStoreIdentifier, "Store Identifier not found"); checkParam(batchJob, pUser, "User not found"); checkParam(batchJob, pPassword, "Password not found"); checkParam(batchJob, pContentType, "Content Type not found"); checkParam(batchJob, pNameProperty, "Content Name not found"); checkParam(batchJob, pContainerType, "Container Type not found"); checkParam(batchJob, pContainerNameProperty, "Container Name not found"); checkParam(batchJob, pContainerAssocType, "Container Assoc not found"); checkParam(batchJob, pParentAssocType, "Parent Assoc not found"); // Trasformazione dei parametri in QName QName contentTypeQName = resolvePrefixNameToQName(pContentType.getValue()); QName contentNamePropertyQName = resolvePrefixNameToQName(pNameProperty.getValue()); QName containerTypeQName = resolvePrefixNameToQName(pContainerType.getValue()); QName containerNamePropertyQName = resolvePrefixNameToQName( pContainerNameProperty.getValue()); QName containerAssocTypeQName = resolvePrefixNameToQName( pContainerAssocType.getValue()); QName parentAssocTypeQName = resolvePrefixNameToQName(pParentAssocType.getValue()); // Prendo un oggetto UserTransaction UserTransaction transaction = transactionService.getNonPropagatingUserTransaction(); try { // Inizio la transazione transaction.begin(); // Cambio l'utente, con l'utente che ha deve importare authenticationService.authenticate(pUser.getValue(), EncryptionHelper.decrypt(pPassword.getValue()).toCharArray()); } catch (Exception e) { logger.debug(e); throw e; } finally { // Anche se non ho fatto try { transaction.rollback(); } catch (Exception e) { } } // Creo un nodo, usando l'UID del folder dove devo mettere i dati StoreRef sr = new StoreRef(pStoreProtocol.getValue(), pStoreIdentifier.getValue()); // DictionarySvc.SPACES_STORE NodeRef nodeRef = new NodeRef(sr, pUID.getValue()); // Attivo l'importazione ricorsiva int nContent = handleRootFolder(tmpWorkDir, nodeRef, parentAssocTypeQName, containerTypeQName, containerNamePropertyQName, containerAssocTypeQName, contentTypeQName, contentNamePropertyQName); // Reimposto lo status e vado al prossimo JOB batchJob.setMessage("Content nuovi: " + nContent + " Datafile " + pName.getValue() + ".processed"); batchJob.setStatus(JobStatus.FINISHED); jobManager.updateJob(batchJob); } catch (Exception e) { logger.error("[ArchiveImporterJob::execute] ERROR", e); try { // Reimposto il getMessage(), nel caso arrivi vuoto if (batchJob.getMessage().length() == 0) { batchJob.setMessage(e.getMessage()); } // Reimposto lo status e vado al prossimo JOB batchJob.setStatus(JobStatus.ERROR); jobManager.updateJob(batchJob); } catch (Exception ee) { // TODO: vedere se e' giusto tenerlo muto } } finally { // Non posso toccare lo stato a ready, altrimenti vado in loop } } } } } else { logger.error("[ArchiveImporterJob::execute] JobManager NULL per " + ECMENGINE_JOB_MANAGER_BEAN); } } catch (Exception e) { logger.error("[ArchiveImporterJob::execute] ERROR", e); throw new JobExecutionException(e); } finally { running = false; logger.debug("[ArchiveImporterJob::execute] END"); } logger.debug("[ArchiveImporterJob::execute] END run"); } /** * Metodo statico di utilità per la creazione del job da fornire come * parametro al job manager. * * @param archive * Un oggetto DataArchive con i dati dell'archivio da decomprimere * @param node * Il nodo dove importare i dti * @param context * L'istanza di {@code OperationContext} con la quale autenticarsi * @return L'istanza di {@code BatchJob} contenente i dati necessati al job * di gestione di un DataArchive * @throws Exception * @see {@link it.doqui.index.ecmengine.business.job.JobBusinessInterface} */ public static BatchJob createBatchJob(DataArchive archive, NodeRef node, OperationContext context) throws Exception { BatchJob job = new BatchJob(ECMENGINE_ARCHIVE_IMPORTER_JOB_REF); // UID sotto al quale agganciare o ZIP e suo store Ref String cUID = node.getId(); StoreRef sr = node.getStoreRef(); job.addParam(new BatchJobParam(PARAM_UID, cUID)); job.addParam(new BatchJobParam(PARAM_STORE_PROTOCOL, sr.getProtocol())); job.addParam(new BatchJobParam(PARAM_STORE_IDENTIFIER, sr.getIdentifier())); // Aggiungo l'utente che effettuera' l'operazione job.addParam(new BatchJobParam(PARAM_USER, context.getUsername())); job.addParam(new BatchJobParam(PARAM_PASSWORD, EncryptionHelper.encrypt(context.getPassword()))); // Serve creare un file temporaneo con dentro archive.getContent() e mettere il nome in un parametro String cTimeStamp = "" + new java.util.Date().getTime(); String cName = cTimeStamp + "_" + Math.random() + "_" + cUID + "." + archive.getFormat(); job.addParam(new BatchJobParam(PARAM_NAME, cName)); // Path di memorizzazione del file PARAM_NAME String workDir = TempFileProvider.getTempDir().getAbsolutePath(); job.addParam(new BatchJobParam(PARAM_CONTENTSTORE_DIR, workDir)); // Metto il content nel file temporaneo String cFile = workDir + File.separator + cName; putBinary(cFile, archive.getContent()); // Formato del file da importare job.addParam(new BatchJobParam(PARAM_FORMAT, archive.getFormat())); // Parametri necessari all'esecuzione job.addParam(new BatchJobParam(PARAM_CONTENT_TYPE, getValue(archive.getMappedContentTypePrefixedName(), DEFAULT_CONTENT_TYPE))); job.addParam(new BatchJobParam(PARAM_CONTENT_NAME_PROPERTY, getValue(archive.getMappedContentNamePropertyPrefixedName(), DEFAULT_CONTENT_NAME_PROPERTY))); job.addParam(new BatchJobParam(PARAM_CONTAINER_TYPE, getValue(archive.getMappedContainerTypePrefixedName(), DEFAULT_CONTAINER_TYPE))); job.addParam(new BatchJobParam(PARAM_CONTAINER_NAME_PROPERTY, getValue(archive.getMappedContainerNamePropertyPrefixedName(), DEFAULT_CONTAINER_NAME_PROPERTY))); job.addParam(new BatchJobParam(PARAM_CONTAINER_ASSOC_TYPE, getValue(archive.getMappedContainerAssocTypePrefixedName(), DEFAULT_CONTAINER_ASSOC_TYPE))); job.addParam(new BatchJobParam(PARAM_PARENT_ASSOC_TYPE, getValue(archive.getParentContainerAssocTypePrefixedName(), DEFAULT_PARENT_ASSOC_TYPE))); return job; } /** * Metodo di comodità per validare un valore ed eventualmente utilizzare un * valore di default. * * <p> * Questo metodo verifica se {@code targetValue} è diverso da null o * stringa vuota e ne restituisce il valore. Altrimenti restituisce il * valore fornito come default. * </p> * * @param targetValue * Il valore da validare ed eventualmente restituire. * @param defaultValule * Il valore di default. * * @return {@code targetValue} se diverso da null o stringa vuota; * {@code defaultValue} altrimenti. */ static private String getValue(String targetValue, String defaultValue) { return (targetValue != null && targetValue.length() > 0) ? targetValue : defaultValue; } /** * Estrae il contenuto di un archivio in formato TAR. * * @param archiveInputStream L'input stream da cui leggere il contenuto dell'archivio. * @param path Il path della directory in cui estrarre il contenuto dell'archivio. * * @throws Exception */ private void extractTar(InputStream archiveInputStream, String path) throws Exception { logger.debug("[ArchiveImporterJob::extractTar] BEGIN"); TarInputStream inputStream = null; try { inputStream = new TarInputStream(archiveInputStream); String entryName = null; for (TarEntry entry = inputStream.getNextEntry(); entry != null; entry = inputStream.getNextEntry()) { entryName = entry.getName(); File entryFile = new File(path + File.separator + entryName); if (entry.isDirectory()) { if (!entryFile.mkdirs()) { throw new EcmEngineException("cannot create directory: " + entryFile.getAbsolutePath()); } } else { File parentDir = entryFile.getParentFile(); if (!parentDir.exists()) { if (!parentDir.mkdirs()) { throw new EcmEngineException("cannot create directory: " + parentDir.getAbsolutePath()); } } inputStream.copyEntryContents(new FileOutputStream(path + File.separator + entryName)); } } } catch (Exception e) { throw e; } finally { if (inputStream != null) { try { inputStream.close(); } catch (Exception ee) { } } logger.debug("[ArchiveImporterJob::extractTar] END"); } } /** * Estrae il contenuto di un archivio in formato TAR con compressione GZip. * * @param archiveInputStream L'input stream da cui leggere il contenuto dell'archivio. * @param path Il path della directory in cui estrarre il contenuto dell'archivio. * * @throws Exception */ private void extractTarGz(InputStream archiveInputStream, String path) throws Exception { logger.debug("[ArchiveImporterJob::extractTarGz] BEGIN"); try { extractTar(new GZIPInputStream(archiveInputStream), path); } catch (Exception e) { throw e; } finally { logger.debug("[ArchiveImporterJob::extractTarGz] END"); } } /** * Estrae il contenuto di un archivio in formato ZIP. * * @param archiveInputStream L'input stream da cui leggere il contenuto dell'archivio. * @param path Il path della directory in cui estrarre il contenuto dell'archivio. * * @throws Exception */ // TODO: verificare come mai se il file non e' ZIP non va in errore private void extractZip(InputStream archiveInputStream, String path) throws Exception { logger.debug("[ArchiveImporterJob::extractZip] BEGIN"); ZipInputStream inputStream = null; FileOutputStream fileOutputStream = null; try { inputStream = new ZipInputStream(archiveInputStream); String entryName = null; byte[] buffer = new byte[1024]; int n = 0; for (ZipEntry entry = inputStream.getNextEntry(); entry != null; entry = inputStream.getNextEntry()) { entryName = entry.getName(); File entryFile = new File(path + File.separator + entryName); if (entry.isDirectory()) { if (!entryFile.mkdirs()) { throw new EcmEngineException("cannot create directory: " + entryFile.getAbsolutePath()); } } else { File parentDir = entryFile.getParentFile(); if (!parentDir.exists()) { if (!parentDir.mkdirs()) { throw new EcmEngineException("cannot create directory: " + parentDir.getAbsolutePath()); } } fileOutputStream = new FileOutputStream(entryFile); while ((n = inputStream.read(buffer, 0, buffer.length)) > -1) { fileOutputStream.write(buffer, 0, n); } fileOutputStream.close(); } inputStream.closeEntry(); } } catch (Exception e) { throw e; } finally { if (inputStream != null) { try { inputStream.close(); } catch (Exception e) { } try { fileOutputStream.close(); } catch (Exception e) { } } logger.debug("[ArchiveImporterJob::extractZip] END"); } } private byte[] getBinary(String cFile) throws Exception { File file = new File(cFile); FileInputStream fileinputstream = new FileInputStream(file); byte abyte0[] = new byte[(int) file.length()]; fileinputstream.read(abyte0); fileinputstream.close(); return abyte0; } static private void putBinary(String cFile, byte[] bOut) throws Exception { File file = new File(cFile); FileOutputStream fileoutputstream = new FileOutputStream(file); fileoutputstream.write(bOut); fileoutputstream.close(); return; } private int handleRootFolder(File folder, NodeRef parentNodeRef, QName parentAssocTypeQName, QName containerTypeQName, QName containerNamePropertyQName, QName containerAssocTypeQName, QName contentTypeQName, QName contentNamePropertyQName) throws Exception { logger.debug("[ArchiveImporterJob::handleRootFolder] BEGIN"); // Conto quanti dati sono stati scritti int nContent = 0; try { // Prima si inizia col creare i singoli contenuti boolean bContent = false; { // Prendo un oggetto UserTransaction UserTransaction transaction = transactionService.getNonPropagatingUserTransaction(); try { // Inizio la transazione transaction.begin(); // Conto i content creati int nSubContent = 0; // Prima creo i content in una transazione File[] folderEntries = folder.listFiles(); for (File entry : folderEntries) { // Se e' una directory if (!entry.isDirectory()) { logger.debug("[ArchiveImporterJob::handleRootFolder] creating content: " + entry.getName() + ", nodeRef=" + parentNodeRef + ", association=" + parentAssocTypeQName); // Creo il contenuti if (createContent(entry, parentNodeRef, contentTypeQName, contentNamePropertyQName, parentAssocTypeQName)) { nSubContent++; } } } // Se ho inserito 0 content, e non si e' generata una eccezione, vuol dire che i dati inseriti // sono tutti dei doppioni, in questo caso, meto bContent a true, e lascio andare avanti l'algoritmo bContent = (nSubContent == 0); nContent += nSubContent; logger.debug("[ArchiveImporterJob::handleRootFolder] Content inseriti: " + nContent); // Nel caso che si chiami una commit, senza righe da committare // C'e' una eccezione. // TODO: gestire le transazioni da 0 contenuti .. ma .. cosa fare in questa situazione? transaction.commit(); // Se non ho ecezione sulla commit, indico come true la creazione content bContent = true; logger.debug("[ArchiveImporterJob::handleRootFolder] Content bool " + bContent); } catch (RollbackException re) { try { transaction.rollback(); } catch (Exception ee) { logger.debug("[ArchiveImporterJob::handleRootFolder] RollbackException"); } } catch (EcmEngineFoundationException e) { // Rollback try { transaction.rollback(); } catch (Exception ee) { logger.debug("[ArchiveImporterJob::handleRootFolder] EcmEngineFoundationException"); } } catch (Exception e) { logger.debug(e); throw e; } } // Se i contenuti vanno a buon fine, inizio a cancellarli da disco boolean bDelete = false; if (bContent) { try { // Prima creo i content in una transazione File[] folderEntries = folder.listFiles(); for (File entry : folderEntries) { // Se e' una directory if (!entry.isDirectory()) { // Cancello il contenuto entry.delete(); } } bDelete = true; } catch (Exception e) { logger.debug(e); throw e; } } // Se le delete vanno a buon fine, inizio a creare le directory if (bDelete) { try { boolean bDeleteFolder = true; // Per tutti i file della cartella File[] folderEntries = folder.listFiles(); for (File entry : folderEntries) { // Se e' una directory if (entry.isDirectory()) { // Create directory logger.debug("[ArchiveImporterJob::handleRootFolder] creating directory: " + entry.getName() + ", nodeRef=" + parentNodeRef + ", association=" + parentAssocTypeQName); // nodo di riferimento NodeRef nr = null; // Stranamente, per una get di dati, viene espressamente richiesta una transazione // Prendo un oggetto UserTransaction UserTransaction transaction = transactionService.getNonPropagatingUserTransaction(); try { // Inizio la transazione transaction.begin(); // Verifico se la cartella e' presente nel nodo padre nr = nodeService.getChildByName(parentNodeRef, parentAssocTypeQName, entry.getName()); // Anche se non ho fatto transaction.rollback(); } catch (Exception e) { logger.debug(e); throw e; } finally { // Anche se non ho fatto try { transaction.rollback(); } catch (Exception e) { } } // Prendo un oggetto UserTransaction transaction = transactionService.getNonPropagatingUserTransaction(); boolean bTrans = false; try { // Se non e' presente, provo a crearla if (nr == null) { bTrans = true; // Preparo le properties di un folder QName prefixedNameQName = resolvePrefixNameToQName("cm:" + entry.getName()); Map<QName, Serializable> props = new HashMap<QName, Serializable>(); props.put(containerNamePropertyQName, entry.getName()); // Inizio la transazione transaction.begin(); // Creo il folder ChildAssociationRef folderNodeRef = nodeService.createNode(parentNodeRef, parentAssocTypeQName, prefixedNameQName, containerTypeQName, props); // Nel caso che si chiami una commit, senza righe da committare // C'e' una eccezione. // TODO: gestire le transazioni da 0 contenuti transaction.commit(); nr = folderNodeRef.getChildRef(); } // Creazione del subfolder nContent += handleRootFolder(entry, nr, containerAssocTypeQName, // Non passo il parent, ma passo il containerAssocType nei folder figli containerTypeQName, containerNamePropertyQName, containerAssocTypeQName, contentTypeQName, contentNamePropertyQName); } catch (RollbackException re) { if (bTrans) { try { transaction.rollback(); } catch (Exception ee) { logger.debug(re); } } } catch (EcmEngineFoundationException e) { bDeleteFolder = false; // Rollback try { transaction.rollback(); } catch (Exception ee) { logger.debug(e); } } catch (Exception e) { logger.debug(e); throw e; } } } // Rimuovo la directory, se non ho avuto problemi rimuovendo le subdir if (bDeleteFolder) { folder.delete(); } } catch (Exception e) { logger.debug(e); throw e; } } } catch (Exception e) { logger.debug(e); throw e; } finally { logger.debug("[ArchiveImporterJob::handleRootFolder] END"); } return nContent; } /** * Crea un contenuto sul repository a partire da un file su filesystem. * * @param content * Il file da creare sul repository. * @param parentNodeRef * Il {@code NodeRef} del nodo sotto cui creare il contenuto. * @param contentTypeQName * Il {@code QName} del tipo di contenuto da creare. * @param contentNamePropertyQName * Il {@code QName} del nome del contenuto da creare. * @param containerAssocTypeQName * Il {@code QName} dell'associazione che lega contenuto e nodo * padre. * * @return Il {@code NodeRef} del contenuto creato. * * @throws Exception */ private boolean createContent(File content, NodeRef parentNodeRef, QName contentTypeQName, QName contentNamePropertyQName, QName containerAssocTypeQName) throws Exception { logger.debug("[ArchiveImporterJob::createContent] BEGIN"); boolean bRet = false; try { // Verifico se il contenuto e' presente nel nodo padre NodeRef nr = nodeService.getChildByName(parentNodeRef, containerAssocTypeQName, content.getName()); // Se non e' presente, provo a crearlo if (nr == null) { // Creazione nodo QName prefixedNameQName = resolvePrefixNameToQName("cm:" + content.getName()); Map<QName, Serializable> props = new HashMap<QName, Serializable>(); props.put(contentNamePropertyQName, content.getName()); ChildAssociationRef contentChildRef = nodeService.createNode(parentNodeRef, containerAssocTypeQName, prefixedNameQName, contentTypeQName, props); // Scrittura contenuto final ContentWriter writer = contentService.getWriter(contentChildRef.getChildRef(), ContentModel.PROP_CONTENT, true); writer.setMimetype(new MimetypesFileTypeMap().getContentType(content)); writer.setEncoding(getEncoding(content)); writer.putContent(content); // Se riesco a scrivere bRet = true; } } catch (DuplicateChildNodeNameException dc) { // In caso di contenuto duplicato, viene usata una policy conservativa, e viene tenuto // Il valore presente in repository logger.debug("[ArchiveImporterJob::createContent] Contenuto presente (" + content.getName() + ")"); } catch (Exception e) { throw e; } finally { logger.debug("[ArchiveImporterJob::createContent] END"); } return bRet; } /** * Restituisce l'encoding del file specificato. * * @param file Il file di cui ricavare l'encoding. * @return L'encoding del file specificato. */ private String getEncoding(File file) { String encoding = ""; try { FileReader fr = new FileReader(file); encoding = fr.getEncoding(); fr.close(); } catch (Exception e) { } return encoding; } private QName resolvePrefixNameToQName(String prefixName) { logger.debug("[ArchiveImporterJob::resolvePrefixNameToQName] BEGIN"); QName result = null; String[] nameParts = QName.splitPrefixedQName(prefixName); try { logger.debug("[ArchiveImporterJob::resolvePrefixNameToQName] Resolving to QName: " + prefixName); result = QName.createQName(nameParts[0], nameParts[1], namespaceService); logger.debug("[ArchiveImporterJob::resolvePrefixNameToQName] QName: " + result.toString()); } catch (RuntimeException e) { logger.debug("[ArchiveImporterJob::resolvePrefixNameToQName] " + "Error resolving to QName \"" + prefixName + "\": " + e.getMessage()); throw new RuntimeException(); // FIXME } finally { logger.debug("[ArchiveImporterJob::resolvePrefixNameToQName] END"); } return result; } private void checkParam(BatchJob batchJob, Object pParam, String cError) throws EcmEngineException { if (pParam == null) { batchJob.setMessage(cError); throw new EcmEngineException("ArchiveImporterJob: " + batchJob.getMessage()); } } // http://www.rgagnon.com/javadetails/java-0064.html private boolean copyFile(File in, File out) { boolean bRet = false; FileChannel inChannel = null; FileChannel outChannel = null; try { inChannel = new FileInputStream(in).getChannel(); outChannel = new FileOutputStream(out).getChannel(); // On the Windows plateform, you can't copy a file bigger than 64Mb, an // Exception in thread "main" java.io.IOException: Insufficient system // resources exist to complete the requested service is thrown. // inChannel.transferTo(0, inChannel.size(), outChannel); // magic number for Windows, 64Mb - 32Kb) int maxCount = (64 * 1024 * 1024) - (32 * 1024); long size = inChannel.size(); long position = 0; while (position < size) { position += inChannel.transferTo(position, maxCount, outChannel); } bRet = true; } catch (IOException e) { //System.out.println( e ); //throw e; } finally { try { if (inChannel != null) inChannel.close(); } catch (IOException e) { } try { if (outChannel != null) outChannel.close(); } catch (IOException e) { } } return bRet; } }