View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.component;
8   
9   import org.mule.api.component.LifecycleAdapter;
10  import org.mule.api.lifecycle.InitialisationException;
11  import org.mule.api.object.ObjectFactory;
12  import org.mule.config.PoolingProfile;
13  import org.mule.object.PrototypeObjectFactory;
14  import org.mule.tck.services.UniqueComponent;
15  import org.mule.tck.testmodels.fruit.Orange;
16  import org.mule.tck.testmodels.fruit.WaterMelon;
17  import org.mule.util.ExceptionUtils;
18  import org.mule.util.pool.AbstractPoolingTestCase;
19  
20  import java.util.NoSuchElementException;
21  
22  import org.junit.Test;
23  
24  import static org.junit.Assert.assertEquals;
25  import static org.junit.Assert.assertFalse;
26  import static org.junit.Assert.assertNotNull;
27  import static org.junit.Assert.assertNotSame;
28  import static org.junit.Assert.assertNull;
29  import static org.junit.Assert.assertTrue;
30  import static org.junit.Assert.fail;
31  
32  public class PooledJavaComponentTestCase extends AbstractPoolingTestCase
33  {    
34      @Test
35      public void testComponentCreation() throws Exception
36      {
37          PrototypeObjectFactory objectFactory = getDefaultObjectFactory();
38  
39          PoolingProfile pp = createDefaultPoolingProfile();
40          pp.setExhaustedAction(PoolingProfile.WHEN_EXHAUSTED_FAIL);
41  
42          PooledJavaComponent component = new PooledJavaComponent(objectFactory, pp);
43          component.setMuleContext(muleContext);
44          assertNotNull(component.getObjectFactory());
45          assertEquals(objectFactory, component.getObjectFactory());
46          assertEquals(Orange.class, component.getObjectType());
47  
48          assertNotNull(component.getPoolingProfile());
49          assertEquals(pp, component.getPoolingProfile());
50      }
51  
52      @Test
53      public void testPoolManagement() throws Exception
54      {
55          PooledJavaComponent component = new PooledJavaComponent(getDefaultObjectFactory(), createDefaultPoolingProfile());
56          assertNull(component.lifecycleAdapterPool);
57          
58          component.setFlowConstruct(getTestService());
59          component.setMuleContext(muleContext);
60          component.initialise();
61          assertNull(component.lifecycleAdapterPool);
62          
63          component.start();
64          assertNotNull(component.lifecycleAdapterPool);
65          
66          component.stop();
67          assertNull(component.lifecycleAdapterPool);
68      }
69  
70      @Test
71      public void testStartStop() throws Exception
72      {
73          PooledJavaComponent component = createPooledComponent();
74          assertNotSame(component.borrowComponentLifecycleAdaptor(), component.borrowComponentLifecycleAdaptor());
75          
76          component.stop();
77          component.start();
78  
79          Object la1 = ((DefaultComponentLifecycleAdapter) component.borrowComponentLifecycleAdaptor()).componentObject;
80          Object la2 = ((DefaultComponentLifecycleAdapter) component.borrowComponentLifecycleAdaptor()).componentObject;
81          assertNotSame(la1, la2);
82      }
83  
84      @Test
85      public void testCreateLifecycleAdapters() throws Exception
86      {
87          PooledJavaComponent component = createPooledComponent();
88          assertEquals(0, component.lifecycleAdapterPool.getNumActive());
89  
90          LifecycleAdapter borrowed = component.borrowComponentLifecycleAdaptor();
91          assertNotNull(borrowed);
92          assertEquals(1, component.lifecycleAdapterPool.getNumActive());
93          
94          component.returnComponentLifecycleAdaptor(borrowed);
95          assertEquals(0, component.lifecycleAdapterPool.getNumActive());
96  
97          borrowed = component.borrowComponentLifecycleAdaptor();
98          assertNotNull(borrowed);
99          assertEquals(1, component.lifecycleAdapterPool.getNumActive());
100         
101         Object borrowed2 = component.borrowComponentLifecycleAdaptor();
102         assertNotNull(borrowed2);
103         assertEquals(2, component.lifecycleAdapterPool.getNumActive());
104     }
105 
106     @Test
107     public void testFailOnExhaust() throws Exception
108     {
109         PoolingProfile pp = createDefaultPoolingProfile();
110         pp.setExhaustedAction(PoolingProfile.WHEN_EXHAUSTED_FAIL);
111         
112         PooledJavaComponent component = createPooledComponent(pp);
113         borrowLifecycleAdaptersUntilPoolIsFull(component);
114 
115         try
116         {
117             component.borrowComponentLifecycleAdaptor();
118             fail("Should throw an Exception");
119         }
120         catch (NoSuchElementException nse)
121         {
122             // expected
123         }
124     }
125 
126     @Test
127     public void testBlockExpiryOnExhaust() throws Exception
128     {
129         PoolingProfile pp = createDefaultPoolingProfile();
130         pp.setExhaustedAction(PoolingProfile.WHEN_EXHAUSTED_WAIT);
131         
132         PooledJavaComponent component = createPooledComponent(pp);
133         assertEquals(0, component.lifecycleAdapterPool.getNumActive());
134         
135         borrowLifecycleAdaptersUntilPoolIsFull(component);
136 
137         long startTime = System.currentTimeMillis();
138         try
139         {
140             component.borrowComponentLifecycleAdaptor();
141             fail("Should throw an Exception");
142         }
143         catch (NoSuchElementException e)
144         {
145             long totalTime = System.currentTimeMillis() - startTime;
146             assertTrue(totalTime >= MAX_WAIT);
147         }
148     }
149 
150     @Test
151     public void testBlockOnExhaust() throws Exception
152     {
153         PoolingProfile pp = createDefaultPoolingProfile();
154         pp.setExhaustedAction(PoolingProfile.WHEN_EXHAUSTED_WAIT);
155         
156         PooledJavaComponent component = createPooledComponent(pp);
157         assertEquals(0, component.lifecycleAdapterPool.getNumActive());
158 
159         // borrow all but one lifecycle adapters
160         int oneRemainingInPool = (MAX_ACTIVE - 1);
161         for (int i = 0; i < oneRemainingInPool; i++)
162         {
163             LifecycleAdapter borrowed = component.borrowComponentLifecycleAdaptor();
164             assertNotNull(borrowed);
165             assertEquals(component.lifecycleAdapterPool.getNumActive(), i + 1);
166         }
167         assertEquals(oneRemainingInPool, component.lifecycleAdapterPool.getNumActive());
168         
169         long startTime = System.currentTimeMillis();
170         int borrowerWait = 500;
171         Borrower borrower = new Borrower(component, borrowerWait);
172         new Thread(borrower, "BorrowThread").start();
173 
174         // Make sure the borrower borrows first
175         try
176         {
177             Thread.sleep(200);
178         }
179         catch (InterruptedException e)
180         {
181             // ignore
182         }
183 
184         // this will get an object from the pool eventually, after Borrower has returned it
185         Object borrowed = component.borrowComponentLifecycleAdaptor();
186         assertNotNull(borrowed);
187         long totalTime = System.currentTimeMillis() - startTime;
188         assertTrue(totalTime >= borrowerWait);
189     }
190     
191     @Test
192     public void testGrowOnExhaust() throws Exception
193     {
194         PoolingProfile pp = createDefaultPoolingProfile();
195         pp.setExhaustedAction(PoolingProfile.WHEN_EXHAUSTED_GROW);
196         
197         PooledJavaComponent component = createPooledComponent(pp);
198 
199         borrowLifecycleAdaptersUntilPoolIsFull(component);
200 
201         // Should now grow
202         Object borrowed = component.borrowComponentLifecycleAdaptor();
203         assertNotNull(borrowed);
204         assertEquals(MAX_ACTIVE + 1, component.lifecycleAdapterPool.getNumActive());
205     }
206 
207     @Test
208     public void testClearPool() throws Exception
209     {
210         PoolingProfile pp = createDefaultPoolingProfile();
211         pp.setExhaustedAction(PoolingProfile.WHEN_EXHAUSTED_FAIL);
212         
213         PooledJavaComponent component = createPooledComponent(pp);
214 
215         LifecycleAdapter borrowed = component.borrowComponentLifecycleAdaptor();
216         assertEquals(1, component.lifecycleAdapterPool.getNumActive());
217         component.returnComponentLifecycleAdaptor(borrowed);
218         assertEquals(0, component.lifecycleAdapterPool.getNumActive());
219 
220         component.stop();
221         component.start();
222         assertEquals(0, component.lifecycleAdapterPool.getNumActive());
223     }
224 
225     @Test
226     public void testObjectUniqueness() throws Exception
227     {
228         PrototypeObjectFactory objectFactory = new PrototypeObjectFactory(UniqueComponent.class);
229         PooledJavaComponent component = createPooledComponent(objectFactory);
230         assertEquals(0, component.lifecycleAdapterPool.getNumActive());
231 
232         String id1 = getIdFromObjectCreatedByPool(component);
233         String id2 = getIdFromObjectCreatedByPool(component);
234         String id3 = getIdFromObjectCreatedByPool(component);
235 
236         assertFalse("Service IDs " + id1 + " and " + id2 + " should be different", id1.equals(id2));
237         assertFalse("Service IDs " + id2 + " and " + id3 + " should be different", id2.equals(id3));
238         assertFalse("Service IDs " + id1 + " and " + id3 + " should be different", id1.equals(id3));
239     }
240     
241     @Test
242     public void testDisposingFactoryDisposesObject() throws Exception
243     {
244         PrototypeObjectFactory objectFactory = new PrototypeObjectFactory(WaterMelon.class);
245         PooledJavaComponent component = createPooledComponent(objectFactory);
246 
247         DefaultComponentLifecycleAdapter lifecycleAdapter = (DefaultComponentLifecycleAdapter) component.borrowComponentLifecycleAdaptor();
248         component.returnComponentLifecycleAdaptor(lifecycleAdapter);
249         component.stop();
250         component.dispose();
251 
252         assertNull(lifecycleAdapter.componentObject);
253     }
254     
255     private PrototypeObjectFactory getDefaultObjectFactory() throws InitialisationException
256     {
257         PrototypeObjectFactory objectFactory = new PrototypeObjectFactory(Orange.class);
258         objectFactory.initialise();
259         return objectFactory;
260     }
261     
262     private PooledJavaComponent createPooledComponent() throws Exception
263     {
264         return createPooledComponent(createDefaultPoolingProfile(), getDefaultObjectFactory());
265     }
266 
267     private PooledJavaComponent createPooledComponent(ObjectFactory objectFactory) throws Exception
268     {
269         return createPooledComponent(createDefaultPoolingProfile(), objectFactory);
270     }
271     
272     private PooledJavaComponent createPooledComponent(PoolingProfile poolingProfile) throws Exception
273     {
274         return createPooledComponent(poolingProfile, getDefaultObjectFactory());
275     }
276     
277     private PooledJavaComponent createPooledComponent(PoolingProfile poolingProfile, ObjectFactory objectFactory) throws Exception
278     {
279         PooledJavaComponent component = new PooledJavaComponent(objectFactory, poolingProfile);
280         component.setMuleContext(muleContext);
281         component.setFlowConstruct(getTestService());
282         component.initialise();
283         component.start();
284         return component;
285     }
286     
287     private void borrowLifecycleAdaptersUntilPoolIsFull(PooledJavaComponent component) throws Exception
288     {
289         for (int i = 0; i < MAX_ACTIVE; i++)
290         {
291             Object borrowed = component.borrowComponentLifecycleAdaptor();
292             assertNotNull(borrowed);
293             assertEquals(component.lifecycleAdapterPool.getNumActive(), i + 1);
294         }
295         assertEquals(MAX_ACTIVE, component.lifecycleAdapterPool.getNumActive());
296     }
297 
298     private String getIdFromObjectCreatedByPool(PooledJavaComponent component) throws Exception
299     {
300         DefaultComponentLifecycleAdapter lifecycleAdapter = 
301             (DefaultComponentLifecycleAdapter) component.borrowComponentLifecycleAdaptor();
302         Object obj = lifecycleAdapter.componentObject;
303         
304         // there is a slight chance that GC kicks in before we can get a hard reference to the 
305         // object. If this occurs, talk do Dirk and Andrew P about how to fix this
306         assertNotNull(obj);
307         
308         assertTrue("Object should be of type UniqueComponent", obj instanceof UniqueComponent);
309      
310         String id = ((UniqueComponent) obj).getId();
311         assertNotNull(id);
312         return id;
313     }
314     
315     private static class Borrower implements Runnable
316     {
317         private PooledJavaComponent component;
318         private long time;
319 
320         public Borrower(PooledJavaComponent component, long time)
321         {
322             super();
323             if (component == null)
324             {
325                 throw new IllegalArgumentException("Pool cannot be null");
326             }
327             this.component = component;
328             if (time < 500)
329             {
330                 time = 500;
331             }
332             this.time = time;
333         }
334 
335         public void run()
336         {
337             try
338             {
339                 LifecycleAdapter object = component.borrowComponentLifecycleAdaptor();
340                 try
341                 {
342                     Thread.sleep(time);
343                 }
344                 catch (InterruptedException e)
345                 {
346                     // ignore
347                 }
348                 component.returnComponentLifecycleAdaptor(object);
349             }
350             catch (Exception e)
351             {
352                 fail("Borrower thread failed:\n" + ExceptionUtils.getStackTrace(e));
353             }
354         }
355     }
356 }