/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.cxf.io; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import org.apache.cxf.Bus; import org.apache.cxf.BusFactory; import org.apache.cxf.helpers.IOUtils; import org.easymock.EasyMock; import org.easymock.IMocksControl; import org.junit.Assert; import org.junit.Test; public abstract class CachedStreamTestBase extends Assert { // use two typical ciphers for testing private static final String[] CIPHER_LIST = {"RC4", "AES/CTR/NoPadding"}; protected abstract void reloadDefaultProperties(); protected abstract Object createCache(); protected abstract Object createCache(long threshold); protected abstract Object createCache(long threshold, String transformation); protected abstract String getResetOutValue(String result, Object cache) throws IOException; protected abstract File getTmpFile(String result, Object cache) throws IOException; protected abstract Object getInputStreamObject(Object cache) throws IOException; protected abstract String readFromStreamObject(Object cache) throws IOException; protected abstract String readPartiallyFromStreamObject(Object cache, int len) throws IOException; @Test public void testResetOut() throws IOException { String result = initTestData(16); Object cache = createCache(); String test = getResetOutValue(result, cache); assertEquals("The test stream content isn't same ", test, result); close(cache); } @Test public void testDeleteTmpFile() throws IOException { Object cache = createCache(); //ensure output data size larger then 64k which will generate tmp file String result = initTestData(65); File tempFile = getTmpFile(result, cache); assertNotNull(tempFile); //assert tmp file is generated assertTrue(tempFile.exists()); close(cache); //assert tmp file is deleted after close the CachedOutputStream assertFalse(tempFile.exists()); } @Test public void testDeleteTmpFile2() throws IOException { Object cache = createCache(); //ensure output data size larger then 128k which will generate tmp file String result = initTestData(130); File tempFile = getTmpFile(result, cache); assertNotNull(tempFile); //assert tmp file is generated assertTrue(tempFile.exists()); Object in = getInputStreamObject(cache); close(cache); //assert tmp file is not deleted when the input stream is open assertTrue(tempFile.exists()); close(in); //assert tmp file is deleted after the input stream is closed assertFalse(tempFile.exists()); } @Test public void testEncryptAndDecryptWithDeleteOnClose() throws IOException { // need a 8-bit cipher so that all bytes are flushed when the stream is flushed. for (String cipher: CIPHER_LIST) { verifyEncryptAndDecryptWithDeleteOnClose(cipher); } } private void verifyEncryptAndDecryptWithDeleteOnClose(String cipher) throws IOException { Object cache = createCache(4, cipher); final String text = "Hello Secret World!"; File tmpfile = getTmpFile(text, cache); assertNotNull(tmpfile); final String enctext = readFromStream(new FileInputStream(tmpfile)); assertFalse("text is not encoded", text.equals(enctext)); Object fin = getInputStreamObject(cache); assertTrue("file is deleted", tmpfile.exists()); final String dectext = readFromStreamObject(fin); assertEquals("text is not decoded correctly", text, dectext); // the file is deleted when cos is closed while all the associated inputs are closed assertTrue("file is deleted", tmpfile.exists()); close(cache); assertFalse("file is not deleted", tmpfile.exists()); } @Test public void testEncryptAndDecryptWithDeleteOnInClose() throws IOException { for (String cipher: CIPHER_LIST) { verifyEncryptAndDecryptWithDeleteOnInClose(cipher); } } private void verifyEncryptAndDecryptWithDeleteOnInClose(String cipher) throws IOException { // need a 8-bit cipher so that all bytes are flushed when the stream is flushed. Object cache = createCache(4, cipher); final String text = "Hello Secret World!"; File tmpfile = getTmpFile(text, cache); assertNotNull(tmpfile); final String enctext = readFromStream(new FileInputStream(tmpfile)); assertFalse("text is not encoded", text.equals(enctext)); Object fin = getInputStreamObject(cache); close(cache); assertTrue("file is deleted", tmpfile.exists()); // the file is deleted when cos is closed while all the associated inputs are closed final String dectext = readFromStreamObject(fin); assertEquals("text is not decoded correctly", text, dectext); assertFalse("file is not deleted", tmpfile.exists()); } @Test public void testEncryptAndDecryptPartially() throws IOException { for (String cipher: CIPHER_LIST) { verifyEncryptAndDecryptPartially(cipher); } } private void verifyEncryptAndDecryptPartially(String cipher) throws IOException { // need a 8-bit cipher so that all bytes are flushed when the stream is flushed. Object cache = createCache(4, cipher); final String text = "Hello Secret World!"; File tmpfile = getTmpFile(text, cache); assertNotNull(tmpfile); Object fin = getInputStreamObject(cache); // read partially and keep the stream open String pdectext = readPartiallyFromStreamObject(fin, 4); assertTrue("text is not decoded correctly", text.startsWith(pdectext)); Object fin2 = getInputStreamObject(cache); final String dectext = readFromStreamObject(fin2); assertEquals("text is not decoded correctly", text, dectext); // close the partially read stream if (fin instanceof Closeable) { ((Closeable)fin).close(); } // the file is deleted when cos is closed while all the associated inputs are closed assertTrue("file is deleted", tmpfile.exists()); close(cache); assertFalse("file is not deleted", tmpfile.exists()); } @Test public void testUseSysProps() throws Exception { String old = System.getProperty("org.apache.cxf.io.CachedOutputStream.Threshold"); try { System.clearProperty("org.apache.cxf.io.CachedOutputStream.Threshold"); reloadDefaultProperties(); Object cache = createCache(); File tmpfile = getTmpFile("Hello World!", cache); assertNull("expects no tmp file", tmpfile); close(cache); System.setProperty("org.apache.cxf.io.CachedOutputStream.Threshold", "4"); reloadDefaultProperties(); cache = createCache(); tmpfile = getTmpFile("Hello World!", cache); assertNotNull("expects a tmp file", tmpfile); assertTrue("expects a tmp file", tmpfile.exists()); close(cache); assertFalse("expects no tmp file", tmpfile.exists()); } finally { if (old != null) { System.setProperty("org.apache.cxf.io.CachedOutputStream.Threshold", old); } } } @Test public void testUseBusProps() throws Exception { Bus oldbus = BusFactory.getThreadDefaultBus(false); try { Object cache = createCache(64); File tmpfile = getTmpFile("Hello World!", cache); assertNull("expects no tmp file", tmpfile); close(cache); IMocksControl control = EasyMock.createControl(); Bus b = control.createMock(Bus.class); EasyMock.expect(b.getProperty("bus.io.CachedOutputStream.Threshold")).andReturn("4"); EasyMock.expect(b.getProperty("bus.io.CachedOutputStream.MaxSize")).andReturn(null); EasyMock.expect(b.getProperty("bus.io.CachedOutputStream.CipherTransformation")).andReturn(null); BusFactory.setThreadDefaultBus(b); control.replay(); cache = createCache(); tmpfile = getTmpFile("Hello World!", cache); assertNotNull("expects a tmp file", tmpfile); assertTrue("expects a tmp file", tmpfile.exists()); close(cache); assertFalse("expects no tmp file", tmpfile.exists()); control.verify(); } finally { BusFactory.setThreadDefaultBus(oldbus); } } private static void close(Object obj) throws IOException { if (obj instanceof Closeable) { ((Closeable)obj).close(); } } protected static String readFromStream(InputStream is) throws IOException { try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) { IOUtils.copyAndCloseInput(is, buf); return new String(buf.toByteArray(), StandardCharsets.UTF_8); } } protected static String readPartiallyFromStream(InputStream is, int len) throws IOException { try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) { IOUtils.copyAtLeast(is, buf, len); return new String(buf.toByteArray(), StandardCharsets.UTF_8); } } protected static String readFromReader(Reader is) throws IOException { try (StringWriter writer = new StringWriter()) { IOUtils.copyAndCloseInput(is, writer); return writer.toString(); } } protected static String readPartiallyFromReader(Reader is, int len) throws IOException { try (StringWriter writer = new StringWriter()) { IOUtils.copyAtLeast(is, writer, len); return writer.toString(); } } private static String initTestData(int packetSize) { String temp = "abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+?><[]/0123456789"; String result = new String(); for (int i = 0; i < 1024 * packetSize / temp.length(); i++) { result = result + temp; } return result; } }