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    }