package org.jboss.seam.wiki.core.upload.importers; import net.sf.jmimemagic.Magic; import org.hibernate.validator.ClassValidator; import org.hibernate.validator.InvalidValue; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Logger; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.AutoCreate; import org.jboss.seam.core.Validators; import static org.jboss.seam.international.StatusMessage.Severity.ERROR; import static org.jboss.seam.international.StatusMessage.Severity.INFO; import org.jboss.seam.international.StatusMessages; import org.jboss.seam.log.Log; import org.jboss.seam.wiki.core.dao.WikiNodeDAO; import org.jboss.seam.wiki.core.model.WikiNode; import org.jboss.seam.wiki.core.model.WikiUpload; import org.jboss.seam.wiki.core.upload.UploadType; import org.jboss.seam.wiki.core.upload.UploadTypes; import org.jboss.seam.wiki.core.upload.Uploader; import org.jboss.seam.wiki.core.upload.importers.annotations.UploadImporter; import org.jboss.seam.wiki.core.upload.importers.metamodel.Importer; import org.jboss.seam.wiki.util.WikiUtil; import javax.persistence.EntityManager; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** * TODO: Delegate code to util.Unarchiver * * @author Christian Bauer */ @Name("zipImporter") @UploadImporter( handledMimeTypes = {"application/zip", "application/java-archive"}, handledExtensions = {"zip", "jar"}, description = "lacewiki.label.ZipImporter" ) @AutoCreate public class ZipImporter implements Importer { @Logger Log log; @In protected WikiNodeDAO wikiNodeDAO; @In protected Map<String, UploadType> uploadTypes; @In protected StatusMessages statusMessages; public boolean handleImport(EntityManager em, WikiUpload zipFile) { if (zipFile.getData().length == 0) return true; Map<String, Object> newObjects = new HashMap<String, Object>(); ByteArrayInputStream byteStream = null; ZipInputStream zipInputStream = null; try { byteStream = new ByteArrayInputStream(zipFile.getData()); zipInputStream = new ZipInputStream(new BufferedInputStream(byteStream)); int bufferSize = 1024; ZipEntry ze; ByteArrayOutputStream baos; byte[] buffer = new byte[bufferSize]; byte[] uncompressedBytes; int bytesRead; while ((ze = zipInputStream.getNextEntry()) != null) { log.debug("extracting zip entry: " + ze.getName()); if (!beforeUncompress(em, zipFile, ze)) continue; baos = new ByteArrayOutputStream(); while ((bytesRead = zipInputStream.read(buffer, 0, bufferSize)) > 0) { baos.write(buffer, 0, bytesRead); } baos.close(); uncompressedBytes = baos.toByteArray(); Object newObject = createNewObject(em, zipFile, ze, uncompressedBytes); if (newObject != null) { newObjects.put(ze.getName(), newObject); } zipInputStream.closeEntry(); } } catch (Exception ex) { throw new RuntimeException(ex); } finally { try { if (zipInputStream != null) zipInputStream.close(); if (byteStream != null) byteStream.close(); } catch (Exception e) { e.printStackTrace(System.err); } } handleNewObjects(em, zipFile, newObjects); return true; } protected boolean beforeUncompress(EntityManager em, WikiUpload zipFile, ZipEntry zipEntry) { if (zipEntry.getName().contains("/")) { log.debug("skipping directory: " + zipEntry.getName()); statusMessages.addFromResourceBundleOrDefault( ERROR, "lacewiki.msg.ImportSkippingDirectory", "Skipping directory '{0}', importing not supported...", zipEntry.getName() ); return false; // Not supported } // This assumes that the wiki name is the zip entry filename without extension - maybe we should // support with extension as option, that's unique inside a zip archive so we fail too often WikiUpload tmp = new WikiUpload(); tmp.setFilename(zipEntry.getName()); return validateNewWikiname(zipFile, WikiUtil.convertToWikiName(tmp.getFilenameWithoutExtension())); } protected boolean validateNewWikiname(WikiUpload zipFile, String newWikiname) { log.debug("validating wiki name of new file: " + newWikiname); if (wikiNodeDAO.isUniqueWikiname(zipFile.getAreaNumber(), newWikiname) ) { log.debug("new name is unique and valid"); return true; } else { log.debug("new name is not unique and invalid"); statusMessages.addFromResourceBundleOrDefault( ERROR, "lacewiki.msg.ImportDuplicateName", "Skipping file '{0}', name is already used in this area...", newWikiname ); return false; } } protected Object createNewObject(EntityManager em, WikiUpload zipFile, ZipEntry zipEntry, byte[] uncompressedBytes) { log.debug("creating new file from zip entry: " + zipEntry.getName()); // First figure out what it is String mimeType = null; try { mimeType = Magic.getMagicMatch(uncompressedBytes).getMimeType(); } catch (Exception ex) {} String contentType = mimeType != null ? mimeType : UploadTypes.DEFAULT_UPLOAD_TYPE; // Just a temporary value holder this time Uploader uploader = new Uploader(); uploader.setData(uncompressedBytes); uploader.setFilename(zipEntry.getName()); uploader.setContentType(contentType); // Get the right handler for that type and produce a WikiUpload instance UploadType uploadType = uploadTypes.get(contentType); if (uploadType == null) uploadType = uploadTypes.get(UploadTypes.GENERIC_UPLOAD_TYPE); WikiUpload wikiUpload = uploadType.getUploadHandler().handleUpload(uploader); // Now set the other properties so we can persist it directly wikiUpload.setName(wikiUpload.getFilenameWithoutExtension()); wikiUpload.setWikiname(WikiUtil.convertToWikiName(wikiUpload.getName())); wikiUpload.setAreaNumber(zipFile.getAreaNumber()); wikiUpload.setCreatedBy(zipFile.getCreatedBy()); wikiUpload.setLastModifiedBy(wikiUpload.getCreatedBy()); wikiUpload.setCreatedOn(new Date(zipEntry.getTime())); wikiUpload.setLastModifiedOn(wikiUpload.getCreatedOn()); wikiUpload.setReadAccessLevel(zipFile.getReadAccessLevel()); wikiUpload.setWriteAccessLevel(zipFile.getWriteAccessLevel()); log.debug("created new file from zip entry: " + wikiUpload); return wikiUpload; } public void handleNewObjects(EntityManager em, WikiUpload zipFile, Map<String, Object> newObjects) { persistWikiUploadsSorted( em, zipFile, newObjects, new Comparator() { public int compare(Object o, Object o1) { if ( !(o instanceof WikiNode) && !(o1 instanceof WikiNode) ) return 0; return ((WikiNode)o).getWikiname().compareTo( ((WikiNode)o1).getWikiname() ); } } ); } private void persistWikiUploadsSorted(EntityManager em, WikiUpload zipFile, Map<String, Object> newObjects, Comparator comparator) { List<WikiNode> newNodes = new ArrayList<WikiNode>(); for (Object newObject : newObjects.values()) { if (newObject instanceof WikiNode) { newNodes.add((WikiNode)newObject); } } Collections.sort(newNodes, comparator); for (WikiNode newNode : newNodes) { log.debug("validating new node"); ClassValidator validator = Validators.instance().getValidator(newNode.getClass()); InvalidValue[] invalidValues = validator.getInvalidValues(newNode); if (invalidValues != null && invalidValues.length > 0) { log.debug("new node is invalid: " + newNode); for (InvalidValue invalidValue : invalidValues) { statusMessages.addFromResourceBundleOrDefault( ERROR, "lacewiki.msg.ImportInvalidNode", "Skipping entry '{0}', invalid: {1}", newNode.getName(), invalidValue.getMessage() ); } continue; } log.debug("persisting newly imported node: " + newNode); newNode.setParent(zipFile.getParent()); em.persist(newNode); statusMessages.addFromResourceBundleOrDefault( INFO, "lacewiki.msg.ImportOk", "Created file '{0}' in current directory.", newNode.getName() ); } } }