Java tutorial
/** * Copyright 2009 Welocalize, Inc. * * Licensed under the Apache 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.apache.org/licenses/LICENSE-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 com.globalsight.connector.mindtouch.util; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URL; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Vector; import org.apache.commons.codec.binary.Base64; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.cookie.Cookie; import org.apache.http.cookie.CookieOrigin; import org.apache.http.cookie.CookieSpec; import org.apache.http.cookie.CookieSpecProvider; import org.apache.http.cookie.MalformedCookieException; import org.apache.http.entity.FileEntity; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.cookie.BestMatchSpecFactory; import org.apache.http.impl.cookie.BrowserCompatSpec; import org.apache.http.impl.cookie.BrowserCompatSpecFactory; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import org.apache.log4j.Logger; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.json.JSONObject; import org.xml.sax.InputSource; import com.globalsight.connector.mindtouch.MindTouchManager; import com.globalsight.connector.mindtouch.vo.MindTouchPage; import com.globalsight.connector.mindtouch.vo.MindTouchPageInfo; import com.globalsight.cxe.entity.mindtouch.MindTouchConnector; import com.globalsight.cxe.entity.mindtouch.MindTouchConnectorTargetServer; import com.globalsight.everest.servlet.EnvoyServletException; import com.globalsight.everest.servlet.util.ServerProxy; import com.globalsight.everest.util.comparator.GlobalSightLocaleComparator; import com.globalsight.ling.common.URLEncoder; import com.globalsight.util.AmbFileStoragePathUtils; import com.globalsight.util.FileUtil; import com.globalsight.util.GlobalSightLocale; import com.globalsight.util.SortUtil; import com.globalsight.util.StringUtil; import com.globalsight.util.edit.EditUtil; public class MindTouchHelper { static private final Logger logger = Logger.getLogger(MindTouchHelper.class); private MindTouchConnector mtc = null; private HashMap<String, MindTouchConnectorTargetServer> targetServersMap = null; private CloseableHttpClient httpClient = null; public MindTouchHelper(MindTouchConnector mtc) { this.mtc = mtc; setTargetServersMap(); } /** * Test if it can connect to MindTouch server successfully. * * @return error message if failed; return null if successfully. */ public String doTest() { CloseableHttpClient httpClient = getHttpClient(); HttpResponse httpResponse = null; try { String url = mtc.getUrl() + "/@api/deki/pages/home/info"; HttpGet httpget = getHttpGet(url); httpResponse = httpClient.execute(httpget); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode == 200) { return null; } else { return httpResponse.getStatusLine().toString(); } } catch (Exception e) { logger.warn("Fail to test MindTouch connector: " + e.getMessage()); return "Failed to connect to MindTouch server"; } finally { consumeQuietly(httpResponse); } } public void deletePage(long pageId) throws Exception { CloseableHttpClient httpClient = getHttpClient(); HttpResponse httpResponse = null; try { String url = mtc.getUrl() + "/@api/deki/pages/" + pageId; HttpDelete httpDelete = getHttpDelete(url); httpResponse = httpClient.execute(httpDelete); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode != 200) { logger.error("Fail to delete page: " + pageId + ", returning info is: " + EntityUtils.toString(httpResponse.getEntity())); } } catch (Exception e) { logger.error("Fail to delete page: " + pageId, e); } finally { consumeQuietly(httpResponse); } } /** * Get the tree in XML format for specified pageId. For root page, the * parameter can be "home". */ public String getTreeXml(String pageId) { CloseableHttpClient httpClient = getHttpClient(); HttpResponse httpResponse = null; try { String url = mtc.getUrl() + "/@api/deki/pages/" + pageId + "/tree"; HttpGet httpget = getHttpGet(url); httpResponse = httpClient.execute(httpget); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode == 200) { return EntityUtils.toString(httpResponse.getEntity()); } else { logger.warn("Fail to get sitemap tree: " + httpResponse.getStatusLine().toString()); return null; } } catch (Exception e) { logger.error("Fail to get sitemap tree: " + e.getMessage()); return null; } finally { consumeQuietly(httpResponse); } } /** * Parse tree xml from "getTreeXml(pageId)" method to form a tree. * * @param treeXml * -- the tree information in XML format. * @return MindTouchPage */ @SuppressWarnings("rawtypes") public MindTouchPage parseTreeXml(String treeXml) throws Exception { MindTouchPage rootMtp = null; Document doc = getDocument(treeXml); String id = null; String href = null; List pageNodes = doc.selectNodes("//page"); List<MindTouchPage> allPages = new ArrayList<MindTouchPage>(); Iterator it = pageNodes.iterator(); while (it.hasNext()) { MindTouchPage mtp = new MindTouchPage(); Element pageNode = (Element) it.next(); // page id id = pageNode.attributeValue("id"); mtp.setId(Long.parseLong(id)); // href href = pageNode.attributeValue("href"); mtp.setHref(href); // parent page id String parentName = null; if (pageNode.getParent() != null) { parentName = pageNode.getParent().getName(); } if ("subpages".equals(parentName)) { String parentId = pageNode.getParent().getParent().attributeValue("id"); mtp.setParentId(Long.parseLong(parentId)); } else if ("pages".equals(parentName)) { rootMtp = mtp; } Iterator subNodeIt = pageNode.nodeIterator(); while (subNodeIt.hasNext()) { Element node = (Element) subNodeIt.next(); String name = node.getName(); String text = node.getText(); if ("uri.ui".equals(name)) { mtp.setUriUi(text); } else if ("title".equals(name)) { // title cannot have "<" and ">" text = text.replace("<", "<").replace(">", ">"); // as json does not allow "\" and "/", remove them for displaying. text = text.replace("\\", "").replace("/", ""); text = text.replace("%22", "\""); text = text.replace("%3F", "?"); text = text.replace("%23", "#"); text = text.replace("%3D", "="); text = text.replace("%26", "&"); text = text.replace("%25", "%"); mtp.setTitle(text); } else if ("path".equals(name)) { mtp.setPath(text); } else if ("date.created".equals(name)) { mtp.setDateCreated(text); } } allPages.add(mtp); } HashMap<Long, MindTouchPage> map = new HashMap<Long, MindTouchPage>(); for (MindTouchPage mtp : allPages) { map.put(mtp.getId(), mtp); } for (MindTouchPage mtp : allPages) { long parentId = mtp.getParentId(); MindTouchPage parent = map.get(parentId); if (parent != null) { parent.addSubPage(mtp); } } return rootMtp; } /** * Get page contents with "contents" API. * * @param pageId */ public String getPageContents(String pageId) { CloseableHttpClient httpClient = getHttpClient(); HttpResponse httpResponse = null; try { String url = mtc.getUrl() + "/@api/deki/pages/" + pageId + "/contents?mode=edit"; HttpGet httpget = getHttpGet(url); httpResponse = httpClient.execute(httpget); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode == 200) { return EntityUtils.toString(httpResponse.getEntity()); } else { logger.warn("Fail to get page content for pageId " + pageId + " : " + httpResponse.getStatusLine().toString()); } } catch (Exception e) { logger.error("Fail to get page content for pageId: " + pageId, e); } finally { consumeQuietly(httpResponse); } return null; } /** * Get page tags with "tags" API. * * @param pageId * @return String */ public String getPageTags(long pageId) { CloseableHttpClient httpClient = getHttpClient(); HttpResponse httpResponse = null; try { String url = mtc.getUrl() + "/@api/deki/pages/" + pageId + "/tags"; HttpGet httpget = getHttpGet(url); httpResponse = httpClient.execute(httpget); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode == 200) { return EntityUtils.toString(httpResponse.getEntity()); } else { logger.warn("Fail to get page tags for pageId " + pageId + " : " + httpResponse.getStatusLine().toString()); } } catch (Exception e) { logger.error("Fail to get page tags for pageId: " + pageId, e); } finally { consumeQuietly(httpResponse); } return null; } /** * Get page properties with "properties" API. * * @param pageId * @return String */ public String getPageProperties(long pageId) { CloseableHttpClient httpClient = getHttpClient(); HttpResponse httpResponse = null; try { String url = mtc.getUrl() + "/@api/deki/pages/" + pageId + "/properties"; HttpGet httpget = getHttpGet(url); httpResponse = httpClient.execute(httpget); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode == 200) { return EntityUtils.toString(httpResponse.getEntity()); } else { logger.warn("Fail to get page properties for pageId " + pageId + " : " + httpResponse.getStatusLine().toString()); } } catch (Exception e) { logger.error("Fail to get page properties for pageId: " + pageId, e); } finally { consumeQuietly(httpResponse); } return null; } public String getPageProperties(String url, String pagePath) { CloseableHttpClient httpClient = getHttpClient(); HttpResponse httpResponse = null; try { url += "/@api/deki/pages/=" + pagePath + "/properties"; HttpGet httpget = getHttpGet(url); httpResponse = httpClient.execute(httpget); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode == 200) { return EntityUtils.toString(httpResponse.getEntity()); } else { logger.warn("Fail to get page properties for pagePath " + pagePath + " : " + httpResponse.getStatusLine().toString()); } } catch (Exception e) { logger.error("Fail to get page properties for pagePath: " + pagePath, e); } finally { consumeQuietly(httpResponse); } return null; } public String getPageFiles(String pageId) { CloseableHttpClient httpClient = getHttpClient(); String url = mtc.getUrl() + "/@api/deki/pages/" + pageId + "/files"; int count = 0; String pageFilesXml = null; while (pageFilesXml == null && count < 3) { count++; if (count > 1) { logger.info("Retry to getPageFiles for url: " + url); } HttpResponse httpResponse = null; try { HttpGet httpget = getHttpGet(url); httpResponse = httpClient.execute(httpget); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode == 200) { pageFilesXml = EntityUtils.toString(httpResponse.getEntity()); } else { logger.warn("Fail to get page files for pageId " + pageId + " : " + httpResponse.getStatusLine().toString()); } } catch (Exception e) { logger.error("Fail to get page files for pageId: " + pageId, e); } finally { consumeQuietly(httpResponse); } } return pageFilesXml; } public String getPageFile(String url) { logger.info("getPageFile url: " + url); CloseableHttpClient httpClient = getHttpClient(); HttpResponse httpResponse = null; try { HttpGet httpget = getHttpGet(url); httpResponse = httpClient.execute(httpget); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode == 200) { String fileName = url.substring(url.lastIndexOf("/") + 1); File docFolder = AmbFileStoragePathUtils.getCxeDocDir(mtc.getCompanyId()); String filePath = docFolder + File.separator + "MindTouchConnectorFiles" + File.separator + fileName; File storeFile = new File(filePath); storeFile.getParentFile().mkdirs(); if (!storeFile.exists()) { storeFile.createNewFile(); } FileOutputStream output = new FileOutputStream(storeFile); InputStream instream = httpResponse.getEntity().getContent(); byte b[] = new byte[1024]; int j = 0; while ((j = instream.read(b)) != -1) { output.write(b, 0, j); } output.flush(); output.close(); instream.close(); return filePath; } else { logger.warn( "Fail to get page file for url " + url + " : " + httpResponse.getStatusLine().toString()); } } catch (Exception e) { logger.error("Fail to get page file for url: " + url, e); } finally { consumeQuietly(httpResponse); } return null; } @SuppressWarnings("rawtypes") public String handleFiles(String pageId, String content, String targetLocale, String sourceLocale, MindTouchPageInfo pageInfo) throws DocumentException { String filesXml = getPageFiles(pageId); if (StringUtil.isEmpty(filesXml)) { return content; } HashMap<String, String> fileMap = new HashMap<String, String>(); Document doc = getDocument(filesXml); List propertyNodes = doc.selectNodes("//contents "); Iterator it = propertyNodes.iterator(); String sourceFileUrl = null; while (it.hasNext()) { Element propertyNode = (Element) it.next(); sourceFileUrl = propertyNode.attributeValue("href"); String filePath = getPageFile(sourceFileUrl); if (filePath != null) { fileMap.put(sourceFileUrl, filePath); } } if (fileMap.size() > 0) { for (String tempSourceFileUrl : fileMap.keySet()) { String fileXml = putPageFile(fileMap.get(tempSourceFileUrl), targetLocale, sourceLocale, pageInfo); if (StringUtil.isNotEmpty(fileXml)) { doc = getDocument(fileXml); propertyNodes = doc.selectNodes("//contents "); it = propertyNodes.iterator(); while (it.hasNext()) { Element propertyNode = (Element) it.next(); String targetFileUrl = propertyNode.attributeValue("href"); fileMap.put(tempSourceFileUrl, targetFileUrl); } } } for (String tempSourceFileUrl : fileMap.keySet()) { content = StringUtil.replace(content, tempSourceFileUrl, fileMap.get(tempSourceFileUrl)); } } return content; } /** * Get page info with "info" API. * * @param pageId * @return String */ public String getPageInfo(String url, long pageId) { url += "/@api/deki/pages/" + pageId + "/info"; return getPageInfo2(url); } /** * Get page info with "info" API. * * @param path * @return String */ public String getPageInfo(String url, String path) { url += "/@api/deki/pages/=" + path + "/info"; return getPageInfo2(url); } private String getPageInfo2(String url) { CloseableHttpClient httpClient = getHttpClient(); HttpResponse httpResponse = null; try { HttpGet httpget = getHttpGet(url); httpResponse = httpClient.execute(httpget); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode == 200) { return EntityUtils.toString(httpResponse.getEntity()); } } catch (Exception e) { logger.warn("Fail to get page info for page: " + url, e); } finally { consumeQuietly(httpResponse); } return null; } /** * Parse page info xml from "getPageInfo()" method. * * @param pageInfoXml * @return MindTouchPage * @throws DocumentException */ @SuppressWarnings("rawtypes") public MindTouchPage parsePageInfoXml(String pageInfoXml) throws DocumentException { MindTouchPage mtp = new MindTouchPage(); Document doc = getDocument(pageInfoXml); String id = null; String href = null; List pageNodes = doc.selectNodes("//page"); if (pageNodes != null && pageNodes.size() > 0) { Element pageNode = (Element) pageNodes.get(0); // page id id = pageNode.attributeValue("id"); mtp.setId(Long.parseLong(id)); // href href = pageNode.attributeValue("href"); mtp.setHref(href); String name = null; String text = null; Iterator subNodeIt = pageNode.nodeIterator(); while (subNodeIt.hasNext()) { Element node = (Element) subNodeIt.next(); name = node.getName(); text = node.getText(); if ("uri.ui".equals(name)) { mtp.setUriUi(text); } else if ("title".equals(name)) { mtp.setTitle(text); } else if ("path".equals(name)) { mtp.setPath(text); } else if ("date.created".equals(name)) { mtp.setDateCreated(text); } } } return mtp; } /** * Send the translated contents back to MindTouch server via pages "post" * API. If the path specified page has already exists, it will be * updated;Otherwise, create a new page. * * @param contentsTrgFile * @param pageInfo * @param targetLocale * @throws Exception */ public void postPageContents(File contentsTrgFile, MindTouchPageInfo pageInfo, String sourceLocale, String targetLocale) throws Exception { if (!isTargetServerExist(targetLocale) && !mtc.getIsPostToSourceServer()) { return; } CloseableHttpClient httpClient = getHttpClient(); String path = null; try { // to be safe, it must use "text/plain" content type instead of // "text/xml" or "application/xml". String content = FileUtil.readFile(contentsTrgFile, "UTF-8"); content = StringUtil.replace(content, " ", " "); String title = getTitleFromTranslatedContentXml(content); content = fixTitleValueInContentXml(content); // Only when target server exists, do this... if (isTargetServerExist(targetLocale)) { content = handleFiles(pageInfo.getPageId(), content, targetLocale, sourceLocale, pageInfo); } int times = 0; while (times < 2) { times++; HttpResponse response = null; try { String tmpContent = content; tmpContent = EditUtil.decodeXmlEntities(tmpContent); // empty body if (tmpContent.indexOf("<body/>") > -1) { tmpContent = ""; } // normal case else { tmpContent = tmpContent.substring(tmpContent.indexOf("<body>") + 6); tmpContent = tmpContent.substring(0, tmpContent.indexOf("</body>")); } StringEntity reqEntity = new StringEntity(tmpContent, "UTF-8"); reqEntity.setContentType("text/plain; charset=UTF-8"); path = getNewPath(pageInfo, sourceLocale, targetLocale); String strUrl = getPutServerUrl(targetLocale) + "/@api/deki/pages/=" + path + "/contents?edittime=now&abort=never"; if (title != null) { strUrl += "&title=" + title; } URL url = new URL(strUrl); URI uri = new URI(url.getProtocol(), url.getHost(), url.getPath(), url.getQuery(), null); HttpPost httppost = getHttpPost(uri, targetLocale); httppost.setEntity(reqEntity); response = httpClient.execute(httppost); String entityContent = null; if (response.getEntity() != null) { entityContent = EntityUtils.toString(response.getEntity()); } if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { break; } else { String msg = ""; if (times == 1) { msg = "First try "; } else { msg = "Second try "; } msg += "fails to post contents back to MindTouch server for page '" + path + "' : " + entityContent; logger.error(msg); } } catch (Exception e) { logger.error("Fail to post contents back to MindTouch server for " + times + "times for page '" + path + "'.", e); } finally { consumeQuietly(response); } } } catch (Exception e) { logger.error("Fail to post contents back to MindTouch server for page '" + path + "'.", e); } } /** * Put translated tags to MindTouch server page. * * If tags are the same, MindTouch server will ignore them, tag IDs keep * unchanged. Otherwise, MindTouch will delete old ones and add new to * create new tags(with new tag IDs). * * @param tagsTrgFile * @param pageInfo * @param targetLocale */ public void putPageTags(File tagsTrgFile, MindTouchPageInfo pageInfo, String sourceLocale, String targetLocale) { if (!isTargetServerExist(targetLocale) && !mtc.getIsPostToSourceServer()) { return; } CloseableHttpClient httpClient = getHttpClient(); HttpResponse response = null; String path = null; String url = null; try { url = getPutServerUrl(targetLocale); path = getNewPath(pageInfo, sourceLocale, targetLocale); // To add tags to page, the page must exist. Should ensure the page // has been created before this. The loop waiting should not happen // actually. int count = 0; while (count < 5 && getPageInfo(url, path) == null) { count++; Thread.sleep(1000); } url += "/@api/deki/pages/=" + path + "/tags"; HttpPut httpput = getHttpPut(url, targetLocale); String content = FileUtil.readFile(tagsTrgFile, "UTF-8"); content = getTagTitlesXml(content); StringEntity reqEntity = new StringEntity(content, "UTF-8"); reqEntity.setContentType("application/xml; charset=UTF-8"); httpput.setEntity(reqEntity); response = httpClient.execute(httpput); String entityContent = null; if (response.getEntity() != null) { entityContent = EntityUtils.toString(response.getEntity()); } if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { logger.error( "Fail to put tags back to MindTouch server for page '" + path + "' : " + entityContent); } } catch (Exception e) { logger.error("Fail to put tags back to MindTouch server for page '" + path + "'.", e); } finally { consumeQuietly(response); } } public String putPageFile(String filePath, String targetLocale, String sourceLocale, MindTouchPageInfo pageInfo) { if (!isTargetServerExist(targetLocale) && !mtc.getIsPostToSourceServer()) { return null; } CloseableHttpClient httpClient = getHttpClient(); String entityContent = null; int count = 0; String path = null; File picFile = null; while (entityContent == null && count < 3) { count++; HttpResponse response = null; try { String fileName = filePath.substring(filePath.lastIndexOf(File.separator) + 1); String tempFileName = URLEncoder.encode(fileName); tempFileName = URLEncoder.encode(tempFileName); path = getNewPath(pageInfo, sourceLocale, targetLocale); String url = getPutServerUrl(targetLocale) + "/@api/deki/pages/=" + path + "/files/=" + tempFileName; if (count > 1) { logger.info("Retry to putPageFile for url: " + url); } HttpPut httpput = getHttpPut(url, targetLocale); picFile = new File(filePath); FileEntity reqEntity = new FileEntity(picFile); httpput.setEntity(reqEntity); response = httpClient.execute(httpput); if (response.getEntity() != null) { entityContent = EntityUtils.toString(response.getEntity()); } if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { logger.error("Fail to put file back to MindTouch server for file '" + filePath + "' : " + entityContent); } } catch (Exception e) { logger.error("Fail to put file back to MindTouch server for file '" + filePath + "'.", e); } finally { consumeQuietly(response); } } if (picFile != null && picFile.exists()) { picFile.delete(); } return entityContent; } public static byte[] File2byte(String filePath) { byte[] buffer = null; try { File file = new File(filePath); FileInputStream fis = new FileInputStream(file); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] b = new byte[1024]; int n; while ((n = fis.read(b)) != -1) { bos.write(b, 0, n); } fis.close(); bos.close(); buffer = bos.toByteArray(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return buffer; } /** * Put translated properties to MindTouch server page. * * @param tagsTrgFile * @param pageInfo * @param targetLocale */ public void putPageProperties(File propertiesTrgFile, MindTouchPageInfo pageInfo, String sourceLocale, String targetLocale) { if (!isTargetServerExist(targetLocale) && !mtc.getIsPostToSourceServer()) { return; } CloseableHttpClient httpClient = getHttpClient(); HttpResponse response = null; String path = null; String url = null; try { url = getPutServerUrl(targetLocale); path = getNewPath(pageInfo, sourceLocale, targetLocale); // To add properties to page, the page must exist. Should ensure the // page has been created before this. The loop waiting should not // happen actually. int count = 0; while (count < 5 && getPageInfo(url, path) == null) { count++; Thread.sleep(1000); } // Use Etag from target server if exists. HashMap<String, String> etagMap = getePropertiesEtagMap(getPageProperties(url, path)); if (etagMap.size() == 0) { etagMap = getePropertiesEtagMap(getPageProperties(mtc.getUrl(), path)); } url += "/@api/deki/pages/=" + path + "/properties"; HttpPut httpput = getHttpPut(url, targetLocale); String content = FileUtil.readFile(propertiesTrgFile, "UTF-8"); content = getPropertiesContentsXml(content, etagMap); StringEntity reqEntity = new StringEntity(content, "UTF-8"); reqEntity.setContentType("application/xml; charset=UTF-8"); httpput.setEntity(reqEntity); response = httpClient.execute(httpput); String entityContent = null; if (response.getEntity() != null) { entityContent = EntityUtils.toString(response.getEntity()); } if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { logger.error("Fail to put properties back to MindTouch server for page '" + path + "' : " + entityContent); } } catch (Exception e) { logger.error("Fail to put properties back to MindTouch server for page '" + path + "'.", e); } finally { consumeQuietly(response); } } public boolean isTargetServerExist(String targetLocale) { if (targetServersMap.get(targetLocale) != null) { return true; } return false; } private String getPutServerUrl(String targetLocale) { if (targetServersMap.get(targetLocale) != null) { return targetServersMap.get(targetLocale).getUrl(); } else if (mtc.getIsPostToSourceServer()) { return mtc.getUrl(); } return null; } private String getPutServerUsername(String targetLocale) { if (targetServersMap.get(targetLocale) != null) { return targetServersMap.get(targetLocale).getUsername(); } else if (mtc.getIsPostToSourceServer()) { return mtc.getUsername(); } return null; } private String getPutServerPassword(String targetLocale) { if (targetServersMap.get(targetLocale) != null) { return targetServersMap.get(targetLocale).getPassword(); } else if (mtc.getIsPostToSourceServer()) { return mtc.getPassword(); } return null; } private void setTargetServersMap() { HashMap<String, MindTouchConnectorTargetServer> tempMap = new HashMap<String, MindTouchConnectorTargetServer>(); List<MindTouchConnectorTargetServer> targetServers = MindTouchManager.getAllTargetServers(mtc.getId()); for (MindTouchConnectorTargetServer ts : targetServers) { tempMap.put(ts.getTargetLocale(), ts); } targetServersMap = tempMap; } /** * If the original page path is like "en-us/Developer_Resources", the target * path should be like "zh-cn/Developer_Resources"; If the original page * path has no source locale information, it will add target locale as * suffix. * * @param pageInfo * @param sourceLocale -- sample "en_US" * @param targetLocale -- sample "zh_CN" * @return String * * @throws UnsupportedEncodingException */ private String getNewPath(MindTouchPageInfo pageInfo, String sourceLocale, String targetLocale) throws UnsupportedEncodingException { String path = pageInfo.getPath(); path = java.net.URLDecoder.decode(path, "UTF-8"); // If no target server and post to source server, need re-organize path if (!isTargetServerExist(targetLocale) && mtc.getIsPostToSourceServer()) { sourceLocale = sourceLocale.replace("_", "-").toLowerCase(); String sourceLang = null; if (sourceLocale.indexOf("-") > 0) { sourceLang = sourceLocale.substring(0, sourceLocale.indexOf("-")); } targetLocale = targetLocale.replace("_", "-").toLowerCase(); // this must be root page if (StringUtil.isEmpty(path)) { path = pageInfo.getTitle() + "(" + targetLocale + ")"; } // any non-root pages else { path = path.replace("\\", "/");// to be safe int index = path.indexOf(sourceLocale); if (index > -1) { String part1 = path.substring(0, index); String part2 = path.substring(index + sourceLocale.length()); path = part1 + targetLocale + part2; } else { path = getNewPathByLangOnly(path, sourceLang, targetLocale); } } } path = URLEncoder.encode(path); path = URLEncoder.encode(path); return path; } private String getNewPathByLangOnly(String sourcePath, String sourceLang, String targetLocale) { if (sourceLang == null) return sourcePath + "(" + targetLocale + ")"; boolean isSrcFound = false; StringBuilder newPath = new StringBuilder(); String[] paths = sourcePath.split("/"); String path = null; for (int i = 0; i < paths.length; i++) { path = paths[i]; if (path.startsWith(sourceLang + "-") && !isSrcFound) { isSrcFound = true; newPath.append("/").append(targetLocale); } // replace first section in path into target locale, the first section must be like "xx-xx" style. else if (i == 0 && path.split("-").length == 2 && path.indexOf("-") > 0) { isSrcFound = true; newPath.append("/").append(targetLocale); } else { newPath.append("/").append(path); } } if (!isSrcFound) { newPath.append("(").append(targetLocale).append(")"); } String result = newPath.toString(); if (result.startsWith("/")) { result = result.substring(1); } return result; } /** * Return an XML like * "<tags><tag value=\"title1\"/><tag value=\"title2\"/></tags>". * * @param tagsXml * @return String * @throws DocumentException */ @SuppressWarnings("rawtypes") private String getTagTitlesXml(String tagsXml) throws DocumentException { StringBuffer titles = new StringBuffer(); titles.append("<tags>"); Document doc = getDocument(tagsXml); List titleNodes = doc.selectNodes("//title"); Iterator it = titleNodes.iterator(); String title = null; while (it.hasNext()) { Element titleNode = (Element) it.next(); title = titleNode.getTextTrim(); if (title != null && title.length() > 0) { title = StringUtil.replace(title, "&", "&"); title = StringUtil.replace(title, "<", "<"); title = StringUtil.replace(title, ">", ">"); titles.append("<tag value=\"").append(title).append("\"/>"); } } titles.append("</tags>"); return titles.toString(); } /** * Return an XML like * "<properties><property name="name1"><contents type="text/plain">yes</contents></property><property name="name2"/></properties>" * * @param propertiesXml * @return String * @throws DocumentException */ @SuppressWarnings("rawtypes") private String getPropertiesContentsXml(String propertiesXml, HashMap<String, String> etagMap) throws DocumentException { StringBuffer titles = new StringBuffer(); titles.append("<properties>"); Document doc = getDocument(propertiesXml); List propertyNodes = doc.selectNodes("//property"); Iterator it = propertyNodes.iterator(); String name = null; String content = null; while (it.hasNext()) { Element propertyNode = (Element) it.next(); name = propertyNode.attributeValue("name"); Element contentNode = (Element) propertyNode.selectSingleNode("contents"); content = contentNode.getTextTrim(); if (content != null && content.length() > 0) { content = StringUtil.replace(content, "&", "&"); content = StringUtil.replace(content, "<", "<"); content = StringUtil.replace(content, ">", ">"); titles.append("<property name=\"").append(name).append("\" etag=\"").append(etagMap.get(name)) .append("\"><contents type=\"text/plain; charset=UTF-8\">").append(content) .append("</contents></property>"); } } titles.append("</properties>"); return titles.toString(); } @SuppressWarnings("rawtypes") private HashMap<String, String> getePropertiesEtagMap(String propertiesXml) { HashMap<String, String> etagMap = new HashMap<String, String>(); try { if (propertiesXml != null) { Document doc = getDocument(propertiesXml); List propertyNodes = doc.selectNodes("//property"); Iterator it = propertyNodes.iterator(); String name = null; String etag = null; while (it.hasNext()) { Element propertyNode = (Element) it.next(); name = propertyNode.attributeValue("name"); etag = propertyNode.attributeValue("etag"); etagMap.put(name, etag); } } } catch (DocumentException e) { logger.warn(e); } return etagMap; } /** * As the "title" need to be translated, get the translated title from * target file. * * @param contentXml * @return title */ private String getTitleFromTranslatedContentXml(String contentXml) { try { contentXml = fixTitleValueInContentXml(contentXml); int index = contentXml.indexOf("<body>"); if (index == -1) { index = contentXml.indexOf("<body"); } String content = contentXml.substring(0, index); content = content.replace(" ", " "); content += "</content>"; Element root = getDocument(content).getRootElement(); String title = root.attributeValue("title"); if (title.trim().length() > 0) { // Encode the whole title is the right behavior, but as // MindTouch does not decode title, we have to only encode # = & // title = URLEncoder.encode(title); title = title.replace("#", "%23"); title = title.replace("=", "%3D"); title = title.replace("&", "%26"); return new String(title.trim().getBytes("UTF-8"), "UTF-8"); } } catch (Exception e) { logger.error("Fail to get title from translated contents xml: " + contentXml, e); return null; } return null; } /** * If there are "<", ">" in value of "title" attribute, it will fail to * create job because of bad XML, need fix them. * * @param contentXml * @return Fixed contentXml */ public static String fixTitleValueInContentXml(String contentXml) { StringBuilder xml = new StringBuilder(); try { int index = contentXml.indexOf(" title="); String a = contentXml.substring(0, index + " title=\"".length()); xml.append(a); String b = contentXml.substring(index + " title=\"".length()); index = b.indexOf("="); if (index > -1) { a = b.substring(0, index); b = b.substring(index); index = a.lastIndexOf(" "); String title = a.substring(0, index - 1); title = title.replace("\"", """).replace("<", "<").replace(">", ">"); xml.append(title); xml.append(a.substring(index - 1)); xml.append(b); } else { xml.append(b); } return new String(xml.toString().trim().getBytes("UTF-8"), "UTF-8"); } catch (Exception e) { logger.error("Fail to fix title in contents xml: " + contentXml, e); return contentXml; } } /** * An object file content is like: * {"title":"Get Involved","PageId":"1845","MindTouchConnectorId":"7","path":"en-us/Developer_Resources/Community/Get_Involved"} * * @return MindTouchPageInfo * */ public static MindTouchPageInfo parseObjFile(File objFile) { MindTouchPageInfo info = new MindTouchPageInfo(); if (objFile.exists() && objFile.isFile()) { try { String json = FileUtil.readFile(objFile, "UTF-8"); JSONObject jsonObj = new JSONObject(json); String mindTouchConnectorId = String.valueOf(jsonObj.get("mindTouchConnectorId")); info.setMindTouchConnectorId(mindTouchConnectorId); String pageId = String.valueOf(jsonObj.get("pageId")); info.setPageId(pageId); String path = (String) jsonObj.get("path"); info.setPath(path); String title = (String) jsonObj.get("title"); info.setTitle(title); } catch (Exception e) { logger.warn(e.getMessage()); } } return info; } public static Document getDocument(String xml) throws DocumentException { SAXReader reader = new SAXReader(); return reader.read(new InputSource(new StringReader(xml))); } private HttpPost getHttpPost(URI uri, String targetLocale) { HttpPost httppost = new HttpPost(uri); httppost.setHeader(HttpHeaders.AUTHORIZATION, authorizationHeader(getPutServerUsername(targetLocale), getPutServerPassword(targetLocale))); return httppost; } private HttpGet getHttpGet(String url) { HttpGet httpget = new HttpGet(url); httpget.setHeader(HttpHeaders.AUTHORIZATION, authorizationHeader(mtc.getUsername(), mtc.getPassword())); return httpget; } private HttpPut getHttpPut(String url, String targetLocale) { HttpPut httpput = new HttpPut(url); httpput.setHeader(HttpHeaders.AUTHORIZATION, authorizationHeader(getPutServerUsername(targetLocale), getPutServerPassword(targetLocale))); return httpput; } private HttpDelete getHttpDelete(String url) { HttpDelete httpdelete = new HttpDelete(url); httpdelete.setHeader(HttpHeaders.AUTHORIZATION, authorizationHeader(mtc.getUsername(), mtc.getPassword())); return httpdelete; } CookieSpecProvider easySpecProvider = new CookieSpecProvider() { public CookieSpec create(HttpContext context) { return new BrowserCompatSpec() { @Override public void validate(Cookie cookie, CookieOrigin origin) throws MalformedCookieException { // Oh, I am easy } }; } }; Registry<CookieSpecProvider> reg = RegistryBuilder.<CookieSpecProvider>create() .register(CookieSpecs.BEST_MATCH, new BestMatchSpecFactory()) .register(CookieSpecs.BROWSER_COMPATIBILITY, new BrowserCompatSpecFactory()) .register("mySpec", easySpecProvider).build(); RequestConfig requestConfig = RequestConfig.custom().setCookieSpec("mySpec").setConnectTimeout(5000) .setSocketTimeout(20000).build(); private CloseableHttpClient getHttpClient() { if (httpClient == null) { httpClient = HttpClients.custom().setDefaultCookieSpecRegistry(reg) .setDefaultRequestConfig(requestConfig).build(); } return httpClient; } private void consumeQuietly(HttpResponse httpResponse) { if (httpResponse != null) { try { EntityUtils.consumeQuietly(httpResponse.getEntity()); } catch (Exception ignore) { } } } public void shutdownHttpClient() { if (httpClient == null) return; try { httpClient.close(); } catch (IOException e) { logger.error("Fail to close httpclient", e); } } private final String authorizationHeader(String username, String password) { String auth = username + ":" + password; byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(Charset.forName("US-ASCII"))); String authHeader = "Basic " + new String(encodedAuth); return authHeader; } @SuppressWarnings({ "unchecked" }) public static Vector<GlobalSightLocale> getAllTargetLocales() { Vector<GlobalSightLocale> targetLocales = new Vector<GlobalSightLocale>(); try { targetLocales = ServerProxy.getLocaleManager().getAllTargetLocales(); SortUtil.sort(targetLocales, new GlobalSightLocaleComparator(GlobalSightLocaleComparator.DISPLAYNAME, Locale.US)); } catch (Exception e) { throw new EnvoyServletException(EnvoyServletException.EX_GENERAL, e); } return targetLocales; } }