Skip to content

Commit

Permalink
SAK-25867 allow creation of a new site via archived zip file
Browse files Browse the repository at this point in the history
git-svn-id: https://source.sakaiproject.org/svn/common/trunk@308141 66ffb92e-73f9-0310-93c1-f5514f145a0a
  • Loading branch information
ottenhoff committed Apr 11, 2014
1 parent 447a675 commit d9917af
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

package org.sakaiproject.archive.api;

import java.io.IOException;

/**
* <p>
* ArchiveService takes care of exporting and importing entities.
Expand Down Expand Up @@ -70,4 +72,26 @@ public interface ArchiveService
* @return A log of messages from the merge.
*/
String merge(String archiveUrl, String siteId, String siteCreatorId);

/**
* Read in an archived set of resources, and merge the entries into the specified site, and set site creator name
* Allows a ZIP file to be used instead of the site archive directory
*
* @param zipFilePath
* The archived site as a zip file
* @param siteId
* The id of the site to merge the content into.
* @param siteCreatorId
* the site creator Id
* @return A log of messages from the merge.
*/
public String mergeFromZip(String zipFilePath, String siteId, String siteCreatorId);

/**
* Archive a site then compress it to a zip.
* @param siteId - id of site to be archived
* @return true if archive and zip successful, false if not.
* @throws IOException
*/
public boolean archiveAndZip(String siteId) throws IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.sakaiproject.archive.api;

import java.io.IOException;

public interface ZipAware {

/**
* Read in an archived set of resources, and merge the entries into the specified site, and set site creator name
* Allows a ZIP file to be used instead of the site archive directory
*
* @param zipFilePath
* The archived site as a zip file
* @param siteId
* The id of the site to merge the content into.
* @param siteCreatorId
* the site creator Id
* @return A log of messages from the merge.
*/
public String mergeZip(String zipFilePath, String siteId, String siteCreatorId);

/**
* Compress a site archive to a zip. Note that the site must have already been archived!
* @param siteId - id of site that was archived
* @return true if zip successful, false if not.
* @throws IOException
*/
public boolean archiveZip(String siteId) throws IOException;


}
Original file line number Diff line number Diff line change
Expand Up @@ -1289,6 +1289,16 @@ protected String translateServiceName(String name)
return name;
}

// SAK-25867 Stub to build
public boolean archiveAndZip(String siteId) throws java.io.IOException {
return true;
}

// SAK-25867 Stub to build
public String mergeFromZip(String zipFilePath, String siteId, String creatorId) {
return "";
}

} // BasicArchiveService


Expand Down
16 changes: 16 additions & 0 deletions common/archive-impl/impl2/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,26 @@
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</dependency>
<!-- <dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency> -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
</dependencies>
<build>
<resources />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

package org.sakaiproject.archive.impl;

import java.io.IOException;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.archive.api.ArchiveService;
Expand Down Expand Up @@ -57,6 +60,12 @@ public void setSiteMerger(SiteMerger siteMerger) {
m_siteMerger = siteMerger;
}

/** Dependency: SiteZipper */
protected SiteZipper m_siteZipper = null;
public void setSiteZipper(SiteZipper siteZipper) {
m_siteZipper = siteZipper;
}

/*********************************************/
/* Injected Default Settings */
/*********************************************/
Expand All @@ -66,6 +75,12 @@ public void setStoragePath(String path) {
m_storagePath = path;
}

/** Path used for processing zips **/
protected String m_unzipPath = "/";
public void setUnzipPath(String unzipPath) {
m_unzipPath = unzipPath;
}

protected boolean m_filterSakaiServices = false;
public void setMergeFilterSakaiServices(boolean filter) {
m_filterSakaiServices = filter;
Expand All @@ -92,10 +107,15 @@ public void setMergeFilteredSakaiRoles(String[] filtered) {
public void init() {

m_storagePath = m_serverConfigurationService.getString("archive.storage.path", m_storagePath);
if ((m_storagePath != null) && (!m_storagePath.endsWith("/"))) {
if (!StringUtils.endsWith(m_storagePath,"/")) {
m_storagePath = m_storagePath + "/";
}


m_unzipPath = m_serverConfigurationService.getString("archive.unzip.path", m_unzipPath);
if(!StringUtils.endsWith(m_unzipPath, "/")) {
m_unzipPath = m_unzipPath + "/";
}

m_filterSakaiServices = m_serverConfigurationService.getBoolean("archive.merge.filter.services", m_filterSakaiServices);
m_filterSakaiRoles = m_serverConfigurationService.getBoolean("archive.merge.filter.roles", m_filterSakaiRoles);
String[] filteredServices = m_serverConfigurationService.getStrings("archive.merge.filtered.services");
Expand All @@ -107,7 +127,7 @@ public void init() {
m_filteredSakaiRoles = filteredRoles;
}

M_log.info("init(): storage path: " + m_storagePath + ", merge filter{services="+m_filterSakaiServices+", roles="+m_filterSakaiRoles+"}");
M_log.info("init(): storage path: " + m_storagePath + ", unzip path: " + m_unzipPath + ", merge filter{services="+m_filterSakaiServices+", roles="+m_filterSakaiRoles+"}");
}

public void destroy() {
Expand Down Expand Up @@ -136,5 +156,23 @@ public String merge(String fileName, String siteId, String creatorId)
{
return m_siteMerger.merge(fileName, siteId, creatorId, m_storagePath, m_filterSakaiServices, m_filteredSakaiServices, m_filterSakaiRoles, m_filteredSakaiRoles);
}

@Override
public String mergeFromZip(String zipFilePath, String siteId, String creatorId) {
try {
String fileName = m_siteZipper.unzipArchive(zipFilePath, m_unzipPath);
//not a lot we can do with the return value here since it always returns a string. would need a reimplementation/wrapper method to return a better value (boolean or some status)
return m_siteMerger.merge(fileName, siteId, creatorId, m_storagePath, m_filterSakaiServices, m_filteredSakaiServices, m_filterSakaiRoles, m_filteredSakaiRoles);
} catch (IOException e) {
M_log.error("Error merging from zip: " + e.getClass() + ":" + e.getMessage());
return "Error merging from zip: " + e.getClass() + ":" + e.getMessage();
}
}

@Override
public boolean archiveAndZip(String siteId) throws IOException {
m_siteArchiver.archive(siteId, m_storagePath, FROM_SAKAI_2_8);
return m_siteZipper.zipArchive(siteId, m_storagePath);
}

} // ArchiveService2Impl
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/***********************************************************************************
*
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/

package org.sakaiproject.archive.impl;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.component.api.ServerConfigurationService;


public class SiteZipper {

private static Log log = LogFactory.getLog(SiteZipper.class);

protected ServerConfigurationService serverConfigurationService = null;
public void setServerConfigurationService(ServerConfigurationService service) {
serverConfigurationService = service;
}

/**
* Unzip a zip file into the unzip directory. Return the NAME of the archive so we can then merge from it. The merger knows the base dir.
* @param zipFilePath path to ZIP file
* @param m_unzipPath unzip dir for zips
* @return
* @throws IOException
*/
public String unzipArchive(String zipFilePath, String m_unzipPath) throws IOException {

log.debug("zipFilePath: " + zipFilePath);

ZipFile zipFile = new ZipFile(zipFilePath);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();

//destination file from zip. Straight into the normal archive directory
File dest = new File(m_unzipPath, entry.getName());
log.debug("Dest: " + dest.getAbsolutePath());

if(entry.isDirectory()) {
//create dir
dest.mkdir();
} else {
//extract contents
InputStream in = zipFile.getInputStream(entry);
OutputStream out = new FileOutputStream(dest);
IOUtils.copy(in, out);
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(out);
}
}

//get original filename, remove timestamp, add -archive
String unzippedArchivePath = StringUtils.substringAfterLast(StringUtils.substringBeforeLast(zipFile.getName(), "-") + "-archive", File.separator);

log.debug("unzippedArchivePath: " + unzippedArchivePath);

return unzippedArchivePath;

}

/**
* Zip a site archive. It is stored back in the zip directory
* @param siteId site that has already been archived
* @param m_storagePath path to where the archives are
* @return
* @throws IOException
*/
public boolean zipArchive(String siteId, String m_storagePath) throws IOException {

//get path to archive dir for this site
//suffix of -archive is hardcoded as per archive service
String archivePath = m_storagePath + siteId + "-archive";

//setup timestamp
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String timestamp = dateFormat.format(Calendar.getInstance().getTime());

//create path to compressed archive
String compressedArchivePath = m_storagePath + siteId + "-" + timestamp + ".zip";
File zipFile = new File(compressedArchivePath);

if(!zipFile.exists()) {
log.info("Creating zip file: " + compressedArchivePath);
zipFile.createNewFile();
}

FileOutputStream fOut = null;
BufferedOutputStream bOut = null;
ZipArchiveOutputStream zOut = null;

try {
fOut = new FileOutputStream(zipFile);
bOut = new BufferedOutputStream(fOut);
zOut = new ZipArchiveOutputStream(bOut);
addFileToZip(zOut, archivePath, ""); //add the directory which will then add all files recursively
} finally {
zOut.finish();
zOut.close();
bOut.close();
fOut.close();
}

//create a sha1 hash of the zip
String hashPath = m_storagePath + siteId + "-" + timestamp + ".sha1";
log.info("Creating hash: " + hashPath);
FileInputStream zip = new FileInputStream(compressedArchivePath);
String hash = DigestUtils.sha1Hex(zip);
FileUtils.writeStringToFile(new File(hashPath), hash);

return true;
}

/**
* Creates a zip entry for the path specified with a name built from the base passed in and the file/directory
* name. If the path is a directory, a recursive call is made such that the full directory is added to the zip.
*
* @param zOut The zip file's output stream
* @param path The filesystem path of the file/directory being added
* @param base The base prefix to for the name of the zip file entry
*
* @throws IOException If anything goes wrong
*/
private static void addFileToZip(ZipArchiveOutputStream zOut, String path, String base) throws IOException {
File f = new File(path);
String entryName = base + f.getName();
ZipArchiveEntry zipEntry = new ZipArchiveEntry(f, entryName);

zOut.putArchiveEntry(zipEntry);

if (f.isFile()) {
FileInputStream fInputStream = null;
try {
fInputStream = new FileInputStream(f);
IOUtils.copy(fInputStream, zOut);
zOut.closeArchiveEntry();
} finally {
IOUtils.closeQuietly(fInputStream);
}

} else {
zOut.closeArchiveEntry();
File[] children = f.listFiles();

if (children != null) {
for (File child : children) {
addFileToZip(zOut, child.getAbsolutePath(), entryName + "/");
}
}
}
}

}
Loading

0 comments on commit d9917af

Please sign in to comment.