/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.rioproject.test; import net.jini.core.lookup.ServiceItem; import net.jini.core.lookup.ServiceTemplate; import net.jini.lookup.ServiceDiscoveryManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeoutException; /** * The class represents a service monitor. Using this class, you * can specify the type of services you are interested in, and then: * <ul> * <li>Retrieve the currently available services. * <li>Determine the number of currently available services of the * specified type. * <li>Wait until the number of services of the specified type is * equal to a given number and stable. * </ul> */ public class ServiceMonitor<T> { public static long MAX_TIMEOUT = 180000; public static long STABILITY_TIMEOUT = 3000; private static Logger logger = LoggerFactory.getLogger(ServiceMonitor.class.getPackage().getName()); private ServiceDiscoveryManager sdm; private Class<T> type; private long maxTimeout = MAX_TIMEOUT; private long stabilityTimeout = STABILITY_TIMEOUT; /** * Constructs a <code>ServiceMonitor</code>. * * @param sdm the service discovery manager to use to discover services. * @param type The parameterized type */ public ServiceMonitor(ServiceDiscoveryManager sdm, Class<T> type) { this.sdm = sdm; this.type = type; } /** * Waits until the number of services is equal to a given number and * stable. * <p> * The method waits until the number of services is equal to a given * number. If the number of services has reached the required level, * the method, to ensure that the number of services is stable, waits * for the so-called "stability timeout" and checks that the number * of services does not change. If the number of services does not * change during the stability timeout, the method exits successfully. * Otherwise the method starts waiting from the beginning. * <p> * If the method waits for too long so that the so-called "maximum * timeout" elapses, a {@link TimeoutException} is thrown. * * @param serviceCount the number of service to wait for. * * @throws TimeoutException if the wait times out (i.e. the maximum * timeout has elapsed). */ public void waitFor(long serviceCount) throws TimeoutException { logger.info("Waiting for ["+serviceCount+"] services of type ["+type.getName()+"] ..."); // This method is implemented as a state machine invoked once // a second. An alternative is a fully event-driven state machine // controlled by simultaneous timers and lookup cache events // (which seems to be unreasonably complex for now) final long TIME_STEP = 1000; final int WAIT = 0; final int STABILITY_WAIT = 1; int state = WAIT; long maxTime = System.currentTimeMillis() + maxTimeout; long stabilityTime = 0; // State machine while (true) { long time = System.currentTimeMillis(); if (state == WAIT) { if (getAndLogCount(serviceCount) == serviceCount) { // -> STABILITY_WAIT state = STABILITY_WAIT; stabilityTime = time + stabilityTimeout; logger.info("Ensuring stable state ..."); } else if (time > maxTime) { // ERROR throw new TimeoutException(); } } if (state == STABILITY_WAIT) { if (getAndLogCount(serviceCount) != serviceCount) { // -> WAIT state = WAIT; logger.info("Waiting again ..."); } else if (time > stabilityTime) { // SUCCESS break; } else if (time > maxTime) { // ERROR throw new TimeoutException(); } } try { Thread.sleep(TIME_STEP); } catch (InterruptedException e) { // } } logger.info("Waiting for ["+serviceCount+"] services of type ["+type.getName()+"] - OK"); } /** * Determines the number of currently available services. * * @return the number of currently available services */ public int getCount() { return getServices().size(); } /** * Determines and logs the number of currently available services. * * @param expected The expected amount * * @return the number of currently available services */ private int getAndLogCount(long expected) { logger.info(type.getName()+" found: ["+getCount()+"], Expecting ["+expected+"]"); return getCount(); } /** * Retrieves the currently available services. * * @return the List of proxies */ @SuppressWarnings("unchecked") public List<T> getServices() { ServiceTemplate template = new ServiceTemplate(null, new Class[]{type}, null); ServiceItem[] items = sdm.lookup(template, Integer.MAX_VALUE, null); List<T> services = new ArrayList<T>(); for (ServiceItem item : items) { services.add((T)item.service); // TODO: prepare } return services; } }