-
-
Save rgriffith/28ca51a9f24d6d285d21 to your computer and use it in GitHub Desktop.
Sample Publish Trigger that reads back a published page from an FTP or SFTP server and puts it into a docx directory on the server.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.cms.publish.client; | |
import java.io.FileInputStream; | |
import java.io.FileOutputStream; | |
import java.util.zip.ZipEntry; | |
import java.util.zip.ZipOutputStream; | |
import org.apache.log4j.Logger; | |
import com.cms.publish.PublishTrigger; | |
import com.cms.publish.PublishTriggerEntityTypes; | |
import com.cms.publish.PublishTriggerException; | |
import com.cms.publish.PublishTriggerInformation; | |
import com.enterprisedt.net.ftp.FTPClient; | |
import com.enterprisedt.net.ftp.FTPTransferType; | |
import com.hannonhill.cascade.api.asset.admin.Destination; | |
import com.hannonhill.cascade.api.asset.admin.FTPTransport; | |
import com.hannonhill.cascade.api.asset.admin.Target; | |
import com.hannonhill.cascade.api.asset.admin.Transport; | |
import com.hannonhill.cascade.api.asset.common.BaseAsset; | |
import com.hannonhill.cascade.api.asset.common.Identifier; | |
import com.hannonhill.cascade.api.asset.common.PageConfiguration; | |
import com.hannonhill.cascade.api.asset.home.Page; | |
import com.hannonhill.cascade.api.operation.Read; | |
import com.hannonhill.cascade.api.operation.result.ReadOperationResult; | |
import com.hannonhill.cascade.model.dom.identifier.EntityType; | |
import com.hannonhill.cascade.model.dom.identifier.EntityTypes; | |
import com.jcraft.jsch.ChannelSftp; | |
import com.jcraft.jsch.JSch; | |
import com.jcraft.jsch.Session; | |
/** | |
* This trigger reads back the published page from the FTP or SFTP server and puts it into the docx directory | |
* on the server. | |
* Then, the docx directory is converted into a .docx file and sent back to the FTP or SFTP server. | |
* The docx directory must be downloaded and saved into cascade server /WEB-INF/classes/com/cms/publish/client | |
* folder. | |
* | |
* This trigger was originally written for Juilliard and integrated into the Cascade Server | |
* codebase on September 10, 2008. | |
* | |
* @author Artur Tomusiak | |
* @version $Id$ | |
* @since 5.7 | |
*/ | |
public class DocxPublishTrigger implements PublishTrigger | |
{ | |
private PublishTriggerInformation information; // information about currently published asset | |
private ZipOutputStream cpZipOutputStream = null; // used by the zip method | |
private String strSource = ""; // path to the directory that should be zipped | |
private String strTarget = ""; // path to a zip file that should be created | |
private final String currentFolderWithJAR = this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath(); | |
private final String currentFolderBeforeFix = currentFolderWithJAR.substring(0, currentFolderWithJAR.lastIndexOf('/') + 1); | |
private final String currentFolder = currentFolderBeforeFix.charAt(2) == ':' ? currentFolderBeforeFix.substring(1) : currentFolderBeforeFix; | |
private static final String DESTINATION_NAME = "Word Open XML"; // The name of destination for which this | |
// trigger should react | |
private static final String DOCX_PATH2 = "docx/"; // Path to the template folder. Don't use "../" | |
private final String docxPath = currentFolder + DOCX_PATH2; | |
private final String wordPath = docxPath + "openxml_word_template/word/"; // path to the folder, in which | |
// document.xml file should be | |
private static final Logger LOG = Logger.getLogger(DocxPublishTrigger.class); | |
/** | |
* Default constructor | |
*/ | |
public DocxPublishTrigger() | |
{ | |
super(); | |
} | |
public void setPublishInformation(PublishTriggerInformation information) | |
{ | |
this.information = information; | |
} | |
/** | |
* An identifier | |
* | |
* @author Artur Tomusiak | |
*/ | |
private class IdentifierImpl implements Identifier | |
{ | |
private final String id; | |
private final EntityType type; | |
public IdentifierImpl(String id, EntityType type) | |
{ | |
this.id = id; | |
this.type = type; | |
} | |
public String getId() | |
{ | |
return id; | |
} | |
public EntityType getType() | |
{ | |
return type; | |
} | |
} | |
/** | |
* This method gets executed every time Cascade Server publishes a file or page | |
* If it is a page and its publishing currently using destination "Word Open XML", | |
* it will load back the published file, put it into the word folder, | |
* zip the openxml_word_template folder contents and upload back the zipped archive | |
*/ | |
public void invoke() throws PublishTriggerException | |
{ | |
if (information.getEntityType() == PublishTriggerEntityTypes.TYPE_PAGE && information.getDestinationName().equals(DESTINATION_NAME)) | |
{ | |
LOG.debug("********************************"); | |
LOG.debug("DOC PUBLISH TRIGGER START"); | |
LOG.debug("Destination: " + information.getDestinationName()); | |
LOG.debug("Page: " + information.getEntityPath()); | |
LOG.debug("Page ID: " + information.getEntityId()); | |
LOG.debug("Page Configuration ID: " + information.getPageConfigurationId()); | |
try | |
{ | |
Transport transport = (Transport) readAsset(information.getTransportId(), EntityTypes.TYPE_TRANSPORT); | |
if (transport instanceof FTPTransport) | |
{ | |
FTPTransport ftpTransport = (FTPTransport) transport; | |
Destination destination = (Destination) readAsset(information.getDestinationId(), EntityTypes.TYPE_DESTINATION); | |
Page page = (Page) readAsset(information.getEntityId(), EntityTypes.TYPE_PAGE); | |
// Start logic that calculates the path of the published file on the server | |
String targetId = information.getTargetId(); | |
String serverName = ftpTransport.getServerName(); | |
int port = ftpTransport.getPort(); | |
String username = ftpTransport.getUserName(); | |
String password = ftpTransport.getPassword(); | |
String destinationDirectory = destination.getDirectory(); | |
String transportDirectory = ftpTransport.getInitialDirectory(); | |
String transportDestinationDirectory = getTransportDestination(transportDirectory, destinationDirectory); | |
String ftpFileName = page.getName(); | |
String ftpFolder = transportDestinationDirectory; | |
if (!isEmptyString(targetId)) | |
{ | |
Target target = (Target) readAsset(targetId, EntityTypes.TYPE_TARGET); | |
String targetPath = information.getTargetPath(); | |
boolean removeBaseFolder = target.isRemoveBaseFolderFromPubPath(); | |
boolean includeTargetPath = target.isShouldIncludeTargetPath(); | |
ftpFileName += target.getExtension(); | |
ftpFolder += (includeTargetPath ? addSlash(targetPath) : ""); | |
ftpFolder += addSlash(removeBaseFolder(page.getParentFolder().getPath(), target.getBaseFolder().getPath(), removeBaseFolder)); | |
} | |
else | |
{ | |
ftpFolder += addSlash(page.getParentFolder().getPath()); | |
PageConfiguration pageConfiguration = (PageConfiguration) readAsset(information.getPageConfigurationId(), | |
EntityTypes.TYPE_PAGECONFIGURATION); | |
ftpFileName += pageConfiguration.getExtension(); | |
} | |
String ftpPath = ftpFolder + "/" + ftpFileName; | |
String localPath = wordPath + ftpFileName; | |
// End logic that calculates the path of the published file on the server | |
boolean downloaded; | |
// Download the published file | |
if (ftpTransport.isSecure()) | |
downloaded = downloadSftpFile(serverName, port, username, password, ftpPath, localPath); | |
else | |
downloaded = downloadFtpFile(serverName, port, username, password, ftpPath, localPath); | |
if (!downloaded) | |
LOG.debug("Error downloading file from the ftp server"); | |
else | |
{ | |
// if it's not already document.xml, delete old document.xml and | |
// rename our file to document.xml | |
if (!ftpFileName.equals("document.xml")) | |
{ | |
// Deleting old document.xml file | |
System.gc();// workaround to fix java bug that doesn't allow to delete files that | |
// were created by java | |
boolean deleted = (new java.io.File(wordPath + "document.xml")).delete(); | |
if (!deleted) | |
LOG.debug("Could not delete the file"); | |
// Renaming document.xml | |
java.io.File f = new java.io.File(wordPath + ftpFileName); | |
f.renameTo(new java.io.File(wordPath + "document.xml")); | |
} | |
// Zipping openxml_word_template to .docx | |
strSource = docxPath + "openxml_word_template"; | |
strTarget = docxPath + page.getName() + ".docx"; | |
zip(); | |
// upload the zipped .docx file | |
String ftpFileNew = page.getName() + ".docx"; | |
String ftpPathNew = ftpFolder + "/" + ftpFileNew; | |
boolean uploaded; | |
if (ftpTransport.isSecure()) | |
uploaded = uploadSftpFile(serverName, port, username, password, strTarget, ftpPathNew); | |
else | |
uploaded = uploadFtpFile(serverName, port, username, password, strTarget, ftpPathNew); | |
if (!uploaded) | |
LOG.debug("Error uploading file to the ftp server"); | |
else | |
{ | |
// Delete the local .docx file | |
System.gc();// workaround to fix java bug that doesn't allow to delete files that | |
// were created by java | |
boolean deleted = (new java.io.File(docxPath + page.getName() + ".docx")).delete(); | |
if (!deleted) | |
LOG.debug("Could not delete the file .docx file"); | |
} | |
} | |
} | |
else | |
{ | |
LOG.debug("Error: The trigger works only for the FTP and SFTP transports"); | |
} | |
} | |
catch (Exception e) | |
{ | |
LOG.error(e.getMessage(), e); | |
} | |
LOG.debug("DOC PUBLISH TRIGGER END"); | |
LOG.debug("********************************"); | |
} | |
} | |
/** | |
* Reads an asset from CascadeServer using the Read operation API | |
* | |
* @param id | |
* @param type | |
* @return | |
* @throws Exception | |
*/ | |
private BaseAsset readAsset(String id, EntityType type) throws Exception | |
{ | |
Read read = new Read(); | |
Identifier toRead = new IdentifierImpl(id, type); | |
read.setToRead(toRead); | |
read.setUsername("system"); | |
ReadOperationResult result = (ReadOperationResult) read.perform(); | |
return result.getAsset(); | |
} | |
/** | |
* Takes the transport directory and destination directory and combines them together. If they match (for | |
* example transport | |
* directory is "/cascade" and destination directory is "/cascade/something"), then it will return only | |
* the destination | |
* directory ("/cascade/something"). If they don't match, they will be simply combined. | |
* | |
* @param transport The transport directory | |
* @param destination The destination directory | |
* @return The combined directory | |
*/ | |
private String getTransportDestination(String transport, String destination) | |
{ | |
if (destination.length() >= transport.length() && transport.equals(destination.substring(0, transport.length()))) | |
return removeSlashAtTheEnd(destination); | |
return removeSlashAtTheEnd(transport) + addSlash(destination); | |
} | |
/** | |
* Removes a slash from the end if it is there | |
* | |
* @param path | |
* @return | |
*/ | |
private String removeSlashAtTheEnd(String path) | |
{ | |
if (path.length() > 0) | |
{ | |
if (path.charAt(path.length() - 1) == '/') | |
return path.substring(0, path.length() - 1); | |
return path; | |
} | |
return path; | |
} | |
/** | |
* Adds a forward slash to the front of the path if necessary and removes | |
* the forward slash from the end. If the path is an empty string, it will return an empty string. | |
* | |
* @param path | |
* @return | |
*/ | |
private String addSlash(String path) | |
{ | |
path = removeSlashAtTheEnd(path); | |
if (path.length() > 0) | |
{ | |
if (path.charAt(0) == '/') | |
return path; | |
return "/" + path; | |
} | |
return path; | |
} | |
/** | |
* Returns a string that is the "path" without the "baseFolderPath". Works only if "shouldRemove" is true. | |
* If "shouldRemove" is false, | |
* it will return back the "path". | |
* | |
* @param path | |
* @param baseFolderPath | |
* @param shouldRemove | |
* @return | |
*/ | |
private String removeBaseFolder(String path, String baseFolderPath, boolean shouldRemove) | |
{ | |
String bigPath = addSlash(path); | |
String smallPath = addSlash(baseFolderPath); | |
if ((shouldRemove) && (bigPath.length() >= smallPath.length())) | |
return bigPath.substring(smallPath.length()); | |
return path; | |
} | |
/** | |
* Downloads a file from an SFTP server. | |
* | |
* @param serverName | |
* @param port | |
* @param username | |
* @param password | |
* @param fromPath | |
* @param toPath | |
* @return True if download successful. False if didn't download. | |
*/ | |
private boolean downloadSftpFile(String serverName, int port, String username, String password, String fromPath, String toPath) | |
{ | |
return transferSftpFile(serverName, port, username, password, fromPath, toPath, true); | |
} | |
/** | |
* Uploads a file to an SFTP server. | |
* | |
* @param serverName | |
* @param port | |
* @param username | |
* @param password | |
* @param fromPath | |
* @param toPath | |
* @return True if upload successful. False if didn't upload. | |
*/ | |
private boolean uploadSftpFile(String serverName, int port, String username, String password, String fromPath, String toPath) | |
{ | |
return transferSftpFile(serverName, port, username, password, fromPath, toPath, false); | |
} | |
/** | |
* Either downloads or uploads a file to/from an SFTP server. | |
* | |
* @param serverName | |
* @param port | |
* @param username | |
* @param password | |
* @param fromPath | |
* @param toPath | |
* @param download | |
* @return True if download/upload successful. False if didn't download/upload. | |
*/ | |
private boolean transferSftpFile(String serverName, int port, String username, String password, String fromPath, String toPath, boolean download) | |
{ | |
try | |
{ | |
JSch sshClient = new JSch(); | |
Session session = sshClient.getSession(username, serverName, port); | |
session.setConfig("StrictHostKeyChecking", "no"); | |
session.setTimeout(15000); | |
session.setPassword(password); | |
session.connect(); | |
ChannelSftp channel = (ChannelSftp) session.openChannel("sftp"); | |
channel.connect(); | |
if (download) | |
{ | |
LOG.debug("Downloading From SFTP"); | |
LOG.debug("To Path: " + toPath); | |
LOG.debug("From Path: " + fromPath); | |
channel.get(fromPath, toPath); | |
} | |
else | |
{ | |
LOG.debug("Uploading To SFTP"); | |
LOG.debug("To Path: " + toPath); | |
LOG.debug("From Path: " + fromPath); | |
channel.put(fromPath, toPath); | |
} | |
if (!channel.isClosed()) | |
channel.quit(); | |
if (!session.isConnected()) | |
session.disconnect(); | |
return true; | |
} | |
catch (Exception e) | |
{ | |
LOG.error(e.getMessage(), e); | |
return false; | |
} | |
} | |
/** | |
* Downloads a file from an FTP server. | |
* | |
* @param serverName | |
* @param port | |
* @param username | |
* @param password | |
* @param fromPath | |
* @param toPath | |
* @return True if download successful. False if didn't download. | |
*/ | |
private boolean downloadFtpFile(String serverName, int port, String username, String password, String fromPath, String toPath) | |
{ | |
return transferFtpFile(serverName, port, username, password, fromPath, toPath, true); | |
} | |
/** | |
* Uploads a file to an FTP server. | |
* | |
* @param serverName | |
* @param port | |
* @param username | |
* @param password | |
* @param fromPath | |
* @param toPath | |
* @return True if upload successful. False if didn't upload. | |
*/ | |
private boolean uploadFtpFile(String serverName, int port, String username, String password, String fromPath, String toPath) | |
{ | |
return transferFtpFile(serverName, port, username, password, fromPath, toPath, false); | |
} | |
/** | |
* Either downloads or uploads a file to/from an FTP server. | |
* | |
* @param serverName | |
* @param port | |
* @param username | |
* @param password | |
* @param fromPath | |
* @param toPath | |
* @param download | |
* @return True if download/upload successful. False if didn't download/upload. | |
*/ | |
private boolean transferFtpFile(String serverName, int port, String username, String password, String fromPath, String toPath, boolean download) | |
{ | |
FTPClient client = new FTPClient(); | |
try | |
{ | |
client.setRemoteHost(serverName); | |
client.setRemotePort(port); | |
client.connect(); | |
client.login(username, password); | |
client.setType(FTPTransferType.BINARY); | |
if (download) | |
{ | |
LOG.debug("Downloading From FTP"); | |
LOG.debug("To Path: " + toPath); | |
LOG.debug("From Path: " + fromPath); | |
client.get(toPath, fromPath); | |
return true; | |
} | |
LOG.debug("Uploading To FTP"); | |
LOG.debug("To Path: " + toPath); | |
LOG.debug("From Path: " + fromPath); | |
client.put(fromPath, toPath); | |
return true; | |
} | |
catch (Exception e) | |
{ | |
LOG.error("Error occurred: " + e.getMessage(), e); | |
return false; | |
} | |
finally | |
{ | |
try | |
{ | |
client.quit(); | |
} | |
catch (Exception e) | |
{ | |
LOG.debug("Error occurred when quitting the client: " + e.getMessage(), e); | |
} | |
} | |
} | |
/** | |
* Zips a file/folder with the path "strSource". The zipped file has path "strTarget" | |
*/ | |
private void zip() | |
{ | |
try | |
{ | |
java.io.File cpFile = new java.io.File(strSource); | |
if (!cpFile.isFile() && !cpFile.isDirectory()) | |
{ | |
LOG.debug("Zipper: Source file/directory Not Found!"); | |
return; | |
} | |
cpZipOutputStream = new ZipOutputStream(new FileOutputStream(strTarget)); | |
cpZipOutputStream.setLevel(0); | |
zipFiles(cpFile); | |
cpZipOutputStream.finish(); | |
cpZipOutputStream.close(); | |
LOG.debug("Finished creating zip file " + strTarget + " from source " + strSource); | |
} | |
catch (Exception e) | |
{ | |
LOG.error(e.getMessage(), e); | |
} | |
} | |
/** | |
* Zips the given {@link java.io.File}. | |
* | |
* @param cpFile | |
*/ | |
private void zipFiles(java.io.File cpFile) | |
{ | |
if (cpFile.isDirectory()) | |
{ | |
java.io.File[] fList = cpFile.listFiles(); | |
for (int i = 0; i < fList.length; i++) | |
{ | |
zipFiles(fList[i]); | |
} | |
} | |
else | |
{ | |
String strAbsPath = cpFile.getAbsolutePath(); | |
String strZipEntryName = strAbsPath.substring(strSource.length() + 1); | |
byte[] b = new byte[(int) (cpFile.length())]; | |
FileInputStream cpFileInputStream = null; | |
try | |
{ | |
cpFileInputStream = new FileInputStream(cpFile); | |
cpFileInputStream.read(b, 0, (int) cpFile.length()); | |
ZipEntry cpZipEntry = new ZipEntry(strZipEntryName); | |
cpZipOutputStream.putNextEntry(cpZipEntry); | |
cpZipOutputStream.write(b, 0, (int) cpFile.length()); | |
cpZipOutputStream.closeEntry(); | |
} | |
catch (Exception e) | |
{ | |
LOG.error(e.getMessage(), e); | |
} | |
finally | |
{ | |
try | |
{ | |
if (cpFileInputStream != null) | |
cpFileInputStream.close(); | |
} | |
catch (Exception e) | |
{ | |
LOG.error("Error closing input stream for file '" + cpFile + "': " + e.getMessage(), e); | |
} | |
} | |
} | |
} | |
public void setParameter(String name, String value) | |
{ | |
LOG.debug("setParameter: name: " + name + " value: " + value); | |
} | |
/** | |
* Returns true if the string is empty or null | |
* | |
* @param s | |
* @return | |
*/ | |
private boolean isEmptyString(String s) | |
{ | |
return s == null || s.equals(""); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment