001 // Copyright 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry.record; 016 017 018 import java.io.File; 019 import java.io.Serializable; 020 import java.lang.reflect.Constructor; 021 import java.net.URL; 022 import java.net.URLClassLoader; 023 import java.util.ArrayList; 024 import java.util.Collections; 025 import java.util.List; 026 import java.util.Random; 027 028 import org.apache.hivemind.ApplicationRuntimeException; 029 import org.apache.hivemind.ClassResolver; 030 import org.apache.hivemind.impl.DefaultClassResolver; 031 import org.apache.hivemind.util.PropertyUtils; 032 import org.apache.tapestry.BaseComponentTestCase; 033 import org.testng.annotations.Test; 034 035 /** 036 * Tests for {@link org.apache.tapestry.record.PersistentPropertyDataEncoderImpl}. 037 * 038 * @author Howard M. Lewis Ship 039 * @since 4.0 040 */ 041 @Test 042 public class PersistentPropertyDataEncoderTest extends BaseComponentTestCase 043 { 044 /** 045 * Test pushing minimal amounts of data, which should favor the non-GZipped version of the 046 * output stream. 047 */ 048 049 public void testRoundTripShort() throws Exception 050 { 051 PropertyChange pc = new PropertyChangeImpl(null, "property", "foo"); 052 List input = Collections.singletonList(pc); 053 054 PersistentPropertyDataEncoder encoder = newEncoder(); 055 056 String encoded = encoder.encodePageChanges(input); 057 058 System.out.println(encoded); 059 060 List output = encoder.decodePageChanges(encoded); 061 062 assertEquals(input, output); 063 064 } 065 066 /** 067 * Test pushing a lot of data, which should trigger the GZip encoding option. 068 */ 069 070 public void testRoundTripLong() throws Exception 071 { 072 Random r = new Random(); 073 074 List input = new ArrayList(); 075 076 for (int i = 0; i < 20; i++) 077 { 078 PropertyChange pc = new PropertyChangeImpl(i % 2 == 0 ? null : "componentId", 079 "property" + i, new Long(r.nextLong())); 080 081 input.add(pc); 082 } 083 084 PersistentPropertyDataEncoder encoder = newEncoder(); 085 086 String encoded = encoder.encodePageChanges(input); 087 088 assertEquals("Z", encoded.substring(0, 1)); 089 090 List output = encoder.decodePageChanges(encoded); 091 092 assertEquals(input, output); 093 } 094 095 private PersistentPropertyDataEncoder newEncoder() 096 { 097 return newEncoder(getClassResolver()); 098 } 099 100 private PersistentPropertyDataEncoder newEncoder(ClassResolver resolver) 101 { 102 PersistentPropertyDataEncoderImpl encoder = new PersistentPropertyDataEncoderImpl(); 103 104 encoder.setClassResolver(resolver); 105 106 return encoder; 107 } 108 109 public void testEmptyEncoding() 110 { 111 PersistentPropertyDataEncoder encoder = newEncoder(); 112 113 assertEquals("", encoder.encodePageChanges(Collections.EMPTY_LIST)); 114 115 assertEquals(0, encoder.decodePageChanges("").size()); 116 } 117 118 public void testEncodeNonSerializable() 119 { 120 PropertyChange pc = new PropertyChangeImpl(null, "property", new Object()); 121 List l = Collections.singletonList(pc); 122 123 PersistentPropertyDataEncoder encoder = newEncoder(); 124 125 try 126 { 127 encoder.encodePageChanges(l); 128 unreachable(); 129 } 130 catch (ApplicationRuntimeException ex) 131 { 132 assertEquals( 133 "An exception occured encoding the data stream into MIME format: java.lang.Object", 134 ex.getMessage()); 135 } 136 } 137 138 public void testDecodeInvalid() 139 { 140 PersistentPropertyDataEncoder encoder = newEncoder(); 141 142 try 143 { 144 encoder.decodePageChanges("ZZZZZZZZZZZZZZZZZZZ"); 145 unreachable(); 146 } 147 catch (ApplicationRuntimeException ex) 148 { 149 assertEquals( 150 "An exception occured decoding the MIME data stream: Not in GZIP format", 151 ex.getMessage()); 152 } 153 } 154 155 public void testDecodeUnknownPrefix() 156 { 157 PersistentPropertyDataEncoder encoder = newEncoder(); 158 159 try 160 { 161 encoder.decodePageChanges("QQQQQQQQQQQ"); 162 unreachable(); 163 } 164 catch (ApplicationRuntimeException ex) 165 { 166 assertEquals( 167 "The prefix of the MIME encoded data stream was 'Q', it should be 'B' or 'Z'.", 168 ex.getMessage()); 169 } 170 171 } 172 173 /** 174 * Test encoding and decoding a class that's only visible through a non-default class loader. We 175 * have to use a lot of reflection on this one. 176 * 177 * @see org.apache.tapestry.junit.utils.TestDataSqueezer#testClassLoader() 178 */ 179 public void testEncodeDecodeCustomClass() throws Exception 180 { 181 File springJAR = new File("tapestry-framework/src/test-data/spring-1.1.jar"); 182 if (!springJAR.exists()) 183 springJAR = new File("src/test-data/spring-1.1.jar"); 184 185 if (!springJAR.exists()) 186 throw new RuntimeException("File " + springJAR 187 + " does not exist; this should have been downloaded by the Ant build scripts."); 188 189 ClassResolver resolver1 = newClassResolver(springJAR); 190 191 Class propertyValueClass = resolver1.findClass("org.springframework.beans.PropertyValue"); 192 Constructor constructor = propertyValueClass.getConstructor(new Class[] 193 { String.class, Object.class }); 194 195 Serializable instance = (Serializable) constructor.newInstance(new Object[] 196 { "fred", "flintstone" }); 197 198 assertEquals("fred", PropertyUtils.read(instance, "name")); 199 assertEquals("flintstone", PropertyUtils.read(instance, "value")); 200 201 PersistentPropertyDataEncoder encoder1 = newEncoder(resolver1); 202 203 PropertyChange pc = new PropertyChangeImpl("foo.bar", "property", instance); 204 List changes = Collections.singletonList(pc); 205 206 String encoded = encoder1.encodePageChanges(changes); 207 208 // OK, to be 100% sure, we create a NEW encoder to decode the string in. 209 ClassResolver resolver2 = newClassResolver(springJAR); 210 PersistentPropertyDataEncoder encoder2 = newEncoder(resolver2); 211 212 changes = encoder2.decodePageChanges(encoded); 213 214 assertEquals(1, changes.size()); 215 216 pc = (PropertyChange) changes.get(0); 217 218 assertEquals("property", pc.getPropertyName()); 219 assertEquals("foo.bar", pc.getComponentPath()); 220 221 Object instance2 = pc.getNewValue(); 222 223 assertNotSame(instance, instance2); 224 225 assertEquals("fred", PropertyUtils.read(instance2, "name")); 226 assertEquals("flintstone", PropertyUtils.read(instance2, "value")); 227 } 228 229 private ClassResolver newClassResolver(File jarFile) throws Exception 230 { 231 URLClassLoader classLoader = new URLClassLoader(new URL[] { jarFile.toURI().toURL() }); 232 233 return new DefaultClassResolver(classLoader); 234 235 } 236 }