/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library 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 library 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. */ package com.liferay.portal.tools.bundle.support.commands; import com.liferay.portal.tools.bundle.support.internal.util.BundleSupportUtil; import com.liferay.portal.tools.bundle.support.internal.util.FileUtil; import com.sun.net.httpserver.BasicAuthenticator; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpContext; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.URI; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.util.Date; import java.util.Locale; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.http.HttpHeaders; import org.apache.http.HttpStatus; import org.apache.http.client.utils.DateUtils; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.littleshoot.proxy.HttpFilters; import org.littleshoot.proxy.HttpFiltersAdapter; import org.littleshoot.proxy.HttpFiltersSourceAdapter; import org.littleshoot.proxy.HttpProxyServer; import org.littleshoot.proxy.HttpProxyServerBootstrap; import org.littleshoot.proxy.ProxyAuthenticator; import org.littleshoot.proxy.impl.DefaultHttpProxyServer; /** * @author David Truong * @author Andrea Di Giorgi */ public class BundleSupportCommandsTest { @BeforeClass public static void setUpClass() throws Exception { _authenticatedHttpProxyServer = _startHttpProxyServer( _AUTHENTICATED_HTTP_PROXY_SERVER_PORT, true, _authenticatedHttpProxyHit); URL url = BundleSupportCommandsTest.class.getResource( "dependencies" + _CONTEXT_PATH_ZIP); _bundleZipFile = new File(url.toURI()); _httpProxyServer = _startHttpProxyServer( _HTTP_PROXY_SERVER_PORT, false, _httpProxyHit); _httpServer = _startHttpServer(); } @AfterClass public static void tearDownClass() throws Exception { if (_authenticatedHttpProxyServer != null) { _authenticatedHttpProxyServer.stop(); } if (_httpProxyServer != null) { _httpProxyServer.stop(); } if (_httpServer != null) { _httpServer.stop(0); } } @Before public void setUp() throws Exception { _authenticatedHttpProxyHit.set(false); _httpProxyHit.set(false); } @Test public void testClean() throws Exception { File liferayHomeDir = temporaryFolder.newFolder("bundles"); File osgiModulesDir = _createDirectory(liferayHomeDir, "osgi/modules"); File jarFile = _createFile(osgiModulesDir, "test.jar"); File osgiWarDir = _createDirectory(liferayHomeDir, "osgi/war"); File warFile = _createFile(osgiWarDir, "test.war"); clean(warFile.getName(), liferayHomeDir); Assert.assertFalse(warFile.exists()); Assert.assertTrue(jarFile.exists()); } @Test public void testDeployJar() throws Exception { File liferayHomeDir = temporaryFolder.newFolder("bundles"); File file = temporaryFolder.newFile("test-1.0.0.jar"); File deployedFile = new File(liferayHomeDir, "osgi/modules/test.jar"); deploy(file, liferayHomeDir, deployedFile.getName()); Assert.assertTrue(deployedFile.exists()); } @Test public void testDeployWar() throws Exception { File liferayHomeDir = temporaryFolder.newFolder("bundles"); File file = temporaryFolder.newFile("test-1.0.0.war"); File deployedFile = new File(liferayHomeDir, "osgi/war/test.war"); deploy(file, liferayHomeDir, deployedFile.getName()); Assert.assertTrue(deployedFile.exists()); } @Test public void testDistBundleTar() throws Exception { _testDistBundle("tar.gz"); } @Test public void testDistBundleZip() throws Exception { _testDistBundle("zip"); } @Test public void testInitBundleTar() throws Exception { _testInitBundleTar(null, null, null, null, null, null, null); } @Test public void testInitBundleTarDifferentLocale() throws Exception { Locale locale = Locale.getDefault(); try { Locale.setDefault(Locale.ITALY); _testInitBundleTar(null, null, null, null, null, null, null); } finally { Locale.setDefault(locale); } } @Test public void testInitBundleTarProxy() throws Exception { _testInitBundleTar( "localhost", _HTTP_PROXY_SERVER_PORT, null, null, null, _httpProxyHit, Boolean.TRUE); } @Test public void testInitBundleTarProxyAuthenticated() throws Exception { _testInitBundleTar( "localhost", _AUTHENTICATED_HTTP_PROXY_SERVER_PORT, _HTTP_PROXY_SERVER_USER_NAME, _HTTP_PROXY_SERVER_PASSWORD, null, _authenticatedHttpProxyHit, Boolean.TRUE); } @Test public void testInitBundleTarProxyNonProxyHosts() throws Exception { _testInitBundleTar( "localhost", _HTTP_PROXY_SERVER_PORT, null, null, "localhost2.localdomain", _httpProxyHit, Boolean.TRUE); } @Test public void testInitBundleTarProxySkip() throws Exception { _testInitBundleTar( "localhost", _HTTP_PROXY_SERVER_PORT, null, null, "localhost.localdomain", _httpProxyHit, Boolean.FALSE); } @Test public void testInitBundleTarProxyUnauthorized() throws Exception { expectedException.expectMessage("Proxy Authentication Required"); _testInitBundleTar( "localhost", _AUTHENTICATED_HTTP_PROXY_SERVER_PORT, null, null, null, _authenticatedHttpProxyHit, Boolean.TRUE); } @Ignore @Test public void testInitBundleZip() throws Exception { _testInitBundleZip(null, _HTTP_SERVER_PASSWORD, _HTTP_SERVER_USER_NAME); } @Test public void testInitBundleZipFile() throws Exception { _testInitBundleZip(_bundleZipFile, null, null); } @Ignore @Test public void testInitBundleZipUnauthorized() throws Exception { expectedException.expectMessage("Unauthorized"); _testInitBundleZip(null, null, null); } @Rule public final ExpectedException expectedException = ExpectedException.none(); @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); protected void clean(String fileName, File liferayHomeDir) throws Exception { CleanCommand cleanCommand = new CleanCommand(); cleanCommand.setFileName(fileName); cleanCommand.setLiferayHomeDir(liferayHomeDir); cleanCommand.execute(); } protected void deploy(File file, File liferayHomeDir, String outputFileName) throws Exception { DeployCommand deployCommand = new DeployCommand(); deployCommand.setFile(file); deployCommand.setLiferayHomeDir(liferayHomeDir); deployCommand.setOutputFileName(outputFileName); deployCommand.execute(); } protected void distBundle( String format, File liferayHomeDir, File outputFile) throws Exception { DistBundleCommand distBundleCommand = new DistBundleCommand(); distBundleCommand.setFormat(format); distBundleCommand.setIncludeFolder(true); distBundleCommand.setLiferayHomeDir(liferayHomeDir); distBundleCommand.setOutputFile(outputFile); distBundleCommand.execute(); } protected void initBundle( File cacheDir, File configsDir, String environment, File liferayHomeDir, String password, int stripComponents, URL url, String userName) throws Exception { InitBundleCommand initBundleCommand = new InitBundleCommand(); initBundleCommand.setCacheDir(cacheDir); initBundleCommand.setConfigsDir(configsDir); initBundleCommand.setEnvironment(environment); initBundleCommand.setLiferayHomeDir(liferayHomeDir); initBundleCommand.setPassword(password); initBundleCommand.setStripComponents(stripComponents); initBundleCommand.setUrl(url); initBundleCommand.setUserName(userName); initBundleCommand.execute(); } private static File _assertExists(File dir, String fileName) { File file = new File(dir, fileName); Assert.assertTrue(file.exists()); return file; } private static void _assertNotExists(File dir, String fileName) { File file = new File(dir, fileName); Assert.assertFalse(file.exists()); } private static void _assertPosixFilePermissions( File dir, String fileName, Set<PosixFilePermission> expectedPosixFilePermissions) throws IOException { File file = _assertExists(dir, fileName); Path path = file.toPath(); if (!FileUtil.isPosixSupported(path)) { return; } Set<PosixFilePermission> actualPosixFilePermissions = Files.getPosixFilePermissions(path); Assert.assertEquals( expectedPosixFilePermissions, actualPosixFilePermissions); } private static File _createDirectory(File parentDir, String dirName) { File dir = new File(parentDir, dirName); Assert.assertTrue(dir.mkdirs()); return dir; } private static File _createFile(File dir, String fileName) throws IOException { File file = new File(dir, fileName); Assert.assertTrue(file.createNewFile()); return file; } private static HttpContext _createHttpContext( HttpServer httpServer, final String contextPath, final String contentType) { HttpHandler httpHandler = new HttpHandler() { @Override public void handle(HttpExchange httpExchange) throws IOException { Headers headers = httpExchange.getResponseHeaders(); headers.add(HttpHeaders.CONTENT_TYPE, contentType); URL url = BundleSupportCommandsTest.class.getResource( "dependencies" + contextPath); File file = new File(url.getFile()); Date lastModifiedDate = new Date(file.lastModified()); headers.add( HttpHeaders.LAST_MODIFIED, DateUtils.formatDate(lastModifiedDate)); try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file))) { int length = (int)file.length(); byte[] byteArray = new byte[length]; bufferedInputStream.read(byteArray, 0, length); httpExchange.sendResponseHeaders(HttpStatus.SC_OK, length); OutputStream outputStream = httpExchange.getResponseBody(); outputStream.write(byteArray, 0, length); outputStream.close(); } } }; return httpServer.createContext(contextPath, httpHandler); } private static int _getTestPort(int... excludedPorts) throws IOException { for (int i = 0; i < _TEST_PORT_RETRIES; i++) { try (ServerSocket serverSocket = new ServerSocket(0)) { int port = serverSocket.getLocalPort(); boolean found = false; for (int excludedPort : excludedPorts) { if (excludedPort == port) { found = true; break; } } if (!found) { return port; } } } throw new IOException("Unable to find a test port"); } private static HttpProxyServer _startHttpProxyServer( int port, boolean authenticate, final AtomicBoolean hit) { HttpProxyServerBootstrap httpProxyServerBootstrap = DefaultHttpProxyServer.bootstrap(); httpProxyServerBootstrap.withFiltersSource( new HttpFiltersSourceAdapter() { @Override public HttpFilters filterRequest( final HttpRequest httpRequest) { return new HttpFiltersAdapter(httpRequest) { @Override public HttpResponse clientToProxyRequest( HttpObject httpObject) { hit.set(true); return super.clientToProxyRequest(httpObject); } }; } }); httpProxyServerBootstrap.withPort(port); if (authenticate) { httpProxyServerBootstrap.withProxyAuthenticator( new ProxyAuthenticator() { @Override public boolean authenticate( String userName, String password) { if (_HTTP_PROXY_SERVER_USER_NAME.equals(userName) && _HTTP_PROXY_SERVER_PASSWORD.equals(password)) { return true; } return false; } @Override public String getRealm() { return _HTTP_PROXY_SERVER_REALM; } }); } return httpProxyServerBootstrap.start(); } private static HttpServer _startHttpServer() throws IOException { HttpServer httpServer = HttpServer.create( new InetSocketAddress(_HTTP_SERVER_PORT), 0); HttpContext httpContext = _createHttpContext( httpServer, _CONTEXT_PATH_ZIP, "application/zip"); httpContext.setAuthenticator( new BasicAuthenticator(_HTTP_SERVER_REALM) { @Override public boolean checkCredentials(String user, String pwd) { if (user.equals(_HTTP_SERVER_USER_NAME) && pwd.equals(_HTTP_SERVER_PASSWORD)) { return true; } return false; } }); _createHttpContext( httpServer, _CONTEXT_PATH_TAR, "application/tar+gzip"); httpServer.setExecutor(null); httpServer.start(); return httpServer; } private void _initBundle( File configsDir, File file, File liferayHomeDir, String password, String userName) throws Exception { File cacheDir = temporaryFolder.newFolder(); URI uri = file.toURI(); initBundle( cacheDir, configsDir, _INIT_BUNDLE_ENVIRONMENT, liferayHomeDir, password, _INIT_BUNDLE_STRIP_COMPONENTS, uri.toURL(), userName); } private void _initBundle( File configsDir, String contextPath, File liferayHomeDir, String password, String userName) throws Exception { File cacheDir = temporaryFolder.newFolder(); URL url = new URL( "http", "localhost.localdomain", _HTTP_SERVER_PORT, contextPath); initBundle( cacheDir, configsDir, _INIT_BUNDLE_ENVIRONMENT, liferayHomeDir, password, _INIT_BUNDLE_STRIP_COMPONENTS, url, userName); } private void _testDistBundle(String format) throws Exception { File liferayHomeDir = temporaryFolder.newFolder("bundles"); File osgiWarDir = _createDirectory(liferayHomeDir, "osgi/war"); _createFile(osgiWarDir, "test.war"); File outputFile = new File(temporaryFolder.getRoot(), "out." + format); Assert.assertFalse(outputFile.exists()); distBundle(format, liferayHomeDir, outputFile); Assert.assertTrue(outputFile.exists()); } private void _testInitBundleTar( String proxyHost, Integer proxyPort, String proxyUser, String proxyPassword, String nonProxyHosts, AtomicBoolean proxyHit, Boolean expectedProxyHit) throws Exception { if (proxyHit != null) { Assert.assertFalse(proxyHit.get()); } proxyHost = BundleSupportUtil.setSystemProperty( "http.proxyHost", proxyHost); proxyPort = BundleSupportUtil.setSystemProperty( "http.proxyPort", proxyPort); proxyUser = BundleSupportUtil.setSystemProperty( "http.proxyUser", proxyUser); proxyPassword = BundleSupportUtil.setSystemProperty( "http.proxyPassword", proxyPassword); nonProxyHosts = BundleSupportUtil.setSystemProperty( "http.nonProxyHosts", nonProxyHosts); try { File liferayHomeDir = temporaryFolder.newFolder("bundles"); _initBundle(null, _CONTEXT_PATH_TAR, liferayHomeDir, null, null); if (proxyHit != null) { Assert.assertEquals( expectedProxyHit.booleanValue(), proxyHit.get()); } _assertExists(liferayHomeDir, "README.markdown"); _assertPosixFilePermissions( liferayHomeDir, "bin/hello.sh", _expectedPosixFilePermissions); } finally { BundleSupportUtil.setSystemProperty("http.proxyHost", proxyHost); BundleSupportUtil.setSystemProperty("http.proxyPort", proxyPort); BundleSupportUtil.setSystemProperty("http.proxyUser", proxyUser); BundleSupportUtil.setSystemProperty( "http.proxyPassword", proxyPassword); BundleSupportUtil.setSystemProperty( "http.nonProxyHosts", nonProxyHosts); } } private void _testInitBundleZip(File file, String password, String userName) throws Exception { File liferayHomeDir = temporaryFolder.newFolder("bundles"); File configsDir = temporaryFolder.newFolder("configs"); File configsLocalDir = _createDirectory( configsDir, _INIT_BUNDLE_ENVIRONMENT); File localPropertiesFile = _createFile( configsLocalDir, "portal-ext.properties"); File configsProdDir = _createDirectory(configsDir, "prod"); File prodPropertiesFile = _createFile( configsProdDir, "portal-prod.properties"); if (file != null) { _initBundle(configsDir, file, liferayHomeDir, password, userName); } else { _initBundle( configsDir, _CONTEXT_PATH_ZIP, liferayHomeDir, password, userName); } _assertExists(liferayHomeDir, "README.markdown"); _assertExists(liferayHomeDir, localPropertiesFile.getName()); _assertNotExists(liferayHomeDir, prodPropertiesFile.getName()); _assertPosixFilePermissions( liferayHomeDir, "bin/hello.sh", _expectedPosixFilePermissions); } private static final int _AUTHENTICATED_HTTP_PROXY_SERVER_PORT; private static final String _CONTEXT_PATH_TAR = "/test.tar.gz"; private static final String _CONTEXT_PATH_ZIP = "/test.zip"; private static final String _HTTP_PROXY_SERVER_PASSWORD = "proxyTest"; private static final int _HTTP_PROXY_SERVER_PORT; private static final String _HTTP_PROXY_SERVER_REALM = "proxyTest"; private static final String _HTTP_PROXY_SERVER_USER_NAME = "proxyTest"; private static final String _HTTP_SERVER_PASSWORD = "test"; private static final int _HTTP_SERVER_PORT; private static final String _HTTP_SERVER_REALM = "test"; private static final String _HTTP_SERVER_USER_NAME = "test"; private static final String _INIT_BUNDLE_ENVIRONMENT = "local"; private static final int _INIT_BUNDLE_STRIP_COMPONENTS = 0; private static final int _TEST_PORT_RETRIES = 20; private static final AtomicBoolean _authenticatedHttpProxyHit = new AtomicBoolean(); private static HttpProxyServer _authenticatedHttpProxyServer; private static File _bundleZipFile; private static final Set<PosixFilePermission> _expectedPosixFilePermissions = PosixFilePermissions.fromString( "rwxr-x---"); private static final AtomicBoolean _httpProxyHit = new AtomicBoolean(); private static HttpProxyServer _httpProxyServer; private static HttpServer _httpServer; static { try { _AUTHENTICATED_HTTP_PROXY_SERVER_PORT = _getTestPort(); _HTTP_PROXY_SERVER_PORT = _getTestPort( _AUTHENTICATED_HTTP_PROXY_SERVER_PORT); _HTTP_SERVER_PORT = _getTestPort( _AUTHENTICATED_HTTP_PROXY_SERVER_PORT, _HTTP_PROXY_SERVER_PORT); } catch (IOException ioe) { throw new ExceptionInInitializerError(ioe); } } }