/******************************************************************************* * Copyright (c) 2013 GigaSpaces Technologies Ltd. All rights reserved * * 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 org.cloudifysource.dsl.download; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.net.URL; import java.text.MessageFormat; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.commons.io.FileUtils; import org.cloudifysource.dsl.internal.tools.download.ChecksumVerifierException; import org.cloudifysource.dsl.internal.tools.download.ResourceDownloadException; import org.cloudifysource.dsl.internal.tools.download.ResourceDownloadFacade; import org.cloudifysource.dsl.internal.tools.download.ResourceDownloadFacadeImpl; import org.cloudifysource.dsl.internal.tools.download.ResourceDownloader; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.util.resource.FileResource; import org.eclipse.jetty.util.resource.Resource; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; /** * This test starts an embedded Jetty server on the localhost and tests download requests made by the download facade * utility. * * @author adaml * @since 2.6.0 */ public class ResourceDownloaderTest { // default resource params. private static final String RESOURCE_NAME = "testResource.txt"; private static final String DESTINATION_FOLDER = "src/test/resources/resourceDownloader/downloadFolder/"; private static final String RESOURCE_DESTINATION = DESTINATION_FOLDER + RESOURCE_NAME; private static final String RESOURCE_URL = "http://localhost:8080/" + RESOURCE_NAME; private static final String RESOURCE_FOLDER = "src/test/resources/resourceDownloader/"; // used for testing checksum failure. private static final String DUMMY_RESOURCE_NAME = "dummyTestResource.txt"; private static final String DUMMY_RESOURCE_URL = "http://localhost:8080/" + DUMMY_RESOURCE_NAME; private static final ResourceDownloadFacade rdf = new ResourceDownloadFacadeImpl(new ResourceDownloader()); private static final Server server = new Server(8080); @BeforeClass public static void beforeClass() throws Exception { // Start embedded jetty server. final URL resourceUrl = new File(RESOURCE_FOLDER). getAbsoluteFile().toURI().toURL(); final Resource resource = new FileResource(resourceUrl); final ResourceHandler handler = new ResourceHandler(); handler.setBaseResource(resource); server.setHandler(handler); server.start(); // Create the destination folder if does not exist final File destinationFolder = new File(DESTINATION_FOLDER); if (!destinationFolder.exists()) { destinationFolder.mkdirs(); } } @AfterClass public static void afterClass() throws Exception { server.stop(); final File destinationFolder = new File(DESTINATION_FOLDER); if (destinationFolder.exists()) { FileUtils.deleteQuietly(destinationFolder); } } @Test public void testTimeout() { // Try doGet with very short timeout. try { rdf.get(RESOURCE_URL, RESOURCE_DESTINATION, false, 1, TimeUnit.MICROSECONDS); Assert.fail("Expected timeout exception."); } catch (TimeoutException e) { // test passed. } catch (ResourceDownloadException e) { Assert.fail("Expecting timeout exception. got " + e.getMessage()); } } @Test public void testBadChecksumVerification() throws Exception { // run doGet on a dummy file and try to verify it against // non-matching checksum files. assertChecksumFailure(RESOURCE_URL + ".md5"); cleanDownloadFolder(); assertChecksumFailure(RESOURCE_URL + ".sha1"); cleanDownloadFolder(); assertChecksumFailure(RESOURCE_URL + ".sha256"); cleanDownloadFolder(); assertChecksumFailure(RESOURCE_URL + ".sha384"); cleanDownloadFolder(); assertChecksumFailure(RESOURCE_URL + ".sha512"); cleanDownloadFolder(); } void assertChecksumFailure(final String hashUrl) { try { rdf.get(DUMMY_RESOURCE_URL, RESOURCE_DESTINATION, false, hashUrl); Assert.fail("File checksum verified. This is not suppose to happen."); } catch (Exception e) { if (!(e.getCause() instanceof ChecksumVerifierException)) { Assert.fail("expecting failure due to checksum validation"); } } } @Test public void testResourceDownload() throws Exception { // test get with checksum verification. // Hashing algorithm determined by the checksum file extension. cleanDownloadFolder(); rdf.get(RESOURCE_URL, RESOURCE_DESTINATION); cleanDownloadFolder(); assertDownloadSuccess(RESOURCE_URL + ".md5"); cleanDownloadFolder(); assertDownloadSuccess(RESOURCE_URL + ".sha1"); cleanDownloadFolder(); assertDownloadSuccess(RESOURCE_URL + ".sha256"); cleanDownloadFolder(); assertDownloadSuccess(RESOURCE_URL + ".sha384"); cleanDownloadFolder(); assertDownloadSuccess(RESOURCE_URL + ".sha512"); cleanDownloadFolder(); } private class DownloadTask implements Callable<Exception> { @Override public Exception call() throws Exception { try { rdf.get(RESOURCE_URL, RESOURCE_DESTINATION); } catch (ResourceDownloadException e) { return e; } catch (TimeoutException e) { return e; } return null; } } @Test public void testConcurrentDownload() throws Exception { for (int i = 0; i < 10; ++i) { cleanDownloadFolder(); ExecutorService pool = Executors.newFixedThreadPool(3); try { DownloadTask task1 = new DownloadTask(); DownloadTask task2 = new DownloadTask(); DownloadTask task3 = new DownloadTask(); Future<Exception> future1 = pool.submit(task1); Future<Exception> future2 = pool.submit(task2); Future<Exception> future3 = pool.submit(task3); Exception e1 = future1.get(); Exception e2 = future2.get(); Exception e3 = future3.get(); Assert.assertNull("concurrent download failed: " + e1, e1); Assert.assertNull("concurrent download failed: " + e2, e2); Assert.assertNull("concurrent download failed: " + e3, e3); final File destinationFolder = new File(DESTINATION_FOLDER); File[] files = destinationFolder.listFiles(); Assert.assertEquals("Expecting only one file", 1, files.length); } finally { pool.shutdown(); pool.awaitTermination(10, TimeUnit.SECONDS); cleanDownloadFolder(); } } } @Test public void testChecksumFormatting() throws Exception { // Tests checksum verification on checksum files that // are formatted using format type '{0}'. assertFormattedChecksumVerification("testResourceFormatted.txt.md5"); cleanDownloadFolder(); assertFormattedChecksumVerification("testResourceFormatted.txt.sha1"); cleanDownloadFolder(); assertFormattedChecksumVerification("testResourceFormatted.txt.sha256"); cleanDownloadFolder(); assertFormattedChecksumVerification("testResourceFormatted.txt.sha384"); cleanDownloadFolder(); assertFormattedChecksumVerification("testResourceFormatted.txt.sha512"); cleanDownloadFolder(); } @Test public void testSkipExistingFlag() throws Exception { // asserts file was not overridden. rdf.get(RESOURCE_URL, RESOURCE_DESTINATION, false, RESOURCE_URL + ".md5"); File downloadedResource = new File(DESTINATION_FOLDER, RESOURCE_NAME); long lastModified = downloadedResource.lastModified(); rdf.get(RESOURCE_URL, RESOURCE_DESTINATION, true, RESOURCE_URL + ".md5"); Assert.assertTrue("File was modified but skip existing flag is true", downloadedResource.lastModified() == lastModified); cleanDownloadFolder(); } void assertFormattedChecksumVerification(final String hashFileName) throws ResourceDownloadException { // test verification for special checksum format types final ResourceDownloader downloader = new ResourceDownloader(); final File resourceFile = new File(RESOURCE_FOLDER, RESOURCE_NAME); final File hashFile = new File(RESOURCE_FOLDER, hashFileName); downloader.setFormat(new MessageFormat("{0}")); downloader.setResourceDest(resourceFile); // Expecting not to throw exception. downloader.verifyResourceChecksum(hashFile); } void assertDownloadSuccess(final String hashUrl) throws ResourceDownloadException, TimeoutException, IOException { rdf.get(RESOURCE_URL, RESOURCE_DESTINATION, false, hashUrl); assertFilesDownloaded("testResource.txt", getResourceName(hashUrl)); } private String getResourceName(final String url) { final int slashIndex = url.lastIndexOf('/'); final String filename = url.substring(slashIndex + 1); return filename; } private void assertFilesDownloaded(final String resourceFileName, final String hashFileName) throws IOException { final File downloadedResourceFile = new File(DESTINATION_FOLDER, resourceFileName); final File downloadedHashFile = new File(DESTINATION_FOLDER, hashFileName); Assert.assertTrue("Resource file download failed", downloadedResourceFile.exists()); Assert.assertTrue("Hash file download failed", downloadedHashFile.exists()); final File destinationFolder = new File(DESTINATION_FOLDER); File[] partFiles = destinationFolder.listFiles(new FilenameFilter() { @Override public boolean accept(final File dir, final String name) { return name.contains(".part."); } }); Assert.assertEquals("Expected no part files to be found", 0, partFiles.length); final File originalResourceFile = new File(RESOURCE_FOLDER, resourceFileName); final File originalHashFile = new File(RESOURCE_FOLDER, hashFileName); // asserting file content is the same. Assert.assertTrue("Resource file was not downloaded properly", FileUtils.readFileToString(downloadedResourceFile). equals(FileUtils.readFileToString(originalResourceFile))); Assert.assertTrue("Hash file was not downloaded properly", FileUtils.readFileToString(downloadedHashFile). equals(FileUtils.readFileToString(originalHashFile))); } private void cleanDownloadFolder() throws IOException { FileUtils.cleanDirectory(new File(DESTINATION_FOLDER)); } }