public static void main(String[] args) throws Exception {
    final int totalServiceDefinitions = Integer.parseInt(args[0]);
    final int threadPoolSize = Integer.parseInt(args[1]);

    final ServiceContainer container = ServiceContainer.Factory.create();
    final ThreadPoolExecutor executor =
        new ThreadPoolExecutor(
            threadPoolSize,
            threadPoolSize,
            1000,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>());
    container.setExecutor(executor);

    BatchBuilder batch = container.batchBuilder();

    final LatchedFinishListener listener = new LatchedFinishListener();

    final Value<Field> testFieldValue =
        new CachedValue<Field>(
            new LookupFieldValue(new ImmediateValue<Class<?>>(TestObject.class), "test"));

    final List<Value<Class<?>>> params =
        Collections.singletonList((Value<Class<?>>) new ImmediateValue<Class<?>>(TestObject.class));
    final List<Value<Method>> setterMethodValues = new ArrayList<Value<Method>>(5);
    for (int i = 0; i < 5; i++)
      setterMethodValues.add(
          new CachedValue<Method>(
              new LookupMethodValue(
                  new ImmediateValue<Class<?>>(TestObject.class), "setOther" + (i), params)));

    for (int i = 0; i < totalServiceDefinitions; i++) {
      final TestObject testObject = new TestObject("test" + i);
      final TestObjectService service = new TestObjectService(testObject);
      final ServiceBuilder<TestObject> builder =
          batch.addService(ServiceName.of(("test" + i).intern()), service).addListener(listener);

      final Object injectedValue = new Object();
      //            builder.addInjection(injectedValue).toFieldValue(testFieldValue);

      int nextDivByFive = (5 - (i % 5)) + i;
      int numDeps = Math.min(nextDivByFive - i, totalServiceDefinitions - i - 1);
      for (int j = 0; j < numDeps; j++) {
        int depId = i + j + 1;
        if (depId % 5 == 0) continue;

        //                builder.addDependency(ServiceName.of(("test" +
        // depId).intern())).toMethodValue(setterMethodValues.get(j),
        // Collections.singletonList(Values.injectedValue()));
      }
    }

    batch.install();
    listener.await();
    System.out.println(totalServiceDefinitions + " : " + listener.getElapsedTime() / 1000.0);
    container.shutdown();
    executor.shutdown();
  }
  @Override
  @SuppressWarnings({"rawtypes", "unchecked"})
  public ServiceState registerService(
      AbstractBundle bundleState, String[] clazzes, Object serviceValue, Dictionary properties) {
    if (clazzes == null || clazzes.length == 0)
      throw new IllegalArgumentException("Null service classes");

    // Immediately after registration of a {@link ListenerHook}, the ListenerHook.added() method
    // will be called
    // to provide the current collection of service listeners which had been added prior to the hook
    // being registered.
    Collection<ListenerInfo> listenerInfos = null;
    if (serviceValue instanceof ListenerHook)
      listenerInfos = eventsPlugin.getServiceListenerInfos(null);

    // A temporary association of the clazz and name
    Map<ServiceName, String> associations = new HashMap<ServiceName, String>();

    // Generate the service names
    long serviceId = getNextServiceId();
    ServiceName[] serviceNames = new ServiceName[clazzes.length];
    for (int i = 0; i < clazzes.length; i++) {
      if (clazzes[i] == null)
        throw new IllegalArgumentException("Null service class at index: " + i);

      String shortName = clazzes[i].substring(clazzes[i].lastIndexOf(".") + 1);
      serviceNames[i] =
          ServiceName.of(
              "jbosgi", bundleState.getSymbolicName(), shortName, new Long(serviceId).toString());
    }

    final ServiceState serviceState =
        new ServiceState(bundleState, serviceId, serviceNames, clazzes, serviceValue, properties);
    BatchBuilder batchBuilder = serviceContainer.batchBuilder();
    Service service =
        new Service() {
          @Override
          public Object getValue() throws IllegalStateException {
            // [TODO] for injection to work this needs to be the Object value
            return serviceState;
          }

          @Override
          public void start(StartContext context) throws StartException {}

          @Override
          public void stop(StopContext context) {}
        };

    log.debug("Register service: " + Arrays.asList(serviceNames));

    ServiceName rootServiceName = serviceNames[0];
    BatchServiceBuilder serviceBuilder = batchBuilder.addService(rootServiceName, service);
    associations.put(rootServiceName, clazzes[0]);

    // Set the startup mode
    serviceBuilder.setInitialMode(Mode.AUTOMATIC);

    // Add the service aliases
    for (int i = 1; i < serviceNames.length; i++) {
      ServiceName alias = serviceNames[i];
      associations.put(alias, clazzes[1]);
      serviceBuilder.addAliases(alias);
    }

    try {
      batchBuilder.install();

      // Register the name association. We do this here
      // in case anything went wrong during the install
      for (Entry<ServiceName, String> aux : associations.entrySet()) {
        bundleState.addRegisteredService(serviceState);
        registerNameAssociation(aux.getValue(), aux.getKey());
      }
    } catch (ServiceRegistryException ex) {
      log.error("Cannot register services: " + serviceNames, ex);
    }

    // Call the newly added ListenerHook.added() method
    if (serviceValue instanceof ListenerHook) {
      ListenerHook listenerHook = (ListenerHook) serviceValue;
      listenerHook.added(listenerInfos);
    }

    // This event is synchronously delivered after the service has been registered with the
    // Framework.
    eventsPlugin.fireServiceEvent(bundleState, ServiceEvent.REGISTERED, serviceState);

    return serviceState;
  }