/* * Copyright (C) 2003-2017 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.management.service.api; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.filefilter.RegexFileFilter; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; import org.exoplatform.container.PortalContainer; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.gatein.management.api.ContentType; import org.gatein.management.api.PathAddress; import org.gatein.management.api.controller.ManagedRequest; import org.gatein.management.api.controller.ManagedResponse; import org.gatein.management.api.controller.ManagementController; import org.gatein.management.api.operation.OperationNames; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; /** * The Class AbstractResourceHandler. */ public abstract class AbstractResourceHandler implements ResourceHandler { /** The Constant MANAGED_COMPONENT_REST_URI. */ protected static final String MANAGED_COMPONENT_REST_URI = "/rest/private/managed-components"; /** The log. */ private Log log = ExoLogger.getLogger(this.getClass()); /** * Gets the logger. * * @return the logger */ protected Log getLogger() { return log; } /** GateIN Management Controller. */ private ManagementController managementController = null; /** * Gets the gateIN Management Controller. * * @return the gateIN Management Controller */ protected ManagementController getManagementController() { if (managementController == null) { managementController = (ManagementController) PortalContainer.getInstance().getComponentInstanceOfType(ManagementController.class); } return managementController; } /** * {@inheritDoc} */ public void synchronize(List<Resource> resources, Map<String, String> exportOptions, Map<String, String> importOptions, TargetServer targetServer) throws Exception { for (Resource resource : resources) { synchronize(resource, exportOptions, importOptions, targetServer); } } /** * {@inheritDoc} */ public void export(List<Resource> resources, ZipOutputStream exportFileOS, Map<String, String> exportOptions) throws Exception { for (Resource resource : resources) { export(resource, exportFileOS, exportOptions); } } /** * {@inheritDoc} */ public void synchronizeResourcesInFilter(List<Resource> resources, Map<String, String> exportOptions, Map<String, String> importOptions, TargetServer targetServer) throws Exception { for (Resource resource : resources) { if (getPath().equals(resource.getPath())) { synchronize(new Resource(getPath(), getPath(), getPath()), exportOptions, importOptions, targetServer); } else { String resourcePath = resource.getPath().replace(getPath() + "/", ""); Map<String, String> exportOptionsTmp = new HashMap<String, String>(exportOptions); exportOptionsTmp.put("filter/" + resourcePath, null); synchronize(new Resource(getPath(), getPath(), getPath()), exportOptionsTmp, importOptions, targetServer); } } } /** * {@inheritDoc} */ public void exportResourcesInFilter(List<Resource> resources, ZipOutputStream exportFileOS, Map<String, String> exportOptions) throws Exception { Map<String, String> exportOptionsTmp = new HashMap<String, String>(exportOptions); for (Resource resource : resources) { if (getPath().equals(resource.getPath())) { export(new Resource(getPath(), getPath(), getPath()), exportFileOS, exportOptions); } else { String resourcePath = resource.getPath().replace(getPath() + "/", ""); exportOptionsTmp.put("filter/" + resourcePath, null); } } if (!exportOptionsTmp.isEmpty()) { export(new Resource(getPath(), getPath(), getPath()), exportFileOS, exportOptionsTmp); } } /** * Sends data (exported zip) to the target server. * * @param file the file * @param options the options * @param targetServer the target server * @return true, if successful * @throws Exception the exception */ protected boolean sendData(File file, Map<String, String> options, TargetServer targetServer) throws Exception { FileInputStream fileInputStream = null; try { getLogger().info("Sending data to server: " + targetServer.getHost()); String targetServerURL = getServerURL(targetServer, getPath(), options); URL url = new URL(targetServerURL); fileInputStream = new FileInputStream(file); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("PUT"); String passString = targetServer.getUsername() + ":" + targetServer.getPassword(); String basicAuth = "Basic " + new String(Base64.encodeBase64(passString.getBytes())); conn.setRequestProperty("Authorization", basicAuth); conn.setFixedLengthStreamingMode(fileInputStream.available()); conn.setRequestProperty("Content-Type", "application/zip"); conn.setDoOutput(true); IOUtils.copy(fileInputStream, conn.getOutputStream()); getLogger().info("Content sent to target server: " + targetServer.getHost()); getLogger().info("Importing content in target server, please wait ..."); if (conn.getResponseCode() != 200) { if (399 < conn.getResponseCode() && conn.getResponseCode() < 499) { throw new ConnectException("Synchronization operation error, HTTP error code from target server : " + conn.getResponseCode()); } else { throw new IllegalStateException("Synchronization operation error, HTTP error code from target server : " + conn.getResponseCode()); } } getLogger().info("Import in target server finished successfully."); } finally { if (fileInputStream != null) { fileInputStream.close(); } clearTempFiles(); } return true; } /** * Call GateIN Management Controller to export selected resource using options * passed in filters. * * @param path managed path * @param selectedOptions passed to GateIN Management SPI * @return archive file exported from GateIN Management Controller call */ public ManagedResponse getExportedResourceFromOperation(String path, Map<String, String> selectedOptions) { ManagedRequest request = null; if (!selectedOptions.isEmpty()) { Map<String, List<String>> attributes = extractAttributes(selectedOptions); request = ManagedRequest.Factory.create(OperationNames.EXPORT_RESOURCE, PathAddress.pathAddress(path), attributes, ContentType.ZIP); } else { request = ManagedRequest.Factory.create(OperationNames.EXPORT_RESOURCE, PathAddress.pathAddress(path), ContentType.ZIP); } return getManagementController().execute(request); } /** * Buil server URL. * * @param targetServer the target server * @param uri the uri * @param options the options * @return the server URL */ public static String getServerURL(TargetServer targetServer, String uri, Map<String, String> options) { String targetServerURL = "http"; if (targetServer.isSsl()) { targetServerURL += "s"; } targetServerURL += "://" + targetServer.getHost() + ":" + targetServer.getPort() + MANAGED_COMPONENT_REST_URI; if (!uri.startsWith("/")) { targetServerURL += "/"; } targetServerURL += uri; if (options != null) { String optionsString = encodeURLParameters(options); if (!options.isEmpty()) { targetServerURL += "?" + optionsString; } } return targetServerURL; } /** * Delete temp files created by GateIN management operations. */ protected void clearTempFiles() { deleteTempFilesStartingWith("gatein-export(.*)\\.zip"); deleteTempFilesStartingWith("data(.*)\\.xml"); } /** * Export. * * @param resource the resource * @param exportFileOS the export file OS * @param exportOptions the export options * @throws Exception the exception */ private void export(Resource resource, ZipOutputStream exportFileOS, Map<String, String> exportOptions) throws Exception { FileOutputStream outputStream = null; FileInputStream inputStream = null; File tmpFile = null; try { ManagedResponse managedResponse = getExportedResourceFromOperation(resource.getPath(), exportOptions); // if (managedResponse.getResult() instanceof NoResultModel) { tmpFile = File.createTempFile("staging", "-export.zip"); tmpFile.deleteOnExit(); outputStream = new FileOutputStream(tmpFile); managedResponse.writeResult(outputStream, false); outputStream.flush(); outputStream.close(); outputStream = null; getLogger().info("Export operation finished."); inputStream = new FileInputStream(tmpFile); Utils.copyZipEnries(new ZipInputStream(inputStream), exportFileOS, null); } finally { if (outputStream != null) { outputStream.close(); } if (inputStream != null) { inputStream.close(); } if (tmpFile != null) { tmpFile.delete(); } clearTempFiles(); } } /** * Synchronize. * * @param resource the resource * @param exportOptions the export options * @param importOptions the import options * @param targetServer the target server * @throws Exception the exception */ private void synchronize(Resource resource, Map<String, String> exportOptions, Map<String, String> importOptions, TargetServer targetServer) throws Exception { FileOutputStream fileOutputStream = null; File tmpFile = null; try { ManagedResponse managedResponse = getExportedResourceFromOperation(resource.getPath(), exportOptions); tmpFile = File.createTempFile(resource.getPath().replace("/", ""), ".zip"); tmpFile.deleteOnExit(); fileOutputStream = new FileOutputStream(tmpFile); managedResponse.writeResult(fileOutputStream, false); getLogger().info("Export operation finished."); sendData(tmpFile, importOptions, targetServer); } catch (Exception ex) { throw ex; } finally { if (tmpFile != null) { tmpFile.delete(); } if (fileOutputStream != null) { fileOutputStream.close(); } } } /** * Encode URL parameters. * * @param options the options * @return the string */ private static String encodeURLParameters(Map<String, String> options) { Map<String, List<String>> attributes = extractAttributes(options); List<NameValuePair> parameters = new ArrayList<NameValuePair>(); Iterator<Entry<String, List<String>>> entryIterator = attributes.entrySet().iterator(); while (entryIterator.hasNext()) { Entry<String, List<String>> entry = entryIterator.next(); String parameterName = entry.getKey(); List<String> parameterValues = entry.getValue(); for (String parameterValue : parameterValues) { parameters.add(new BasicNameValuePair(parameterName, parameterValue)); } } return URLEncodedUtils.format(parameters, (String) null); } /** * Convert map of option to a map of attributes for the export operation. * Example : * filter/with-membership -> true * filter/replace-existing -> * true * importMode -> merge is converted to * filter -> * {with-membership:true, replace-existing} * importMode -> {merge} * * @param selectedOptions the selected options * @return the map */ private static Map<String, List<String>> extractAttributes(Map<String, String> selectedOptions) { Map<String, List<String>> attributes = new HashMap<String, List<String>>(); Set<Entry<String, String>> optionsEntrySet = selectedOptions.entrySet(); for (Entry<String, String> option : optionsEntrySet) { String optionName; String optionValue; String[] optionParts = option.getKey().split("/", 2); if (optionParts.length == 1) { optionName = option.getKey(); optionValue = option.getValue(); } else if (optionParts.length == 2) { optionName = optionParts[0]; optionValue = optionParts[1]; if (option.getValue() != null) { optionValue += ":" + option.getValue(); } } else { throw new RuntimeException("Option '" + option.getKey() + "' is not valid."); } List<String> attribute = attributes.get(optionName); if (attribute == null) { attribute = new ArrayList<String>(); attributes.put(optionName, attribute); } attribute.add(optionValue); } return attributes; } /** * Delete temp files starting with. * * @param regex the regex */ protected void deleteTempFilesStartingWith(String regex) { String tempDirPath = System.getProperty("java.io.tmpdir"); File file = new File(tempDirPath); if (log.isDebugEnabled()) { log.debug("Delete files with regex '" + regex + "*' under " + tempDirPath); } File[] listFiles = file.listFiles((FileFilter) new RegexFileFilter(regex)); for (File tempFile : listFiles) { deleteFile(tempFile); } } /** * Delete file. * * @param tempFile the temp file */ protected void deleteFile(File tempFile) { if (tempFile != null && tempFile.exists() && !tempFile.isDirectory()) { try { FileUtils.forceDelete(tempFile); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug("Unable to delete temp file: " + tempFile.getAbsolutePath() + ". Not blocker."); } tempFile.deleteOnExit(); } } } }