001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.xbean.kernel.standard;
018
019 import java.util.Iterator;
020 import java.util.List;
021 import java.util.Set;
022 import java.util.Arrays;
023 import java.util.LinkedHashSet;
024 import java.util.Collections;
025
026 import java.util.concurrent.TimeUnit;
027 import java.util.concurrent.atomic.AtomicLong;
028 import java.util.concurrent.locks.ReentrantLock;
029 import org.apache.xbean.kernel.ForcedStopException;
030 import org.apache.xbean.kernel.IllegalServiceStateException;
031 import org.apache.xbean.kernel.Kernel;
032 import org.apache.xbean.kernel.KernelOperationInterruptedException;
033 import org.apache.xbean.kernel.KernelOperationTimoutException;
034 import org.apache.xbean.kernel.ServiceEvent;
035 import org.apache.xbean.kernel.ServiceFactory;
036 import org.apache.xbean.kernel.ServiceMonitor;
037 import org.apache.xbean.kernel.ServiceName;
038 import org.apache.xbean.kernel.ServiceNotFoundException;
039 import org.apache.xbean.kernel.ServiceState;
040 import org.apache.xbean.kernel.StartStrategies;
041 import org.apache.xbean.kernel.StartStrategy;
042 import org.apache.xbean.kernel.StopStrategy;
043 import org.apache.xbean.kernel.UnregisterServiceException;
044 import org.apache.xbean.kernel.UnsatisfiedConditionsException;
045 import org.apache.xbean.kernel.InvalidServiceTypeException;
046
047 /**
048 * The ServiceManager handles the life cycle of a single service. The manager is responsible for gaurenteeing that
049 * all start conditions have been satisfied before the service is constructed, and that all stop conditions have been
050 * satisfied before the service is destroyed. The ServiceManager can be started and stopped several times, but once
051 * destroyed no methods may be called.
052 *
053 * @author Dain Sundstrom
054 * @version $Id$
055 * @since 2.0
056 */
057 public class ServiceManager implements Comparable {
058 /**
059 * The kernel in which this service is registered.
060 */
061 private final Kernel kernel;
062
063 /**
064 * The unique id of this service in the kernel.
065 */
066 private final long serviceId;
067
068 /**
069 * The unique name of this service in the kernel.
070 */
071 private final ServiceName serviceName;
072
073 /**
074 * The factory used to create and destroy the service instance.
075 */
076 private final ServiceFactory serviceFactory;
077
078 /**
079 * The type of service this service manager will create. This value is cached from the serviceFactory.getT
080 */
081 private final Set serviceTypes;
082
083 /**
084 * The monitor to which we fire service events. The ServiceManager requires an asynchronous monitor becuse events are
085 * fired from within the lock. This helps to reduce complexity but will cause more services to sit in the
086 * {@link ServiceState#STARTING} and {@link ServiceState#STOPPING} states since events are propagated in a separate
087 * thread.
088 */
089 private final ServiceMonitor serviceMonitor;
090
091 /**
092 * The service context given to the service factory. This contans a reference to the kernel, serviceName and
093 * classloader.
094 */
095 private final StandardServiceContext standardServiceContext;
096
097 /**
098 * Current state of this service.
099 */
100 private volatile ServiceState state = ServiceState.STOPPED;
101
102 /**
103 * The time the service was started or 0 if not started.
104 */
105 private volatile long startTime;
106
107 /**
108 * The {@link ServiceCondition) objects required to be ready before this service can be completely started.
109 */
110 private AggregateCondition startCondition;
111
112 /**
113 * The {@link ServiceCondition) objects required to be ready before this service can be completely stopped.
114 */
115 private AggregateCondition stopCondition;
116
117 /**
118 * The service instance.
119 */
120 private volatile Object service;
121
122 /**
123 * The single lock we use.
124 */
125 private final ReentrantLock lock = new ReentrantLock();
126
127 /**
128 * The maximum duration to wait for the lock.
129 */
130 private final long timeoutDuration;
131
132 /**
133 * The unit of measure for the {@link #timeoutDuration}.
134 */
135 private final TimeUnit timeoutUnits;
136
137 /**
138 * The name of the operation for which the lock is held; this is used in the reentrant exception message.
139 */
140 private String currentLockHolderOperation = "NOT-HELD";
141
142 /**
143 * Sequence number for service event objects.
144 */
145 private final AtomicLong eventId = new AtomicLong(0);
146
147 /**
148 * If true, when start is successful we will startRecusrive all of the services owned by this service.
149 */
150 private boolean recursive = false;
151
152 /**
153 * Creates a service manager for a single service.
154 *
155 * @param kernel the kernel in which this wraper will be registered
156 * @param serviceId the unique id of this service in the kernel
157 * @param serviceName the unique name of this service in the kernel
158 * @param serviceFactory the factory used to create and destroy the service instance
159 * @param serviceMonitor the monitor of service events
160 * @param timeoutDuration the maximum duration to wait for a lock
161 * @param timeoutUnits the unit of measure for the timeoutDuration
162 */
163 public ServiceManager(Kernel kernel,
164 long serviceId,
165 ServiceName serviceName,
166 ServiceFactory serviceFactory,
167 ServiceMonitor serviceMonitor,
168 long timeoutDuration,
169 TimeUnit timeoutUnits) {
170
171 this.kernel = kernel;
172 this.serviceId = serviceId;
173 this.serviceName = serviceName;
174 this.serviceFactory = serviceFactory;
175 this.serviceMonitor = serviceMonitor;
176 this.timeoutDuration = timeoutDuration;
177 this.timeoutUnits = timeoutUnits;
178 standardServiceContext = new StandardServiceContext(kernel, serviceName, serviceFactory.getClassLoader());
179 serviceTypes = Collections.unmodifiableSet(new LinkedHashSet(Arrays.asList(serviceFactory.getTypes())));
180 }
181
182 /**
183 * Initializes the service.
184 *
185 * @throws IllegalServiceStateException if the service is not restartable and is disabled
186 * @throws UnsatisfiedConditionsException if the service is not restartable and there were unsatisfied start conditions
187 * @throws Exception if the service is not restartable and service construction threw an exception
188 * @see Kernel#registerService(ServiceName, ServiceFactory, ClassLoader)
189 */
190 public void initialize() throws IllegalServiceStateException, UnsatisfiedConditionsException, Exception {
191 if (!serviceFactory.isRestartable() && !serviceFactory.isEnabled()) {
192 throw new IllegalServiceStateException("A disabled non-restartable service factory can not be initalized", serviceName);
193 }
194
195 serviceMonitor.serviceRegistered(createServiceEvent());
196
197 // if we are not restartable, we need to start immediately, otherwise we are not going to register this service
198 if (!serviceFactory.isRestartable()) {
199 try {
200 start(false, StartStrategies.UNREGISTER);
201 } catch (UnregisterServiceException e) {
202 serviceMonitor.serviceUnregistered(createServiceEvent());
203 Throwable cause = e.getCause();
204 if (cause instanceof Exception) {
205 throw (Exception) cause;
206 } else if (cause instanceof Error) {
207 throw (Error) cause;
208 } else {
209 throw new AssertionError(cause);
210 }
211 }
212
213 // a non restartable service uses a special stop conditions object that picks up stop conditions as they
214 // are added. When the stop() method is called on a non-restartable service all of the stop conditions
215 // registered with the service factory are initialized (if not already initialized), and the isSatisfied
216 // method is called. This should cause the stop logic of a stop condition to fire.
217 lock("initialize");
218 try {
219 stopCondition = new NonRestartableStopCondition(kernel, serviceName, serviceFactory.getClassLoader(), lock, serviceFactory);
220 } finally {
221 unlock();
222 }
223 }
224 }
225
226 /**
227 * Attempts to stop and destroy the service.
228 *
229 * @param stopStrategy the strategy used to determine how to handle unsatisfied stop conditions
230 * @throws IllegalServiceStateException is the service did not stop
231 * @throws UnsatisfiedConditionsException if there were unsatisfied stop conditions
232 * @see Kernel#unregisterService(ServiceName, StopStrategy)
233 */
234 public void destroy(StopStrategy stopStrategy) throws IllegalServiceStateException, UnsatisfiedConditionsException {
235 // if we are not restartable, we need to stop
236 try {
237 if (!stop(stopStrategy)) {
238 throw new IllegalServiceStateException("Service did not stop", serviceName);
239 }
240 } catch (UnsatisfiedConditionsException e) {
241 throw e;
242 }
243
244 if (!serviceFactory.isRestartable()) {
245 lock("destroy");
246 try {
247 if (state != ServiceState.STOPPED) {
248 state = ServiceState.STARTING;
249 serviceMonitor.serviceStopping(createServiceEvent());
250 if (service != null) {
251 try {
252 // destroy the service
253 serviceFactory.destroyService(standardServiceContext);
254 } catch (Throwable e) {
255 serviceMonitor.serviceStopError(createErrorServiceEvent(e));
256 }
257 }
258
259 destroyAllConditions(serviceMonitor);
260
261 service = null;
262 startTime = 0;
263 state = ServiceState.STOPPED;
264 serviceMonitor.serviceStopped(createServiceEvent());
265 }
266 } finally {
267 unlock();
268 }
269 }
270
271 // cool we can unregistered
272 serviceMonitor.serviceUnregistered(createServiceEvent());
273 }
274
275 /**
276 * Gets the unique id of this service in the kernel.
277 *
278 * @return the unique id of this service in the kernel
279 */
280 public long getServiceId() {
281 return serviceId;
282 }
283
284 /**
285 * Gets the unique name of this service in the kernel.
286 *
287 * @return the unique name of this servce in the kernel
288 */
289 public ServiceName getServiceName() {
290 return serviceName;
291 }
292
293 /**
294 * Gets the types of the service that will be managed by this service manager.
295 * @return the types of the service
296 */
297 public Set getServiceTypes() {
298 return serviceTypes;
299 }
300
301 /**
302 * Gets the factory used to create and destroy the service instance.
303 *
304 * @return the factory for the service instance
305 * @see Kernel#getServiceFactory(ServiceName)
306 */
307 public ServiceFactory getServiceFactory() {
308 return serviceFactory;
309 }
310
311 /**
312 * Gets the class loader for this service. This class loader is provided to the service factory in the
313 * ServiceContext object.
314 *
315 * @return the classloader for this service
316 * @see Kernel#getClassLoaderFor(ServiceName)
317 */
318 public ClassLoader getClassLoader() {
319 return serviceFactory.getClassLoader();
320 }
321
322 /**
323 * Gets the service instance.
324 *
325 * @return the service instance
326 * @see Kernel#getService(ServiceName)
327 */
328 public Object getService() {
329 return service;
330 }
331
332 /**
333 * Gets the current state of this service.
334 *
335 * @return the current state of this service
336 * @see Kernel#getServiceState(ServiceName)
337 */
338 public ServiceState getState() {
339 return state;
340 }
341
342 /**
343 * Gets the time at which this service entered the STARTING state or 0 if the service is STOPPED.
344 *
345 * @return the start time or 0 if the service is stopped
346 * @see Kernel#getServiceStartTime(ServiceName)
347 */
348 public long getStartTime() {
349 return startTime;
350 }
351
352 /**
353 * Attempts to starts the service.
354 *
355 * @param recursive if start is successful should we start recursive the services owned by this servic
356 * @param startStrategy the strategy used to determine how to handle unsatisfied start conditions and start errors
357 * @throws IllegalServiceStateException if the service is in a state in which it can not be started
358 * @throws UnregisterServiceException if the kernel should unregister this service
359 * @throws UnsatisfiedConditionsException if there were unsatisfied start conditions
360 * @throws Exception it service creation threw an exception
361 * @see Kernel#startService(ServiceName)
362 * @see Kernel#startServiceRecursive(ServiceName)
363 */
364 public void start(boolean recursive, StartStrategy startStrategy) throws IllegalServiceStateException, UnregisterServiceException, UnsatisfiedConditionsException, Exception {
365 // verify that it is possible to start this service in the current state before obtaining the lock
366 if (!verifyStartable(state)) {
367 if (recursive) {
368 startOwnedServices(startStrategy);
369 }
370 return;
371 }
372
373 boolean shouldStartRecursive = false;
374 lock("start");
375 try {
376 // update the recursive flag
377 this.recursive = this.recursive || recursive;
378
379 Throwable startError = null;
380 try {
381 //
382 // Loop until all start conditions have been satified. The start strategy can break this loop.
383 //
384 boolean satisfied = false;
385 while (!satisfied) {
386 // do we still want to start?
387 if (!verifyStartable(state)) {
388 // assume someone else called startOwnedServices
389 return;
390 }
391
392 // if we are in the STOPPED state, we need to move to the STARTING state
393 if (state == ServiceState.STOPPED) {
394 // we are now officially starting
395 state = ServiceState.STARTING;
396 serviceMonitor.serviceStarting(createServiceEvent());
397
398 // initialize the start conditions
399 startCondition = new AggregateCondition(kernel, serviceName, serviceFactory.getClassLoader(), lock, serviceFactory.getStartConditions());
400 startCondition.initialize();
401 }
402
403 // are we satisfied?
404 Set unsatisfiedConditions = startCondition.getUnsatisfied();
405 satisfied = unsatisfiedConditions.isEmpty();
406 if (!satisfied) {
407 // if the stragegy wants us to wait for conditions to be satisfied, it will return true
408 if (startStrategy.waitForUnsatisfiedConditions(serviceName, unsatisfiedConditions)) {
409 // wait for satisfaction and loop
410 startCondition.awaitSatisfaction();
411 } else {
412 // no wait, notify the monitor and exit
413 serviceMonitor.serviceWaitingToStart(createWaitingServiceEvent(unsatisfiedConditions));
414 return;
415 }
416 }
417 }
418
419 // we are ready to create the service
420 service = serviceFactory.createService(standardServiceContext);
421
422 // verify that the service implements all of the types
423 if (service == null) {
424 throw new NullPointerException("Service factory return null from createService for service " + serviceName);
425 }
426 for (Iterator iterator = serviceTypes.iterator(); iterator.hasNext();) {
427 Class type = (Class) iterator.next();
428 if (!type.isInstance(service)) {
429 throw new InvalidServiceTypeException(serviceName, type, service.getClass());
430 }
431 }
432
433 // success transition to running
434 startTime = System.currentTimeMillis();
435 state = ServiceState.RUNNING;
436 serviceMonitor.serviceRunning(createServiceEvent());
437
438 // should we recursively start our children
439 shouldStartRecursive = this.recursive || recursive;
440 this.recursive = false;
441 } catch (UnsatisfiedConditionsException e) {
442 // thrown from waitForUnsatisfiedConditions
443 throw e;
444 } catch (IllegalServiceStateException e) {
445 // this can be thrown while awaiting satisfaction
446 throw e;
447 } catch (Exception e) {
448 startError = e;
449 } catch (Error e) {
450 startError = e;
451 }
452
453 if (startError != null) {
454 try {
455 if (startError instanceof UnregisterServiceException) {
456 throw (UnregisterServiceException) startError;
457 } else {
458 // the strategy will normally rethrow the startError, but if it doesn't notify the service monitor
459 startStrategy.startError(serviceName, startError);
460 serviceMonitor.serviceStartError(createErrorServiceEvent(startError));
461 }
462 } finally {
463 // we are now STOPPING
464 state = ServiceState.STOPPING;
465 serviceMonitor.serviceStopping(createServiceEvent());
466
467 // clean up the conditons
468 destroyAllConditions(serviceMonitor);
469
470 // transition to the STOPPED state
471 service = null;
472 startTime = 0;
473 state = ServiceState.STOPPED;
474 serviceMonitor.serviceStopped(createServiceEvent());
475 }
476 }
477 } finally {
478 unlock();
479 }
480
481
482 // startRecursive all of the owned services
483 if (shouldStartRecursive) {
484 startOwnedServices(startStrategy);
485 }
486 }
487
488 private void startOwnedServices(StartStrategy startStrategy) throws IllegalServiceStateException, UnsatisfiedConditionsException, Exception {
489 Set ownedServices = serviceFactory.getOwnedServices();
490 if (ownedServices == null) throw new NullPointerException("serviceFactory.getOwnedServices() returned null");
491 for (Iterator iterator = ownedServices.iterator(); iterator.hasNext();) {
492 ServiceName ownedService = (ServiceName) iterator.next();
493 try {
494 kernel.startServiceRecursive(ownedService, startStrategy);
495 } catch (ServiceNotFoundException ignored) {
496 // this is ok -- service unregistered
497 } catch (IllegalServiceStateException ignored) {
498 // ownedService is disabled or stopping -- anyway we don't care
499 }
500 }
501 }
502
503 /**
504 * Verifies that the service is startable. This can be used out side a lock to avoid unecessary locking.
505 *
506 * @param state the state of the service
507 * @return true if it is possible to start a service in the specifiec state
508 * @throws IllegalServiceStateException if it is illegal to start a service in the specified state
509 */
510 private boolean verifyStartable(ServiceState state) throws IllegalServiceStateException {
511 // if we are alredy in the running state, there is nothing to do
512 if (state == ServiceState.RUNNING) {
513 return false;
514 }
515
516 // if we are in the stopping states, that is an error
517 if (state == ServiceState.STOPPING) {
518 throw new IllegalServiceStateException("A stopping service can not be started", serviceName);
519 }
520
521 // is this service enabled?
522 if (state == ServiceState.STOPPED && !serviceFactory.isEnabled()) {
523 throw new IllegalServiceStateException("Service is disabled", serviceName);
524 }
525
526 return true;
527 }
528
529 /**
530 * Attempts to stop the service.
531 *
532 * @param stopStrategy the strategy used to determine how to handle unsatisfied stop conditions
533 * @return true if the service was sucessfully stopped; false otherwise
534 * @throws UnsatisfiedConditionsException if there were unsatisfied stop conditions
535 * @see Kernel#stopService(ServiceName)
536 */
537 public boolean stop(StopStrategy stopStrategy) throws UnsatisfiedConditionsException {
538 // check that we aren't already stopped before attempting to acquire the lock
539 ServiceState initialState = state;
540 if (initialState == ServiceState.STOPPED) {
541 return true;
542 }
543
544 lock("stop");
545 try {
546 try {
547 //
548 // Loop until all stop conditions have been satified. The stop strategy can break this loop.
549 //
550 boolean satisfied = false;
551 while (!satisfied) {
552 // do we still want to stop?
553 if (state == ServiceState.STOPPED) {
554 return true;
555 }
556
557 // if we are not the STOPPING state, transition to it
558 // we check on the stopConditions variable because non-restartable services preset this in the
559 // intialization method
560 if (stopCondition == null) {
561 // we are not officially stopping
562 serviceMonitor.serviceStopping(createServiceEvent());
563 state = ServiceState.STOPPING;
564
565 // initialize all of the stop conditions
566 stopCondition = new AggregateCondition(kernel, serviceName, serviceFactory.getClassLoader(), lock, serviceFactory.getStopConditions());
567 stopCondition.initialize();
568 }
569
570 // are we satisfied?
571 Set unsatisfiedConditions = stopCondition.getUnsatisfied();
572 satisfied = unsatisfiedConditions.isEmpty();
573 if (!satisfied) {
574 // if the stragegy wants us to wait for conditions to be satisfied, it will return true
575 if (stopStrategy.waitForUnsatisfiedConditions(serviceName, unsatisfiedConditions)) {
576 // wait for satisfaction and loop
577 stopCondition.awaitSatisfaction();
578 } else {
579 // no wait, notify the monitor and exit
580 serviceMonitor.serviceWaitingToStop(createWaitingServiceEvent(unsatisfiedConditions));
581 return false;
582 }
583 }
584 }
585 } catch (UnsatisfiedConditionsException e) {
586 throw e;
587 } catch (ForcedStopException e) {
588 serviceMonitor.serviceStopError(createErrorServiceEvent(e));
589 } catch (Exception e) {
590 serviceMonitor.serviceStopError(createErrorServiceEvent(e));
591 } catch (Error e) {
592 serviceMonitor.serviceStopError(createErrorServiceEvent(e));
593 }
594
595 if (serviceFactory.isRestartable()) {
596 if (service != null) {
597 try {
598 // destroy the service
599 serviceFactory.destroyService(standardServiceContext);
600 } catch (Throwable e) {
601 serviceMonitor.serviceStopError(createErrorServiceEvent(e));
602 }
603 }
604
605 destroyAllConditions(serviceMonitor);
606
607 service = null;
608 startTime = 0;
609 state = ServiceState.STOPPED;
610 serviceMonitor.serviceStopped(createServiceEvent());
611 }
612 return true;
613 } finally {
614 unlock();
615 }
616 }
617
618 private void destroyAllConditions(ServiceMonitor monitor) {
619 if (!lock.isHeldByCurrentThread()) {
620 throw new IllegalStateException("Current thread must hold lock before calling destroyAllConditions");
621 }
622
623 if (startCondition != null) {
624 List errors = startCondition.destroy();
625 // errors from destroying the start conditions are stop errors because destroy is only called while
626 // stopping the service
627 for (Iterator iterator = errors.iterator(); iterator.hasNext();) {
628 Throwable stopError = (Throwable) iterator.next();
629 monitor.serviceStopError(createErrorServiceEvent(stopError));
630 }
631 startCondition = null;
632 }
633 if (stopCondition != null) {
634 List errors = stopCondition.destroy();
635 for (Iterator iterator = errors.iterator(); iterator.hasNext();) {
636 Throwable stopError = (Throwable) iterator.next();
637 monitor.serviceStopError(createErrorServiceEvent(stopError));
638 }
639 stopCondition = null;
640 }
641 }
642
643 /**
644 * Obtain the lock for the specified operation.
645 *
646 * @param operationName name of the operation that lock will be used for - this is only used for exception messages
647 * @throws IllegalStateException if thread tries to reenter while holding the lock
648 * @throws KernelOperationTimoutException if lock could not be obtained in {@link #timeoutDuration} {@link #timeoutUnits}
649 * @throws KernelOperationInterruptedException if the thread was interrupted while waiting for the lock
650 */
651 private void lock(String operationName) throws IllegalStateException, KernelOperationTimoutException, KernelOperationInterruptedException {
652 if (lock.isHeldByCurrentThread()) {
653 throw new IllegalStateException("Current thread holds lock for " + currentLockHolderOperation +
654 " and lock can not be reacquired for " + operationName + " on " + serviceName);
655 }
656
657 try {
658 if (!lock.tryLock(timeoutDuration, timeoutUnits)) {
659 throw new KernelOperationTimoutException("Could not obtain lock for " + operationName + " operation on " +
660 serviceName + " within " + timeoutDuration + " " + timeoutUnits.toString().toLowerCase(),
661 serviceName,
662 operationName);
663 }
664 currentLockHolderOperation = operationName;
665 } catch (InterruptedException e) {
666 throw new KernelOperationInterruptedException("Interrupted while attempting to obtain lock for " + operationName +
667 " operation on " + serviceName,
668 e,
669 serviceName,
670 operationName);
671
672 }
673 }
674
675 /**
676 * Unlock the lock and clear the currentLockHolderOperation name.
677 */
678 private void unlock() {
679 if (!lock.isHeldByCurrentThread()) {
680 throw new IllegalMonitorStateException("Not owner");
681 }
682
683 currentLockHolderOperation = "NOT-HELD";
684 lock.unlock();
685 }
686
687 private ServiceEvent createServiceEvent() {
688 return new ServiceEvent(eventId.getAndIncrement(), kernel, serviceName, serviceFactory, serviceFactory.getClassLoader(), service, null, null);
689 }
690
691 private ServiceEvent createWaitingServiceEvent(Set unsatisfiedConditions) {
692 return new ServiceEvent(eventId.getAndIncrement(), kernel, serviceName, serviceFactory, serviceFactory.getClassLoader(), service, null, unsatisfiedConditions);
693 }
694
695 private ServiceEvent createErrorServiceEvent(Throwable cause) {
696 return new ServiceEvent(eventId.getAndIncrement(), kernel, serviceName, serviceFactory, serviceFactory.getClassLoader(), null, cause, null);
697 }
698
699 public int hashCode() {
700 return (int) (serviceId ^ (serviceId >>> 32));
701 }
702
703 public boolean equals(Object o) {
704 if (o instanceof ServiceManager) {
705 return serviceId == ((ServiceManager)o).serviceId;
706 }
707 return false;
708 }
709
710 public int compareTo(Object o) {
711 ServiceManager serviceManager = (ServiceManager) o;
712
713 if (serviceId < serviceManager.serviceId) {
714 return -1;
715 } else if (serviceId > serviceManager.serviceId) {
716 return 1;
717 } else {
718 return 0;
719 }
720 }
721
722 public String toString() {
723 return "[ServiceManager: serviceId=" + serviceId + ", serviceName=" + serviceName + ", state=" + state + "]";
724 }
725 }