blob: 4e20e79d5ac2a1de42f601a6628997db0e306c61 [file] [log] [blame]
Felix Meschbergera6ad9762009-08-19 13:38:56 +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.scr.integration;
20
21
Felix Meschberger4f43c912012-03-08 04:37:56 +000022import static org.ops4j.pax.exam.CoreOptions.junitBundles;
Felix Meschbergera6ad9762009-08-19 13:38:56 +000023import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
24import static org.ops4j.pax.exam.CoreOptions.options;
25import static org.ops4j.pax.exam.CoreOptions.provision;
Felix Meschberger12d8c102009-12-18 12:47:44 +000026import static org.ops4j.pax.exam.CoreOptions.systemProperty;
Felix Meschberger4f43c912012-03-08 04:37:56 +000027import static org.ops4j.pax.tinybundles.core.TinyBundles.bundle;
28import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
Felix Meschbergera6ad9762009-08-19 13:38:56 +000029
David Jencks29ea6c02012-09-05 21:49:24 +000030import java.io.BufferedOutputStream;
Felix Meschberger2e885422009-08-21 11:35:00 +000031import java.io.File;
David Jencks29ea6c02012-09-05 21:49:24 +000032import java.io.FileDescriptor;
33import java.io.FileOutputStream;
Felix Meschbergera6ad9762009-08-19 13:38:56 +000034import java.io.IOException;
35import java.io.InputStream;
David Jencks29ea6c02012-09-05 21:49:24 +000036import java.io.OutputStream;
David Jencks8584cf92012-06-16 03:56:11 +000037import java.io.PrintStream;
David Jencks29ea6c02012-09-05 21:49:24 +000038import java.io.PrintWriter;
39import java.io.StringWriter;
Felix Meschberger2e885422009-08-21 11:35:00 +000040import java.lang.reflect.Field;
David Jencks2bdb7712012-10-05 17:19:18 +000041import java.text.SimpleDateFormat;
David Jencks29ea6c02012-09-05 21:49:24 +000042import java.util.ArrayList;
David Jencks8584cf92012-06-16 03:56:11 +000043import java.util.Arrays;
44import java.util.Collections;
David Jencks29ea6c02012-09-05 21:49:24 +000045import java.util.Date;
Felix Meschbergera6ad9762009-08-19 13:38:56 +000046import java.util.Dictionary;
47import java.util.Hashtable;
David Jencks8584cf92012-06-16 03:56:11 +000048import java.util.Iterator;
David Jencks29ea6c02012-09-05 21:49:24 +000049import java.util.List;
David Jencks8584cf92012-06-16 03:56:11 +000050import java.util.TreeSet;
Pierre De Ropf70d25f2012-10-17 11:29:09 +000051import java.util.concurrent.LinkedBlockingQueue;
Felix Meschberger4f43c912012-03-08 04:37:56 +000052
53import javax.inject.Inject;
Pierre De Rope69536e2012-10-13 11:54:00 +000054
Felix Meschbergera6ad9762009-08-19 13:38:56 +000055import junit.framework.TestCase;
56
57import org.apache.felix.scr.Component;
David Jencks8584cf92012-06-16 03:56:11 +000058import org.apache.felix.scr.Reference;
Felix Meschbergera6ad9762009-08-19 13:38:56 +000059import org.apache.felix.scr.ScrService;
Felix Meschbergera6ad9762009-08-19 13:38:56 +000060import org.junit.After;
61import org.junit.Before;
Felix Meschberger2e885422009-08-21 11:35:00 +000062import org.ops4j.pax.exam.CoreOptions;
Felix Meschbergera6ad9762009-08-19 13:38:56 +000063import org.ops4j.pax.exam.Option;
Felix Meschberger2e885422009-08-21 11:35:00 +000064import org.ops4j.pax.exam.OptionUtils;
Felix Meschberger4f43c912012-03-08 04:37:56 +000065import org.ops4j.pax.exam.TestProbeBuilder;
Felix Meschbergera6ad9762009-08-19 13:38:56 +000066import org.ops4j.pax.exam.junit.Configuration;
Felix Meschberger4f43c912012-03-08 04:37:56 +000067import org.ops4j.pax.exam.junit.ProbeBuilder;
Felix Meschbergera6ad9762009-08-19 13:38:56 +000068import org.osgi.framework.Bundle;
69import org.osgi.framework.BundleContext;
70import org.osgi.framework.BundleException;
71import org.osgi.framework.Constants;
Pierre De Ropf70d25f2012-10-17 11:29:09 +000072import org.osgi.framework.FrameworkEvent;
73import org.osgi.framework.FrameworkListener;
Felix Meschbergera6ad9762009-08-19 13:38:56 +000074import org.osgi.framework.InvalidSyntaxException;
David Jencks29ea6c02012-09-05 21:49:24 +000075import org.osgi.framework.ServiceReference;
Felix Meschbergera6ad9762009-08-19 13:38:56 +000076import org.osgi.service.cm.ConfigurationAdmin;
Pierre De Rope69536e2012-10-13 11:54:00 +000077import org.osgi.service.log.LogService;
Felix Meschbergera6ad9762009-08-19 13:38:56 +000078import org.osgi.util.tracker.ServiceTracker;
79
80
81public abstract class ComponentTestBase
82{
83
84 @Inject
85 protected BundleContext bundleContext;
86
87 protected Bundle bundle;
88
89 protected ServiceTracker scrTracker;
90
91 protected ServiceTracker configAdminTracker;
92
Felix Meschberger55e3b972009-10-12 07:16:08 +000093 // the name of the system property providing the bundle file to be installed and tested
94 protected static final String BUNDLE_JAR_SYS_PROP = "project.bundle.file";
95
96 // the default bundle jar file name
97 protected static final String BUNDLE_JAR_DEFAULT = "target/scr.jar";
98
Felix Meschbergera6ad9762009-08-19 13:38:56 +000099 protected static final String PROP_NAME = "theValue";
100 protected static final Dictionary<String, String> theConfig;
101
Felix Meschberger2e885422009-08-21 11:35:00 +0000102 // the JVM option to set to enable remote debugging
103 protected static final String DEBUG_VM_OPTION = "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=30303";
104
105 // the actual JVM option set, extensions may implement a static
106 // initializer overwriting this value to have the configuration()
107 // method include it when starting the OSGi framework JVM
108 protected static String paxRunnerVmOption = null;
109
David Jencksa5a62252012-10-12 04:26:02 +0000110 protected static String DS_LOGLEVEL = "debug";
111
Felix Meschbergerf975aa52009-08-24 10:37:44 +0000112 // the descriptor file to use for the installed test bundle
113 protected static String descriptorFile = "/integration_test_simple_components.xml";
114
David Jencksbad95fc2012-06-05 23:29:58 +0000115 protected static boolean NONSTANDARD_COMPONENT_FACTORY_BEHAVIOR = false;
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000116 protected volatile Log log;
David Jencksbad95fc2012-06-05 23:29:58 +0000117
David Jencks301226a2012-10-25 18:00:00 +0000118 //set to true to only get last 1000 lines of log.
119 protected static boolean restrictedLogging;
120
121
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000122 static
123 {
124 theConfig = new Hashtable<String, String>();
125 theConfig.put( PROP_NAME, PROP_NAME );
126 }
127
Felix Meschberger4f43c912012-03-08 04:37:56 +0000128 @ProbeBuilder
129 public TestProbeBuilder extendProbe(TestProbeBuilder builder) {
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000130 builder.setHeader("Export-Package", "org.apache.felix.scr.integration.components," +
131 "org.apache.felix.scr.integration.components.activatesignature," +
132 "org.apache.felix.scr.integration.components.circular," +
133 "org.apache.felix.scr.integration.components.concurrency," +
Pierre De Rop636c9822012-10-21 09:43:13 +0000134 "org.apache.felix.scr.integration.components.felix3680," +
135 "org.apache.felix.scr.integration.components.felix3680_2");
Felix Meschberger8422a302012-03-08 05:55:06 +0000136 builder.setHeader("Import-Package", "org.apache.felix.scr,org.apache.felix.scr.component;mandatory:=\"status\"; status=\"provisional\"");
137 builder.setHeader("Bundle-ManifestVersion", "2");
Felix Meschberger4f43c912012-03-08 04:37:56 +0000138 return builder;
139 }
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000140
141 @Configuration
142 public static Option[] configuration()
143 {
Felix Meschberger55e3b972009-10-12 07:16:08 +0000144 final String bundleFileName = System.getProperty( BUNDLE_JAR_SYS_PROP, BUNDLE_JAR_DEFAULT );
145 final File bundleFile = new File( bundleFileName );
146 if ( !bundleFile.canRead() )
147 {
148 throw new IllegalArgumentException( "Cannot read from bundle file " + bundleFileName + " specified in the "
149 + BUNDLE_JAR_SYS_PROP + " system property" );
150 }
151
Felix Meschberger2e885422009-08-21 11:35:00 +0000152 final Option[] base = options(
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000153 provision(
Felix Meschberger55e3b972009-10-12 07:16:08 +0000154 CoreOptions.bundle( bundleFile.toURI().toString() ),
Felix Meschberger4f43c912012-03-08 04:37:56 +0000155 mavenBundle( "org.ops4j.pax.tinybundles", "tinybundles", "1.0.0" ),
David Jencks29ea6c02012-09-05 21:49:24 +0000156 mavenBundle( "org.apache.felix", "org.apache.felix.configadmin", "1.0.10" )
Felix Meschberger12d8c102009-12-18 12:47:44 +0000157 ),
Felix Meschberger4f43c912012-03-08 04:37:56 +0000158 junitBundles(),
David Jencks29ea6c02012-09-05 21:49:24 +0000159 systemProperty( "ds.factory.enabled" ).value( Boolean.toString( NONSTANDARD_COMPONENT_FACTORY_BEHAVIOR ) ),
David Jencksa5a62252012-10-12 04:26:02 +0000160 systemProperty( "ds.loglevel" ).value( DS_LOGLEVEL )
Felix Meschberger4f43c912012-03-08 04:37:56 +0000161
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000162 );
Felix Meschberger4f43c912012-03-08 04:37:56 +0000163 final Option vmOption = ( paxRunnerVmOption != null ) ? CoreOptions.vmOption( paxRunnerVmOption ) : null;
Felix Meschberger2e885422009-08-21 11:35:00 +0000164 return OptionUtils.combine( base, vmOption );
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000165 }
166
167
168 @Before
169 public void setUp() throws BundleException
170 {
David Jencks301226a2012-10-25 18:00:00 +0000171 log = new Log(restrictedLogging);
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000172 log.start();
173 bundleContext.addFrameworkListener( log );
174 bundleContext.registerService( LogService.class.getName(), log, null );
175
Felix Meschbergerf873dbe2011-02-04 10:50:18 +0000176 scrTracker = new ServiceTracker( bundleContext, "org.apache.felix.scr.ScrService", null );
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000177 scrTracker.open();
Felix Meschbergerf873dbe2011-02-04 10:50:18 +0000178 configAdminTracker = new ServiceTracker( bundleContext, "org.osgi.service.cm.ConfigurationAdmin", null );
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000179 configAdminTracker.open();
180
Felix Meschbergerf975aa52009-08-24 10:37:44 +0000181 bundle = installBundle( descriptorFile );
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000182 bundle.start();
183 }
184
185
186 @After
187 public void tearDown() throws BundleException
188 {
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000189 try
Felix Meschbergerf975aa52009-08-24 10:37:44 +0000190 {
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000191 if ( bundle != null && bundle.getState() != Bundle.UNINSTALLED )
192 {
193 bundle.uninstall();
194 bundle = null;
195 }
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000196
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000197 configAdminTracker.close();
198 configAdminTracker = null;
199 scrTracker.close();
200 scrTracker = null;
201 }
202 finally
203 {
204 log.stop();
205 }
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000206 }
207
208
Felix Meschbergered21c032010-01-27 09:58:17 +0000209 protected Component[] getComponents()
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000210 {
211 ScrService scr = ( ScrService ) scrTracker.getService();
212 if ( scr != null )
213 {
Felix Meschbergered21c032010-01-27 09:58:17 +0000214 return scr.getComponents();
215 }
216
217 return null;
218 }
219
220
221 protected Component findComponentByName( String name )
222 {
Felix Meschberger5fa8b0e2010-08-03 08:21:13 +0000223 Component[] components = findComponentsByName( name );
224 if ( components != null && components.length > 0 )
Felix Meschbergered21c032010-01-27 09:58:17 +0000225 {
Felix Meschberger5fa8b0e2010-08-03 08:21:13 +0000226 return components[0];
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000227 }
228
229 return null;
230 }
231
232
233 protected Component[] findComponentsByName( String name )
234 {
Felix Meschberger5fa8b0e2010-08-03 08:21:13 +0000235 ScrService scr = ( ScrService ) scrTracker.getService();
236 if ( scr != null )
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000237 {
Felix Meschberger5fa8b0e2010-08-03 08:21:13 +0000238 return scr.getComponents( name );
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000239 }
240
241 return null;
242 }
243
244
245 protected static void delay()
246 {
247 try
248 {
249 Thread.sleep( 300 );
250 }
251 catch ( InterruptedException ie )
252 {
253 // dont care
254 }
255 }
256
257
258 protected ConfigurationAdmin getConfigurationAdmin()
259 {
260 ConfigurationAdmin ca = ( ConfigurationAdmin ) configAdminTracker.getService();
261 if ( ca == null )
262 {
263 TestCase.fail( "Missing ConfigurationAdmin service" );
264 }
265 return ca;
266 }
267
268
269 protected void configure( String pid )
270 {
271 ConfigurationAdmin ca = getConfigurationAdmin();
272 try
273 {
274 org.osgi.service.cm.Configuration config = ca.getConfiguration( pid, null );
275 config.update( theConfig );
276 }
277 catch ( IOException ioe )
278 {
279 TestCase.fail( "Failed updating configuration " + pid + ": " + ioe.toString() );
280 }
281 }
282
283
284 protected void deleteConfig( String pid )
285 {
286 ConfigurationAdmin ca = getConfigurationAdmin();
287 try
288 {
289 org.osgi.service.cm.Configuration config = ca.getConfiguration( pid );
290 config.delete();
291 }
292 catch ( IOException ioe )
293 {
294 TestCase.fail( "Failed deleting configuration " + pid + ": " + ioe.toString() );
295 }
296 }
297
298
299 protected String createFactoryConfiguration( String factoryPid )
300 {
301 ConfigurationAdmin ca = getConfigurationAdmin();
302 try
303 {
304 org.osgi.service.cm.Configuration config = ca.createFactoryConfiguration( factoryPid, null );
305 config.update( theConfig );
306 return config.getPid();
307 }
308 catch ( IOException ioe )
309 {
310 TestCase.fail( "Failed updating factory configuration " + factoryPid + ": " + ioe.toString() );
311 return null;
312 }
313 }
314
315
316 protected void deleteFactoryConfigurations( String factoryPid )
317 {
318 ConfigurationAdmin ca = getConfigurationAdmin();
319 try
320 {
321 final String filter = "(service.factoryPid=" + factoryPid + ")";
322 org.osgi.service.cm.Configuration[] configs = ca.listConfigurations( filter );
323 if ( configs != null )
324 {
325 for ( org.osgi.service.cm.Configuration configuration : configs )
326 {
327 configuration.delete();
328 }
329 }
330 }
331 catch ( InvalidSyntaxException ise )
332 {
333 // unexpected
334 }
335 catch ( IOException ioe )
336 {
337 TestCase.fail( "Failed deleting configurations " + factoryPid + ": " + ioe.toString() );
338 }
339 }
Felix Meschberger2e885422009-08-21 11:35:00 +0000340
341
342 protected static Class<?> getType( Object object, String desiredName )
343 {
344 Class<?> ccImpl = object.getClass();
345 while ( ccImpl != null && !desiredName.equals( ccImpl.getSimpleName() ) )
346 {
347 ccImpl = ccImpl.getSuperclass();
348 }
349 if ( ccImpl == null )
350 {
351 TestCase.fail( "ComponentContext " + object + " is not a " + desiredName );
352 }
353
354 return ccImpl;
355 }
356
357
358 protected static Object getFieldValue( Object object, String fieldName )
359 {
360 try
361 {
362 final Field m_componentsField = getField( object.getClass(), fieldName );
363 return m_componentsField.get( object );
364 }
365 catch ( Throwable t )
366 {
367 TestCase.fail( "Cannot get " + fieldName + " from " + object + ": " + t );
368 return null; // keep the compiler happy
369 }
370 }
371
372
373 protected static Field getField( Class<?> type, String fieldName ) throws NoSuchFieldException
374 {
Pierre De Ropce7ca4d2012-06-15 16:24:54 +0000375 Class<?> clazz = type;
376 while (clazz != null)
377 {
378 Field[] fields = clazz.getDeclaredFields();
379 for (int i = 0; i < fields.length; i++)
380 {
381 Field field = fields[i];
382 if (field.getName().equals(fieldName))
383 {
384 field.setAccessible( true );
385 return field;
386 }
387 }
388 clazz = clazz.getSuperclass();
389 }
390 throw new NoSuchFieldException(fieldName);
Felix Meschberger2e885422009-08-21 11:35:00 +0000391 }
Felix Meschbergerf975aa52009-08-24 10:37:44 +0000392
393
394 protected Bundle installBundle( final String descriptorFile ) throws BundleException
395 {
Felix Meschberger4f43c912012-03-08 04:37:56 +0000396 final InputStream bundleStream = bundle()
397 .add("OSGI-INF/components.xml", getClass().getResource(descriptorFile))
398
399 .set(Constants.BUNDLE_SYMBOLICNAME, "simplecomponent")
400 .set(Constants.BUNDLE_VERSION, "0.0.11")
401 .set(Constants.IMPORT_PACKAGE,
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000402 "org.apache.felix.scr.integration.components," +
403 "org.apache.felix.scr.integration.components.activatesignature," +
404 "org.apache.felix.scr.integration.components.circular," +
405 "org.apache.felix.scr.integration.components.concurrency," +
Pierre De Rop636c9822012-10-21 09:43:13 +0000406 "org.apache.felix.scr.integration.components.felix3680," +
407 "org.apache.felix.scr.integration.components.felix3680_2")
Felix Meschberger4f43c912012-03-08 04:37:56 +0000408 .set("Service-Component", "OSGI-INF/components.xml")
409 .build(withBnd());
Felix Meschbergerf975aa52009-08-24 10:37:44 +0000410
411 try
412 {
413 final String location = "test:SimpleComponent/" + System.currentTimeMillis();
414 return bundleContext.installBundle( location, bundleStream );
415 }
416 finally
417 {
418 try
419 {
420 bundleStream.close();
421 }
422 catch ( IOException ioe )
423 {
424 }
425 }
426 }
427
David Jencks8584cf92012-06-16 03:56:11 +0000428 //Code copied from ScrCommand to make it easier to find out what your test components are actually doing.
429 // @Test
430 public void testDescription()
431 {
432 PrintStream out = System.out;
433 info( out );
434 }
435
436 void info( PrintStream out )
437 {
438 Component[] components = getComponents();
439 if ( components == null )
440 {
441 return;
442 }
443
444 for ( int j = 0; j < components.length; j++ )
445 {
446 Component component = components[j];
447 out.print( "ID: " );
448 out.println( component.getId() );
449 out.print( "Name: " );
450 out.println( component.getName() );
451 out.print( "Bundle: " );
452 out.println( component.getBundle().getSymbolicName() + " (" + component.getBundle().getBundleId() + ")" );
453 out.print( "State: " );
454 out.println( toStateString( component.getState() ) );
455 out.print( "Default State: " );
456 out.println( component.isDefaultEnabled() ? "enabled" : "disabled" );
457 out.print( "Activation: " );
458 out.println( component.isImmediate() ? "immediate" : "delayed" );
459
460 // DS 1.1 new features
461 out.print( "Configuration Policy: " );
462 out.println( component.getConfigurationPolicy() );
463 out.print( "Activate Method: " );
464 out.print( component.getActivate() );
465 if ( component.isActivateDeclared() )
466 {
467 out.print( " (declared in the descriptor)" );
468 }
469 out.println();
470 out.print( "Deactivate Method: " );
471 out.print( component.getDeactivate() );
472 if ( component.isDeactivateDeclared() )
473 {
474 out.print( " (declared in the descriptor)" );
475 }
476 out.println();
477 out.print( "Modified Method: " );
478 if ( component.getModified() != null )
479 {
480 out.print( component.getModified() );
481 }
482 else
483 {
484 out.print( "-" );
485 }
486 out.println();
487
488 if ( component.getFactory() != null )
489 {
490 out.print( "Factory: " );
491 out.println( component.getFactory() );
492 }
493
494 String[] services = component.getServices();
495 if ( services != null )
496 {
497 out.print( "Services: " );
498 out.println( services[0] );
499 for ( int i = 1; i < services.length; i++ )
500 {
501 out.print( " " );
502 out.println( services[i] );
503 }
504 out.print( "Service Type: " );
505 out.println( component.isServiceFactory() ? "service factory" : "service" );
506 }
507
508 Reference[] refs = component.getReferences();
509 if ( refs != null )
510 {
511 for ( int i = 0; i < refs.length; i++ )
512 {
513 out.print( "Reference: " );
514 out.println( refs[i].getName() );
515 out.print( " Satisfied: " );
516 out.println( refs[i].isSatisfied() ? "satisfied" : "unsatisfied" );
517 out.print( " Service Name: " );
518 out.println( refs[i].getServiceName() );
519 if ( refs[i].getTarget() != null )
520 {
521 out.print( " Target Filter: " );
522 out.println( refs[i].getTarget() );
523 }
524 out.print( " Multiple: " );
525 out.println( refs[i].isMultiple() ? "multiple" : "single" );
526 out.print( " Optional: " );
527 out.println( refs[i].isOptional() ? "optional" : "mandatory" );
528 out.print( " Policy: " );
529 out.println( refs[i].isStatic() ? "static" : "dynamic" );
530 out.print( " Policy option: " );
531 out.println( refs[i].isReluctant() ? "reluctant" : "greedy" );
532 }
533 }
534
535 Dictionary props = component.getProperties();
536 if ( props != null )
537 {
538 out.println( "Properties:" );
539 TreeSet keys = new TreeSet( Collections.list( props.keys() ) );
540 for ( Iterator ki = keys.iterator(); ki.hasNext(); )
541 {
542 Object key = ki.next();
543 out.print( " " );
544 out.print( key );
545 out.print( " = " );
546
547 Object prop = props.get( key );
548 if ( prop.getClass().isArray() )
549 {
550 prop = Arrays.asList( ( Object[] ) prop );
551 }
552 out.print( prop );
553
554 out.println();
555 }
556 }
557 }
558 }
559
560 private String toStateString( int state )
561 {
562 switch ( state )
563 {
564 case Component.STATE_DISABLED:
565 return "disabled";
566 case Component.STATE_UNSATISFIED:
567 return "unsatisfied";
568 case Component.STATE_ACTIVE:
569 return "active";
570 case Component.STATE_REGISTERED:
571 return "registered";
572 case Component.STATE_FACTORY:
573 return "factory";
574 case Component.STATE_DISPOSED:
575 return "disposed";
576
577 case Component.STATE_ENABLING:
578 return "enabling";
579 case Component.STATE_ENABLED:
580 return "enabled";
581 case Component.STATE_ACTIVATING:
582 return "activating";
583 case Component.STATE_DEACTIVATING:
584 return "deactivating";
585 case Component.STATE_DISABLING:
586 return "disabling";
587 case Component.STATE_DISPOSING:
588 return "disposing";
589 default:
590 return String.valueOf( state );
591 }
592 }
593
David Jencks29ea6c02012-09-05 21:49:24 +0000594 // Used to ignore logs displayed by the framework from stdout.
595 // (the log service will log it because it listen to fwk error
596 // events ...).
597 static class NullStdout extends PrintStream
598 {
599 NullStdout()
600 {
601 super(new OutputStream()
602 {
603 @Override
604 public void write(int b) throws IOException
605 {
606 }
607 });
608 }
609 }
610
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000611 public static class LogEntry
David Jencks29ea6c02012-09-05 21:49:24 +0000612 {
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000613 private final String m_msg;
614 private final int m_level;
615 private final Throwable m_err;
616 private final long m_time;
617 private final Thread m_thread;
David Jencks29ea6c02012-09-05 21:49:24 +0000618
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000619
620 LogEntry( int level, String msg, Throwable t )
David Jencks29ea6c02012-09-05 21:49:24 +0000621 {
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000622 m_level = level;
623 m_msg = msg;
624 m_err = t;
625 m_time = System.currentTimeMillis();
626 m_thread = Thread.currentThread();
David Jencks29ea6c02012-09-05 21:49:24 +0000627 }
628
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000629
630 public String toString()
631 {
632 return m_msg;
633 }
634
635
636 public int getLevel()
637 {
638 return m_level;
639 }
640
641
642 public String getMessage()
643 {
644 return m_msg;
645 }
646
647
648 public Throwable getError()
649 {
650 return m_err;
651 }
652
653
654 public long getTime()
655 {
656 return m_time;
657 }
658
659
660 public Thread getThread()
661 {
662 return m_thread;
663 }
664 }
665
666 public static class Log implements LogService, FrameworkListener, Runnable
667 {
David Jencks301226a2012-10-25 18:00:00 +0000668 private static final int RESTRICTED_LOG_SIZE = 1000;
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000669 private final SimpleDateFormat m_sdf = new SimpleDateFormat( "HH:mm:ss,S" );
670 private final static PrintStream m_out = new PrintStream( new BufferedOutputStream( new FileOutputStream(
671 FileDescriptor.err ), 128 ) );
David Jencks8cd70382013-02-10 07:43:12 +0000672 private final List<String> m_warnings = Collections.synchronizedList( new ArrayList<String>() );
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000673 private LinkedBlockingQueue<LogEntry> m_logQueue = new LinkedBlockingQueue<LogEntry>();
674 private volatile Thread m_logThread;
675 private volatile PrintStream m_realOut;
676 private volatile PrintStream m_realErr;
677
David Jencks301226a2012-10-25 18:00:00 +0000678 private final boolean restrictedLogging;
679 private final String[] log = new String[1000];
680 private int i = 0;
681
682 public Log( boolean restrictedLogging )
683 {
684 this.restrictedLogging = restrictedLogging;
685 }
686
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000687 public void start()
688 {
689 m_realOut = System.out;
690 m_realErr = System.err;
691 System.setOut( new NullStdout() );
692 System.setErr( new NullStdout() );
693 m_logThread = new Thread( this );
694 m_logThread.start();
695 }
696
697
698 public void stop()
699 {
700 System.setOut(m_realOut);
701 System.setErr(m_realErr);
David Jencks301226a2012-10-25 18:00:00 +0000702 if ( restrictedLogging )
703 {
704 for (int j = 0; j < RESTRICTED_LOG_SIZE; j++)
705 {
706 if ( log[i] != null )
707 {
708 m_realErr.println(log[i++]);
709 }
710 if (i == RESTRICTED_LOG_SIZE) i = 0;
711 }
712 }
713 else
714 {
715 m_out.flush();
716 }
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000717 m_warnings.clear();
718 m_logThread.interrupt();
719 try
720 {
721 m_logThread.join();
722 }
723 catch ( InterruptedException e )
724 {
725 }
726 }
727
728
729 List<String> foundWarnings()
David Jencks29ea6c02012-09-05 21:49:24 +0000730 {
731 return m_warnings;
732 }
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000733
734
735 public void run()
736 {
737 try
738 {
739 LogEntry entry = null;
740 while ( true )
741 {
742 entry = m_logQueue.take();
743 if ( entry.getLevel() <= 2 )
744 {
745 if ( m_warnings.size() < 1024 )
746 {
747 m_warnings.add( entry.getMessage() );
748 }
749 else
750 {
751 // Avoid out of memory ...
752 m_warnings.add( 1024, "Unexpected errors logged. Please look at previous logs" );
753 }
754 }
755
756 StringWriter sw = new StringWriter();
757 sw.append( "log level: " + entry.getLevel() );
758 sw.append( " D=" );
759 sw.append( m_sdf.format( new Date( entry.getTime() ) ) );
760 sw.append( " T=" + entry.getThread() );
761 sw.append( ": " );
762 sw.append( entry.getMessage() );
763 if ( entry.getError() != null )
764 {
765 sw.append( System.getProperty( "line.separator" ) );
766 PrintWriter pw = new PrintWriter( sw );
767 entry.getError().printStackTrace( pw );
768 }
David Jencks301226a2012-10-25 18:00:00 +0000769 if ( restrictedLogging )
770 {
771 log[i++] = sw.toString();
772 if ( i == RESTRICTED_LOG_SIZE ) i = 0;
773 }
774 else
775 {
776 m_out.println( sw.toString() );
777 m_out.flush();
778 }
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000779 }
780 }
781 catch ( InterruptedException e )
782 {
783 return;
784 }
785 }
786
787
788 // ------------- FrameworkListener -----------------------------------------------------------
789
790 public void frameworkEvent( final FrameworkEvent event )
791 {
792 int eventType = event.getType();
793 String msg = getFrameworkEventMessage( eventType );
794 int level = ( eventType == FrameworkEvent.ERROR ) ? LogService.LOG_ERROR : LogService.LOG_WARNING;
795 log( level, msg, event.getThrowable() );
796 }
797
798
799 // ------------ LogService ----------------------------------------------------------------
800
801 public void log( int level, String message )
802 {
803 log( level, message, null );
804 }
805
806
807 public void log( int level, String message, Throwable exception )
808 {
809 if ( level > getEnabledLogLevel() )
810 {
811 return;
812 }
813 m_logQueue.offer( new LogEntry( level, message, exception ) );
814 }
815
816
817 public void log( ServiceReference sr, int osgiLevel, String message )
818 {
819 log( sr, osgiLevel, message, null );
820 }
821
822
823 public void log( ServiceReference sr, int level, String msg, Throwable exception )
824 {
825 if ( sr != null )
826 {
827 StringBuilder sb = new StringBuilder();
828 Object serviceId = sr.getProperty( Constants.SERVICE_ID );
829 if ( serviceId != null )
830 {
831 sb.append( "[" + serviceId.toString() + "] " );
832 }
833 sb.append( msg );
834 log( level, sb.toString(), exception );
835 }
836 else
837 {
838 log( level, msg, exception );
839 }
840 }
841
842
843 private int getEnabledLogLevel()
844 {
Pierre De Rope69536e2012-10-13 11:54:00 +0000845 if ( DS_LOGLEVEL.regionMatches( true, 0, "err", 0, "err".length() ) )
846 {
847 return LogService.LOG_ERROR;
848 }
849 else if ( DS_LOGLEVEL.regionMatches( true, 0, "warn", 0, "warn".length() ) )
850 {
851 return LogService.LOG_WARNING;
852 }
853 else if ( DS_LOGLEVEL.regionMatches( true, 0, "info", 0, "info".length() ) )
854 {
855 return LogService.LOG_INFO;
856 }
857 else
858 {
859 return LogService.LOG_DEBUG;
860 }
861 }
Pierre De Ropf70d25f2012-10-17 11:29:09 +0000862
863
864 private String getFrameworkEventMessage( int event )
865 {
866 switch ( event )
867 {
868 case FrameworkEvent.ERROR:
869 return "FrameworkEvent: ERROR";
870 case FrameworkEvent.INFO:
871 return "FrameworkEvent INFO";
872 case FrameworkEvent.PACKAGES_REFRESHED:
873 return "FrameworkEvent: PACKAGE REFRESHED";
874 case FrameworkEvent.STARTED:
875 return "FrameworkEvent: STARTED";
876 case FrameworkEvent.STARTLEVEL_CHANGED:
877 return "FrameworkEvent: STARTLEVEL CHANGED";
878 case FrameworkEvent.WARNING:
879 return "FrameworkEvent: WARNING";
880 default:
881 return null;
882 }
883 }
David Jencks29ea6c02012-09-05 21:49:24 +0000884 }
Felix Meschbergera6ad9762009-08-19 13:38:56 +0000885}