blob: 50563f8dc84d37fc007e244d39f03a86f9383d2d [file] [log] [blame]
Felix Meschberger8d9851a2010-08-25 13:22:44 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.felix.cm.integration;
20
21
22import java.io.PrintWriter;
23import java.io.StringWriter;
24import java.util.Dictionary;
25import java.util.HashSet;
26import java.util.Properties;
27import java.util.Set;
28import java.util.concurrent.CountDownLatch;
29import java.util.concurrent.TimeUnit;
30
31import junit.framework.Assert;
32
33import org.junit.After;
34import org.junit.Before;
35import org.junit.Ignore;
36import org.junit.Test;
37import org.junit.runner.RunWith;
38import org.ops4j.pax.exam.junit.JUnit4TestRunner;
39import org.osgi.framework.BundleContext;
40import org.osgi.framework.ServiceReference;
41import org.osgi.service.cm.ConfigurationAdmin;
42import org.osgi.service.cm.ConfigurationException;
43import org.osgi.service.cm.ManagedServiceFactory;
44import org.osgi.service.log.LogService;
45import org.osgi.util.tracker.ServiceTracker;
46
47
48/**
49 * The <code>ConfigurationAdminUpdateStressTest</code> repeatedly updates
50 * a ManagedFactoryService with configuration to verify configuration is
51 * exactly delivered once and no update is lost.
52 *
53 * @see <a href="https://issues.apache.org/jira/browse/FELIX-1545">FELIX-1545</a>
54 */
55@RunWith(JUnit4TestRunner.class)
56public class ConfigurationAdminUpdateStressTest extends ConfigurationTestBase implements LogService
57{
58 public static final int TEST_LOOP = 10;
59 public static final int UPDATE_LOOP = 100;
60
61 private String _FACTORYPID = "MyPID";
62
63 private volatile CountDownLatch _factoryConfigCreateLatch;
64 private volatile CountDownLatch _factoryConfigUpdateLatch;
65 private volatile CountDownLatch _factoryConfigDeleteLatch;
66 private volatile CountDownLatch _testLatch;
67 private volatile ServiceTracker _tracker;
68
69
70 // ----------------------- Initialization -------------------------------------------
71
72 @Before
73 public void startup( BundleContext context )
74 {
75 context.registerService( LogService.class.getName(), this, null );
76 _tracker = new ServiceTracker( context, ConfigurationAdmin.class.getName(), null );
77 _tracker.open();
78 }
79
80
81 /**
82 * Always cleanup our bundle location file (because pax seems to forget to cleanup it)
83 * @param context
84 */
85
86 @After
87 public void tearDown( BundleContext context )
88 {
89 _tracker.close();
90 }
91
92
93 // ----------------------- LogService -----------------------------------------------
94
95 public void log( int level, String message )
96 {
97 System.out.println( "[LogService/" + level + "] " + message );
98 }
99
100
101 public void log( int level, String message, Throwable exception )
102 {
103 StringBuilder sb = new StringBuilder();
104 sb.append( "[LogService/" + level + "] " );
105 sb.append( message );
106 parse( sb, exception );
107 System.out.println( sb.toString() );
108 }
109
110
111 public void log( ServiceReference sr, int level, String message )
112 {
113 StringBuilder sb = new StringBuilder();
114 sb.append( "[LogService/" + level + "] " );
115 sb.append( message );
116 System.out.println( sb.toString() );
117 }
118
119
120 public void log( ServiceReference sr, int level, String message, Throwable exception )
121 {
122 StringBuilder sb = new StringBuilder();
123 sb.append( "[LogService/" + level + "] " );
124 sb.append( message );
125 parse( sb, exception );
126 System.out.println( sb.toString() );
127 }
128
129
130 private void parse( StringBuilder sb, Throwable t )
131 {
132 if ( t != null )
133 {
134 sb.append( " - " );
135 StringWriter buffer = new StringWriter();
136 PrintWriter pw = new PrintWriter( buffer );
137 t.printStackTrace( pw );
138 sb.append( buffer.toString() );
139 }
140 }
141
142
143 // --------------------------- CM Update stress test -------------------------------------
144
145 @Test
146 public void testCMUpdateStress( BundleContext context )
147 {
148 _testLatch = new CountDownLatch( 1 );
149 try
150 {
151 CreateUpdateStress stress = new CreateUpdateStress( context );
152 stress.start();
153
154 if ( !_testLatch.await( 15, TimeUnit.SECONDS ) )
155 {
156
Felix Meschberger51e9c902010-08-25 17:26:26 +0000157 log( LogService.LOG_DEBUG, "create latch: " + _factoryConfigCreateLatch.getCount() );
158 log( LogService.LOG_DEBUG, "update latch: " + _factoryConfigUpdateLatch.getCount() );
159 log( LogService.LOG_DEBUG, "delete latch: " + _factoryConfigDeleteLatch.getCount() );
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000160
161 Assert.fail( "Test did not completed timely" );
162 }
163 }
164 catch ( InterruptedException e )
165 {
166 Assert.fail( "Test interrupted" );
167 }
168 }
169
170
171 /**
172 * Setup the latches used throughout this test
173 */
174 private void setupLatches()
175 {
176 _factoryConfigCreateLatch = new CountDownLatch( 1 );
177 _factoryConfigUpdateLatch = new CountDownLatch( UPDATE_LOOP );
178 _factoryConfigDeleteLatch = new CountDownLatch( 1 );
179 }
180
181 /**
182 * This is our Factory class which will react up CM factory configuration objects.
183 * Each time a factory configuration object is created, the _factoryConfigCreatedLatch is counted down.
184 * Each time a factory configuration object is updated, the _factoryConfigUpdatedLatch is counted down.
185 * Each time a factory configuration object is deleted, the _factoryConfigDeletedLatch is counted down.
186 */
187 @Ignore
188 class Factory implements ManagedServiceFactory
189 {
190 Set<String> _pids = new HashSet<String>();
191
192
193 public synchronized void updated( String pid, Dictionary properties ) throws ConfigurationException
194 {
195 if ( _pids.add( pid ) )
196 {
197 // pid created
198 _factoryConfigCreateLatch.countDown();
Felix Meschberger51e9c902010-08-25 17:26:26 +0000199 log( LogService.LOG_DEBUG, "Config created; create latch= " + _factoryConfigCreateLatch.getCount() );
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000200 }
201 else
202 {
203 // pid updated
204 try
205 {
206 Long number = ( Long ) properties.get( "number" );
207 long currentNumber = _factoryConfigUpdateLatch.getCount();
208 if ( number.longValue() != currentNumber )
209 {
210 throw new ConfigurationException( "number", "Expected number=" + currentNumber + ", actual="
211 + number );
212 }
213 _factoryConfigUpdateLatch.countDown();
Felix Meschberger51e9c902010-08-25 17:26:26 +0000214 log( LogService.LOG_DEBUG, "Config updated; update latch= " + _factoryConfigUpdateLatch.getCount()
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000215 + " (number=" + number + ")" );
216 }
217 catch ( ClassCastException e )
218 {
219 throw new ConfigurationException( "number", e.getMessage(), e );
220 }
221 }
222 }
223
224
225 public void deleted( String pid )
226 {
227 _factoryConfigDeleteLatch.countDown();
Felix Meschberger51e9c902010-08-25 17:26:26 +0000228 log( LogService.LOG_DEBUG, "Config deleted; delete latch= " + _factoryConfigDeleteLatch.getCount() );
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000229 }
230
231
232 public String getName()
233 {
234 return "MyPID";
235 }
236 }
237
238 /**
239 * This class creates/update/delete some factory configuration instances, using a separate thread.
240 */
241 @Ignore
242 class CreateUpdateStress extends Thread
243 {
244 BundleContext _bc;
245
246
247 CreateUpdateStress( BundleContext bctx )
248 {
249 _bc = bctx;
250 }
251
252
253 public void run()
254 {
255 try
256 {
257 System.out.println( "Starting CM stress test ..." );
258 ConfigurationAdmin cm = ( ConfigurationAdmin ) _tracker.waitForService( 2000 );
259 setupLatches();
260 Factory factory = new Factory();
261 Properties serviceProps = new Properties();
262 serviceProps.put( "service.pid", _FACTORYPID );
263 _bc.registerService( ManagedServiceFactory.class.getName(), factory, serviceProps );
264
265 for ( int l = 0; l < TEST_LOOP; l++ )
266 {
267 // Create factory configuration
268 org.osgi.service.cm.Configuration conf = cm.createFactoryConfiguration( _FACTORYPID, null );
269 Properties props = new Properties();
270 props.put( "foo", "bar" );
271 conf.update( props );
272
273 // Check if our Factory has seen the factory configuration creation
274 if ( !_factoryConfigCreateLatch.await( 10, TimeUnit.SECONDS ) )
275 {
276 throw new RuntimeException( "_factoryConfigCreateLatch did not reach zero timely" );
277 }
278
279 // Update factory configuration many times
280 for ( int i = 0; i < UPDATE_LOOP; i++ )
281 {
282 props = new Properties();
283 props.put( "foo", "bar" + i );
284 props.put( "number", new Long( UPDATE_LOOP - i ) );
285 conf.update( props );
286 }
287
288 // Check if all configuration updates have been caught by our Factory
289 if ( !_factoryConfigUpdateLatch.await( 10, TimeUnit.SECONDS ) )
290 {
291 throw new RuntimeException( "_factoryConfigUpdateLatch did not reach zero timely" );
292 }
293
294 // Remove factory configuration
295 conf.delete();
296
297 // Check if our Factory has seen the configration removal
298 if ( !_factoryConfigDeleteLatch.await( 10, TimeUnit.SECONDS ) )
299 {
300 throw new RuntimeException( "_factoryConfigDeleteLatch did not reach zero timely" );
301 }
302
303 // Reset latches
304 setupLatches();
305 }
306 }
307 catch ( Exception e )
308 {
309 e.printStackTrace( System.err );
310 return;
311 }
312 _testLatch.countDown(); // Notify that our test is done
313 }
314 }
315}