Goal
This is Part-1 of Migrating the Smart Crop Settings of images in a DM Classic (Scene7) account to a different Scene7 account. Executing the standalone class apps.GenerateSmartCropsLog, smart crop settings of all assets in the account (or a folder) are written to a log all-smart-crops.csv..
Part 2 is for updating the smart crop settings in destination Scene7 account
Running the tool
1) Install Java JRE 8
2) Extract migrate-smart-crops.zip into a folder migrate-smart-crops
3) Set the following properties in migrate-smart-crops\apps\config.properties
a. when path is empty the entire account is traversed or set path to a Scene7 folder eg. EAEM/experience-aem
b. Set the src scene7 account credentials in companyHandle/user/pass format (the company handle is available in AEM at location /conf/global/settings/cloudconfigs/dmscene7/jcr:content@companyHandle)
eg. src=c|1111111/user@company.com/NeverSharePassword
4) Running migrate-smart-crops/fetch.bat reads the smart crops from source S7 account and adds in csv migrate-smart-crops/apps/all-smart-crops.csv
5) Or for manually running the fetch "C:\Program Files\Java\jdk1.8.0_221\bin\java" -classpath ".;./libs/*" apps.GenerateSmartCropsLog
Code
The following java class apps.GenerateSmartCropsLog contains logic to connect to DM Classic (Scene7) and fetch the smart crop settings of each asset in the account...
package apps;
import com.scene7.ipsapi.*;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.net.URL;
import java.util.*;
public class GenerateSmartCropsLog {
private static String S7_NA_IPS_URL = "https://s7sps1apissl.scene7.com/scene7/api/IpsApiService";
private static String SRC_S7_COMPANY_HANDLE = "";
private static String SRC_S7_USER = "";
private static String SRC_S7_PASS = "";
private static String STAND_ALONE_APP_NAME = "Experiencing AEM";
private static String logName = "all-smart-crops.csv";
private static BufferedWriter LOG_WRITER = null;
private static int assetCount = 0;
public static void main(String[] args) throws Exception {
String path = setProperties();
if((path == null) || path.trim().isEmpty()){
System.out.println("INFO : Reading smart crops of all assets in account : " + SRC_S7_COMPANY_HANDLE);
browseFolder(null);
}else{
System.out.println("INFO : Reading smart crops of all assets in folder and sub folders : " + path);
S7Folder rootFolder = new S7Folder();
rootFolder.setFolderPath(path);
browseFolder(rootFolder);
}
System.out.println("TOTAL ASSETS READ : " + assetCount);
LOG_WRITER.flush();
LOG_WRITER.close();
}
private static String setProperties(){
String path = "";
try{
URL propFile = GenerateSmartCropsLog.class.getResource("config.properties");
System.out.println("INFO : Reading configuration from : " + propFile.getPath());
InputStream input = new FileInputStream(propFile.getPath());
Properties prop = new Properties();
prop.load(input);
String srcAccount = prop.getProperty("src");
if((srcAccount == null) || srcAccount.trim().equals("")){
System.out.println("ERROR: 'src' property not found in config.properties");
System.exit(-1);
}
String[] words = srcAccount.split("/");
if(words.length != 3){
System.out.println("ERROR: 'src' property format is 's7CompanyHandle/user/pass' eg.'c|230095/user@company.com/password'");
System.exit(-1);
}
SRC_S7_COMPANY_HANDLE = words[0];
SRC_S7_USER = words[1];
SRC_S7_PASS = words[2];
path = prop.getProperty("path");
LOG_WRITER = new BufferedWriter(new FileWriter((new File(propFile.getPath())).getParentFile().getPath() + "/" + logName));
}catch(Exception e){
System.out.println("ERROR: Reading config.properties, is it in the current folder? - " + e.getMessage());
e.printStackTrace();
System.exit(-1);
}
return path;
}
private static void browseFolder(S7Folder s7Folder) throws Exception {
AuthHeader authHeader = getS7AuthHeader();
Marshaller marshaller = getMarshaller(AuthHeader.class);
StringWriter sw = new StringWriter();
marshaller.marshal(authHeader, sw);
String authHeaderStr = sw.toString();
GetFolderTreeParam getFolderTreeParam = getGetFolderTreeParam(s7Folder);
marshaller = getMarshaller(getFolderTreeParam.getClass());
sw = new StringWriter();
marshaller.marshal(getFolderTreeParam, sw);
String apiMethod = sw.toString();
byte[] responseBody = getResponse(authHeaderStr, apiMethod);
List<S7Folder> folders = parseResponse(responseBody, s7Folder);
folders.forEach(folder -> {
if(folder.isHasSubFolders()){
try{
browseFolder(folder);
}catch (Exception e){
e.printStackTrace();
}
}
});
printFolders(s7Folder, folders);
}
private static void printFolders(S7Folder parentFolder, List<S7Folder> folders){
if( ( parentFolder == null ) && folders.isEmpty()){
System.out.println("No folders or assets in account - " + SRC_S7_COMPANY_HANDLE);
return;
}
if(folders.isEmpty()){
System.out.println("No folders or assets under - " + parentFolder.getFolderPath());
return;
}
if(parentFolder == null){
System.out.println(SRC_S7_COMPANY_HANDLE);
}else{
System.out.println(parentFolder.getFolderPath());
}
folders.forEach(folder -> {
List<S7Asset> assets = folder.getAssets();
assetCount = assetCount + assets.size();
System.out.println("\t" + folder.getFolderPath() + " - " + assets.size());
assets.forEach(asset -> {
try {
LOG_WRITER.write(asset.getAssetPath() + "," + asset.getCropRectsStr());
LOG_WRITER.write("\r\n");
LOG_WRITER.flush();
}catch (Exception lw){
System.out.println("ERROR: writing to log : " + lw.getMessage());
}
});
});
}
private static List<S7Folder> parseResponse(byte[] responseBody, S7Folder parentFolder) throws Exception{
List<S7Folder> folders = new ArrayList<S7Folder>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
ByteArrayInputStream input = new ByteArrayInputStream(responseBody);
Document doc = builder.parse(input);
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/getFolderTreeReturn/folders";
if( (parentFolder != null) && (parentFolder.getFolderHandle() != null)){
expression = "/getFolderTreeReturn/folders/subfolderArray";
}
NodeList itemList = (NodeList) xPath.compile(expression).evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < itemList.getLength(); i++) {
Node item = itemList.item(i);
if( (parentFolder != null) && (parentFolder.getFolderHandle() != null)){
NodeList childList = item.getChildNodes();
for (int j = 0; j < childList.getLength(); j++) {
folders.add(fillFolderDetail(childList.item(j)));
}
}else{
folders.add(fillFolderDetail(item));
}
}
return folders;
}
private static S7Folder fillFolderDetail(Node item) throws Exception{
S7Folder s7Folder = new S7Folder();
if(item.getNodeType() == Node.ELEMENT_NODE) {
Element eElement = (Element) item;
s7Folder.setFolderHandle(eElement.getElementsByTagName("folderHandle").item(0).getTextContent());
s7Folder.setFolderPath(eElement.getElementsByTagName("path").item(0).getTextContent());
s7Folder.setHasSubFolders(new Boolean(eElement.getElementsByTagName("hasSubfolders").item(0).getTextContent()));
fillAssetDetails(s7Folder);
}
return s7Folder;
}
private static void fillAssetDetails(S7Folder s7Folder) throws Exception{
AuthHeader authHeader = getS7AuthHeader();
Marshaller marshaller = getMarshaller(AuthHeader.class);
StringWriter sw = new StringWriter();
marshaller.marshal(authHeader, sw);
String authHeaderStr = sw.toString();
SearchAssetsParam searchAssetsParam = getSearchAssetsParam(s7Folder);
marshaller = getMarshaller(searchAssetsParam .getClass());
sw = new StringWriter();
marshaller.marshal(searchAssetsParam , sw);
String apiMethod = sw.toString();
byte[] responseBody = getResponse(authHeaderStr, apiMethod);
parseSearchResponse(responseBody, s7Folder);
}
private static void parseSearchResponse(byte[] responseBody, S7Folder parentFolder) throws Exception{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
ByteArrayInputStream input = new ByteArrayInputStream(responseBody);
Document doc = builder.parse(input);
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/searchAssetsReturn/assetArray/items";
NodeList itemList = (NodeList) xPath.compile(expression).evaluate(doc, XPathConstants.NODESET);
List<S7Asset> assets = new ArrayList<S7Asset>();
for (int i = 0; i < itemList.getLength(); i++) {
Node item = itemList.item(i);
if(item.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
S7Asset s7asset = new S7Asset();
Element eElement = (Element) item;
s7asset.setType(getTextContent(eElement, "type"));
s7asset.setAssetHandle(getTextContent(eElement, "assetHandle"));
s7asset.setAssetPath(getTextContent(eElement, "folder") + getTextContent(eElement, "fileName"));
NodeList imageInfoList = eElement.getElementsByTagName("imageInfo");
for (int j = 0; j < imageInfoList.getLength(); j++) {
Node imageInfo = imageInfoList.item(j);
if(imageInfo.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
s7asset.setAssetSize(new Long(getTextContent((Element)imageInfo, "fileSize")));
}
if("Image".equals(s7asset.getType())){
Map<String, String> cropRects = getSmartCropsCropRects(s7asset.getAssetHandle());
s7asset.setCropRects(cropRects);
}
assets.add(s7asset);
}
parentFolder.setAssets(assets);
}
public static Map<String, String> getSmartCropsCropRects(String assetHandle) throws Exception{
AuthHeader authHeader = getS7AuthHeader();
Marshaller marshaller = getMarshaller(AuthHeader.class);
StringWriter sw = new StringWriter();
marshaller.marshal(authHeader, sw);
String authHeaderStr = sw.toString();
GetAssociatedAssetsParam getAssociatedAssetsParam = new GetAssociatedAssetsParam();
getAssociatedAssetsParam.setCompanyHandle(SRC_S7_COMPANY_HANDLE);
getAssociatedAssetsParam.setAssetHandle(assetHandle);
marshaller = getMarshaller(AuthHeader.class);
sw = new StringWriter();
marshaller.marshal(authHeader, sw);
marshaller = getMarshaller(getAssociatedAssetsParam.getClass());
sw = new StringWriter();
marshaller.marshal(getAssociatedAssetsParam, sw);
String apiMethod = sw.toString();
byte[] responseBody = getResponse(authHeaderStr, apiMethod);
return parseResponseForCropRects(responseBody);
}
private static Map<String, String> parseResponseForCropRects(byte[] responseBody) throws Exception{
Map<String, String> cropRects = new HashMap<String, String>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
ByteArrayInputStream input = new ByteArrayInputStream(responseBody);
Document doc = builder.parse(input);
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/getAssociatedAssetsReturn/subAssetArray/items";
NodeList itemList = (NodeList) xPath.compile(expression).evaluate(doc, XPathConstants.NODESET);
String subAssetHandle = null, name, cropRectStr;
for (int i = 0; i < itemList.getLength(); i++) {
Node item = itemList.item(i);
if(item.getNodeType() == Node.ELEMENT_NODE) {
Element eElement = (Element) item;
subAssetHandle = eElement.getElementsByTagName("subAssetHandle").item(0).getTextContent();
name = eElement.getElementsByTagName("name").item(0).getTextContent();
try{
Element cropRectEle = (Element)((Element)eElement.getElementsByTagName("smartCrop").item(0))
.getElementsByTagName("cropRect").item(0);
cropRectStr = getCropRectStr(cropRectEle);
}catch(Exception ce){
//ignore no smart crop images
cropRectStr = "";
}
cropRects.put(name, cropRectStr);
}
}
return cropRects;
}
private static String getCropRectStr(Element cropRectEle){
StringWriter sw = new StringWriter();
sw.append("leftN=").append(cropRectEle.getElementsByTagName("leftN").item(0).getTextContent()).append("|");
sw.append("topN=").append(cropRectEle.getElementsByTagName("topN").item(0).getTextContent()).append("|");
sw.append("widthN=").append(cropRectEle.getElementsByTagName("widthN").item(0).getTextContent()).append("|");
sw.append("heightN=").append(cropRectEle.getElementsByTagName("heightN").item(0).getTextContent()).append("|");
return sw.toString();
}
private static String getTextContent(Element eElement, String tagName){
return eElement.getElementsByTagName(tagName).item(0).getTextContent();
}
private static byte[] getResponse(String authHeaderStr, String apiMethod) throws Exception{
StringBuilder requestBody = new StringBuilder("<Request xmlns=\"http://www.scene7.com/IpsApi/xsd/2017-10-29-beta\">");
requestBody.append(authHeaderStr).append(apiMethod).append("</Request>");
byte[] responseBody = null;
PostMethod postMethod = null;
try {
HttpClient httpclient = new HttpClient();
postMethod = new PostMethod(S7_NA_IPS_URL);
postMethod.setRequestHeader("Content-Type", "text/xml");
postMethod.setRequestEntity(new ByteArrayRequestEntity(requestBody.toString().getBytes()));
int responseCode = httpclient.executeMethod(postMethod);
if(responseCode != 200){
System.out.println("Response code - " + responseCode + ", returning here...");
}
InputStream is = postMethod.getResponseBodyAsStream();
responseBody = IOUtils.toByteArray(is);
}finally{
if(postMethod != null){
postMethod.releaseConnection();
}
}
return responseBody;
}
private static AuthHeader getS7AuthHeader(){
AuthHeader authHeader = new AuthHeader();
authHeader.setUser(SRC_S7_USER);
authHeader.setPassword(SRC_S7_PASS);
authHeader.setAppName(STAND_ALONE_APP_NAME);
authHeader.setAppVersion("1.0");
authHeader.setFaultHttpStatusCode(new Integer(200));
return authHeader;
}
private static GetFolderTreeParam getGetFolderTreeParam(S7Folder s7Folder){
GetFolderTreeParam getFolderTreeParam = new GetFolderTreeParam();
getFolderTreeParam.setCompanyHandle(SRC_S7_COMPANY_HANDLE);
getFolderTreeParam.setDepth(1);
if(s7Folder != null){
getFolderTreeParam.setFolderPath(s7Folder.getFolderPath());
}
return getFolderTreeParam;
}
private static SearchAssetsParam getSearchAssetsParam(S7Folder s7Folder){
SearchAssetsParam searchAssetsParam = new SearchAssetsParam();
StringArray assetTypes = new StringArray();
assetTypes.getItems().add("Image");
searchAssetsParam.setCompanyHandle(SRC_S7_COMPANY_HANDLE);
searchAssetsParam.setFolder(s7Folder.getFolderPath());
searchAssetsParam.setIncludeSubfolders(false);
searchAssetsParam.setAssetTypeArray(assetTypes);
return searchAssetsParam;
}
private static Marshaller getMarshaller(Class apiMethodClass) throws JAXBException {
Marshaller marshaller = JAXBContext.newInstance(new Class[]{apiMethodClass}).createMarshaller();
marshaller.setProperty("jaxb.formatted.output", Boolean.valueOf(true));
marshaller.setProperty("jaxb.fragment", Boolean.valueOf(true));
return marshaller;
}
private static class S7Folder{
private String folderHandle;
private String folderPath;
private boolean hasSubFolders;
private List<S7Asset> assets;
public String getFolderHandle() {
return folderHandle;
}
public void setFolderHandle(String folderHandle) {
this.folderHandle = folderHandle;
}
public String getFolderPath() {
return folderPath;
}
public void setFolderPath(String folderPath) {
this.folderPath = folderPath;
}
public boolean isHasSubFolders() {
return hasSubFolders;
}
public void setHasSubFolders(boolean hasSubFolders) {
this.hasSubFolders = hasSubFolders;
}
public List<S7Asset> getAssets() {
return assets;
}
public void setAssets(List<S7Asset> assets) {
this.assets = assets;
}
public String toString(){
return "[" + folderHandle + "," + folderPath + "," + hasSubFolders + "]";
}
}
private static class S7Asset{
private String type;
private String assetHandle;
private String assetPath;
private long assetSize;
private Map<String, String> cropRects;
public String getAssetHandle() {
return assetHandle;
}
public void setAssetHandle(String assetHandle) {
this.assetHandle = assetHandle;
}
public String getAssetPath() {
return assetPath;
}
public void setAssetPath(String assetPath) {
this.assetPath = assetPath;
}
public long getAssetSize() {
return assetSize;
}
public void setAssetSize(long assetSize) {
this.assetSize = assetSize;
}
public Map<String, String> getCropRects() {
return cropRects;
}
public String getCropRectsStr() {
if(cropRects == null){
return "";
}
StringWriter sw = new StringWriter();
cropRects.forEach((k, v) -> sw.append(k).append(":").append(v).append(","));
return sw.toString();
}
public void setCropRects(Map<String, String> cropRects) {
this.cropRects = cropRects;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
}