Copyright (c) 2003, 2017 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2003, 2017 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.osgi.internal.serviceregistry; import java.util.*; import org.eclipse.osgi.internal.debug.Debug; import org.eclipse.osgi.internal.framework.BundleContextImpl; import org.eclipse.osgi.internal.loader.sources.PackageSource; import org.eclipse.osgi.internal.messages.Msg; import org.osgi.framework.*;
A registered service. The framework returns a ServiceRegistration object when a BundleContext.registerService method is successful. This object is for the private use of the registering bundle and should not be shared with other bundles.

The ServiceRegistration object may be used to update the properties for the service or to unregister the service.

If the ServiceRegistration is garbage collected the framework may remove the service. This implies that if a bundle wants to keep its service registered, it should keep the ServiceRegistration object referenced.

@ThreadSafe
/** * A registered service. * * The framework returns a ServiceRegistration object when a * {@link BundleContextImpl#registerService(String, Object, Dictionary) BundleContext.registerService} * method is successful. This object is for the private use of * the registering bundle and should not be shared with other bundles. * <p>The ServiceRegistration object may be used to update the properties * for the service or to unregister the service. * * <p>If the ServiceRegistration is garbage collected the framework may remove * the service. This implies that if a * bundle wants to keep its service registered, it should keep the * ServiceRegistration object referenced. * * @ThreadSafe */
public class ServiceRegistrationImpl<S> implements ServiceRegistration<S>, Comparable<ServiceRegistrationImpl<?>> { private final ServiceRegistry registry;
context which registered this service.
/** context which registered this service. */
private final BundleContextImpl context;
bundle which registered this service.
/** bundle which registered this service. */
private final Bundle bundle;
service classes for this registration.
/** service classes for this registration. */
private final String[] clazzes;
service object for this registration.
/** service object for this registration. */
private final S service;
Reference to this registration.
/** Reference to this registration. */
/* @GuardedBy("registrationLock") */ private ServiceReferenceImpl<S> reference;
List of contexts using the service. List<BundleContextImpl>.
/** List of contexts using the service. * List&lt;BundleContextImpl&gt;. * */
/* @GuardedBy("registrationLock") */ private final List<BundleContextImpl> contextsUsing;
properties for this registration.
/** properties for this registration. */
/* @GuardedBy("registrationLock") */ private Map<String, Object> properties;
service id.
/** service id. */
private final long serviceid;
service ranking.
/** service ranking. */
/* @GuardedBy("registrationLock") */ private int serviceranking; /* internal object to use for synchronization */ private final Object registrationLock = new Object();
The registration state
/** The registration state */
/* @GuardedBy("registrationLock") */ private int state; private static final int REGISTERED = 0x00; private static final int UNREGISTERING = 0x01; private static final int UNREGISTERED = 0x02;
Construct a ServiceRegistration and register the service in the framework's service registry.
/** * Construct a ServiceRegistration and register the service * in the framework's service registry. * */
ServiceRegistrationImpl(ServiceRegistry registry, BundleContextImpl context, String[] clazzes, S service) { this.registry = registry; this.context = context; this.bundle = context.getBundleImpl(); this.clazzes = clazzes; /* must be set before calling createProperties. */ this.service = service; /* must be set before calling createProperties. */ this.serviceid = registry.getNextServiceId(); /* must be set before calling createProperties. */ this.contextsUsing = new ArrayList<>(10); synchronized (registrationLock) { this.state = REGISTERED; /* We leak this from the constructor here, but it is ok * because the ServiceReferenceImpl constructor only * stores the value in a final field without * otherwise using it. */ this.reference = new ServiceReferenceImpl<>(this); } }
Call after constructing this object to complete the registration.
/** * Call after constructing this object to complete the registration. */
void register(Dictionary<String, ?> props) { final ServiceReferenceImpl<S> ref; synchronized (registry) { context.checkValid(); synchronized (registrationLock) { ref = reference; /* used to publish event outside sync */ this.properties = createProperties(props); /* must be valid after unregister is called. */ } if (registry.debug.DEBUG_SERVICES) { Debug.println("registerService[" + bundle + "](" + this + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } registry.addServiceRegistration(context, this); } /* must not hold the registrations lock when this event is published */ registry.publishServiceEvent(new ServiceEvent(ServiceEvent.REGISTERED, ref)); }
Update the properties associated with this service.

The key "objectClass" cannot be modified by this method. It's value is set when the service is registered.

The following steps are followed to modify a service's properties:

  1. The service's properties are replaced with the provided properties.
  2. A ServiceEvent of type ServiceEvent.MODIFIED is synchronously sent.
Params:
  • props – The properties for this service. Changes should not be made to this object after calling this method. To update the service's properties this method should be called again.
Throws:
/** * Update the properties associated with this service. * * <p>The key "objectClass" cannot be modified by this method. It's * value is set when the service is registered. * * <p>The following steps are followed to modify a service's properties: * <ol> * <li>The service's properties are replaced with the provided properties. * <li>A {@link ServiceEvent} of type {@link ServiceEvent#MODIFIED} * is synchronously sent. * </ol> * * @param props The properties for this service. * Changes should not be made to this object after calling this method. * To update the service's properties this method should be called again. * @exception java.lang.IllegalStateException If * this ServiceRegistration has already been unregistered. * * @exception IllegalArgumentException If the <tt>properties</tt> * parameter contains case variants of the same key name. */
@Override public void setProperties(Dictionary<String, ?> props) { final ServiceReferenceImpl<S> ref; final Map<String, Object> previousProperties; synchronized (registry) { synchronized (registrationLock) { if (state != REGISTERED) { /* in the process of unregisterING */ throw new IllegalStateException(Msg.SERVICE_ALREADY_UNREGISTERED_EXCEPTION); } ref = reference; /* used to publish event outside sync */ previousProperties = this.properties; this.properties = createProperties(props); } registry.modifyServiceRegistration(context, this); } /* must not hold the registrationLock when this event is published */ registry.publishServiceEvent(new ModifiedServiceEvent(ref, previousProperties)); }
Unregister the service. Remove a service registration from the framework's service registry. All ServiceReferenceImpl objects for this registration can no longer be used to interact with the service.

The following steps are followed to unregister a service:

  1. The service is removed from the framework's service registry so that it may no longer be used. ServiceReferenceImpls for the service may no longer be used to get a service object for the service.
  2. A ServiceEvent of type ServiceEvent.UNREGISTERING is synchronously sent so that bundles using this service may release their use of the service.
  3. For each bundle whose use count for this service is greater than zero:
    1. The bundle's use count for this service is set to zero.
    2. If the service was registered with a ServiceFactory, the ServiceFactory.ungetService method is called to release the service object for the bundle.
Throws:
See Also:
/** * Unregister the service. * Remove a service registration from the framework's service * registry. * All {@link ServiceReferenceImpl} objects for this registration * can no longer be used to interact with the service. * * <p>The following steps are followed to unregister a service: * <ol> * <li>The service is removed from the framework's service * registry so that it may no longer be used. * {@link ServiceReferenceImpl}s for the service may no longer be used * to get a service object for the service. * <li>A {@link ServiceEvent} of type {@link ServiceEvent#UNREGISTERING} * is synchronously sent so that bundles using this service * may release their use of the service. * <li>For each bundle whose use count for this service is greater * than zero: * <ol> * <li>The bundle's use count for this service is set to zero. * <li>If the service was registered with a {@link ServiceFactory}, * the {@link ServiceFactory#ungetService ServiceFactory.ungetService} method * is called to release the service object for the bundle. * </ol> * </ol> * * @exception java.lang.IllegalStateException If * this ServiceRegistration has already been unregistered. * @see BundleContextImpl#ungetService */
@Override public void unregister() { final ServiceReferenceImpl<S> ref; synchronized (registry) { synchronized (registrationLock) { if (state != REGISTERED) { /* in the process of unregisterING */ throw new IllegalStateException(Msg.SERVICE_ALREADY_UNREGISTERED_EXCEPTION); } /* remove this object from the service registry */ if (registry.debug.DEBUG_SERVICES) { Debug.println("unregisterService[" + bundle + "](" + this + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } registry.removeServiceRegistration(context, this); state = UNREGISTERING; /* mark unregisterING */ ref = reference; /* used to publish event outside sync */ } } /* must not hold the registrationLock when this event is published */ registry.publishServiceEvent(new ServiceEvent(ServiceEvent.UNREGISTERING, ref)); int size = 0; BundleContextImpl[] users = null; synchronized (registrationLock) { /* we have published the ServiceEvent, now mark the service fully unregistered */ state = UNREGISTERED; size = contextsUsing.size(); if (size > 0) { if (registry.debug.DEBUG_SERVICES) { Debug.println("unregisterService: releasing users"); //$NON-NLS-1$ } users = contextsUsing.toArray(new BundleContextImpl[size]); } } /* must not hold the registrationLock while releasing services */ for (int i = 0; i < size; i++) { releaseService(users[i]); } synchronized (registrationLock) { contextsUsing.clear(); reference = null; /* mark registration dead */ } /* The properties field must remain valid after unregister completes. */ }
Is this registration unregistered?
Returns:true if unregistered; otherwise false.
/** * Is this registration unregistered? * * @return true if unregistered; otherwise false. */
boolean isUnregistered() { synchronized (registrationLock) { return state == UNREGISTERED; } }
Returns a ServiceReferenceImpl object for this registration. The ServiceReferenceImpl object may be shared with other bundles.
Throws:
Returns:A ServiceReferenceImpl object.
/** * Returns a {@link ServiceReferenceImpl} object for this registration. * The {@link ServiceReferenceImpl} object may be shared with other bundles. * * @exception java.lang.IllegalStateException If * this ServiceRegistration has already been unregistered. * @return A {@link ServiceReferenceImpl} object. */
@Override public ServiceReference<S> getReference() { return getReferenceImpl(); } ServiceReferenceImpl<S> getReferenceImpl() { /* use reference instead of unregistered so that ServiceFactorys, called * by releaseService after the registration is unregistered, can * get the ServiceReference. Note this technically may violate the spec * but makes more sense. */ synchronized (registrationLock) { if (reference == null) { throw new IllegalStateException(Msg.SERVICE_ALREADY_UNREGISTERED_EXCEPTION); } return reference; } }
Count of service properties set by framework for each service registration.
  • Constants.OBJECTCLASS
  • Constants.SERVICE_ID
  • Constants.SERVICE_BUNDLEID
  • Constants.SERVICE_SCOPE
See Also:
  • createProperties(Dictionary)
/** * Count of service properties set by framework for each * service registration. * <ul> * <li>Constants.OBJECTCLASS</li> * <li>Constants.SERVICE_ID</li> * <li>Constants.SERVICE_BUNDLEID</li> * <li>Constants.SERVICE_SCOPE</li> * </ul> * @see #createProperties(Dictionary) */
private static final int FRAMEWORK_SET_SERVICE_PROPERTIES_COUNT = 4;
Construct a properties object from the dictionary for this ServiceRegistration.
Params:
  • p – The properties for this service.
Returns:A Properties object for this ServiceRegistration.
/** * Construct a properties object from the dictionary for this * ServiceRegistration. * * @param p The properties for this service. * @return A Properties object for this ServiceRegistration. */
/* @GuardedBy("registrationLock") */ private Map<String, Object> createProperties(Dictionary<String, ?> p) { assert Thread.holdsLock(registrationLock); ServiceProperties props = new ServiceProperties(p, FRAMEWORK_SET_SERVICE_PROPERTIES_COUNT); props.put(Constants.OBJECTCLASS, clazzes); props.put(Constants.SERVICE_ID, Long.valueOf(serviceid)); props.put(Constants.SERVICE_BUNDLEID, Long.valueOf(bundle.getBundleId())); final String scope; if (service instanceof ServiceFactory) { if (service instanceof PrototypeServiceFactory) { scope = Constants.SCOPE_PROTOTYPE; } else { scope = Constants.SCOPE_BUNDLE; } } else { scope = Constants.SCOPE_SINGLETON; } props.put(Constants.SERVICE_SCOPE, scope); Object ranking = props.get(Constants.SERVICE_RANKING); if (ranking instanceof Integer) { serviceranking = ((Integer) ranking).intValue(); } else { serviceranking = 0; if (ranking != null) { registry.getContainer().getEventPublisher().publishFrameworkEvent(FrameworkEvent.WARNING, getBundle(), new ServiceException("Invalid ranking type: " + ranking.getClass(), ServiceException.UNSPECIFIED)); //$NON-NLS-1$ } } return props.asUnmodifiableMap(); }
Return the properties object. This is for framework internal use only.
Returns:The service registration's properties.
/** * Return the properties object. This is for framework internal use only. * @return The service registration's properties. */
public Map<String, Object> getProperties() { synchronized (registrationLock) { return properties; } }
Get the value of a service's property.

This method will continue to return property values after the service has been unregistered. This is so that references to unregistered service can be interrogated. (For example: ServiceReference objects stored in the log.)

Params:
  • key – Name of the property.
Returns:Value of the property or null if there is no property by that name.
/** * Get the value of a service's property. * * <p>This method will continue to return property values after the * service has been unregistered. This is so that references to * unregistered service can be interrogated. * (For example: ServiceReference objects stored in the log.) * * @param key Name of the property. * @return Value of the property or <code>null</code> if there is * no property by that name. */
Object getProperty(String key) { synchronized (registrationLock) { return ServiceProperties.cloneValue(properties.get(key)); } }
Get the list of key names for the service's properties.

This method will continue to return the keys after the service has been unregistered. This is so that references to unregistered service can be interrogated. (For example: ServiceReference objects stored in the log.)

Returns:The list of property key names.
/** * Get the list of key names for the service's properties. * * <p>This method will continue to return the keys after the * service has been unregistered. This is so that references to * unregistered service can be interrogated. * (For example: ServiceReference objects stored in the log.) * * @return The list of property key names. */
String[] getPropertyKeys() { synchronized (registrationLock) { return properties.keySet().toArray(new String[0]); } }
Get a copy of the service's properties.

This method will continue to return the properties after the service has been unregistered. This is so that references to unregistered service can be interrogated. (For example: ServiceReference objects stored in the log.)

Returns:A copy of the properties.
/** * Get a copy of the service's properties. * * <p>This method will continue to return the properties after the * service has been unregistered. This is so that references to * unregistered service can be interrogated. * (For example: ServiceReference objects stored in the log.) * * @return A copy of the properties. */
Dictionary<String, Object> getPropertiesCopy() { synchronized (registrationLock) { return new ServiceProperties(properties); } }
Return the service id for this service.
Returns:The service id for this service.
/** * Return the service id for this service. * @return The service id for this service. */
long getId() { return serviceid; }
Return the service ranking for this service.
Returns:The service ranking for this service.
/** * Return the service ranking for this service. * @return The service ranking for this service. */
int getRanking() { synchronized (registrationLock) { return serviceranking; } } String[] getClasses() { return clazzes; } S getServiceObject() { return service; }
Return the bundle which registered the service.

This method will always return null when the service has been unregistered. This can be used to determine if the service has been unregistered.

Returns:The bundle which registered the service.
/** * Return the bundle which registered the service. * * <p>This method will always return <code>null</code> when the * service has been unregistered. This can be used to * determine if the service has been unregistered. * * @return The bundle which registered the service. */
Bundle getBundle() { synchronized (registrationLock) { if (reference == null) { return null; } return bundle; } }
This method returns the bundle which registered the service regardless of the registration status of this service registration. This is not an OSGi specified method.
Returns:The bundle which registered the service.
/** * This method returns the bundle which registered the * service regardless of the registration status of this * service registration. This is not an OSGi specified * method. * @return The bundle which registered the service. */
public Bundle getRegisteringBundle() { return bundle; } S getSafeService(BundleContextImpl user, ServiceConsumer consumer) { try { return getService(user, consumer); } catch (IllegalStateException e) { // can happen if the user is stopped on another thread return null; } }
Get a service object for the using BundleContext.
Params:
  • user – BundleContext using service.
  • consumer – The closure for the consumer type.
Returns:Service object
/** * Get a service object for the using BundleContext. * * @param user BundleContext using service. * @param consumer The closure for the consumer type. * @return Service object */
S getService(BundleContextImpl user, ServiceConsumer consumer) { if (isUnregistered()) { /* service unregistered */ return null; } Map<ServiceRegistrationImpl<?>, ServiceUse<?>> servicesInUse = user.getServicesInUseMap(); if (servicesInUse == null) { /* user is closed */ user.checkValid(); /* throw exception */ } if (registry.debug.DEBUG_SERVICES) { Debug.println("getService[" + user.getBundleImpl() + "](" + this + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } /* Use a while loop to support retry if a call to a ServiceFactory fails */ while (true) { ServiceUse<S> use; boolean added = false; /* Obtain the ServiceUse object for this service by bundle user */ synchronized (servicesInUse) { user.checkValid(); @SuppressWarnings("unchecked") ServiceUse<S> u = (ServiceUse<S>) servicesInUse.get(this); use = u; if (use == null) { /* if this is the first use of the service * optimistically record this service is being used. */ use = newServiceUse(user); added = true; synchronized (registrationLock) { if (state == UNREGISTERED) { /* service unregistered */ return null; } servicesInUse.put(this, use); contextsUsing.add(user); } } } /* Obtain and return the service object */ synchronized (use) { /* if another thread removed the ServiceUse, then * go back to the top and start again */ synchronized (servicesInUse) { user.checkValid(); if (servicesInUse.get(this) != use) { continue; } } S serviceObject = consumer.getService(use); /* if the service factory failed to return an object and * we created the service use, then remove the * optimistically added ServiceUse. */ if ((serviceObject == null) && added) { synchronized (servicesInUse) { synchronized (registrationLock) { servicesInUse.remove(this); contextsUsing.remove(user); } } } return serviceObject; } } }
Create a new ServiceObjects for the requesting bundle.
Params:
  • user – The requesting bundle.
Returns:A new ServiceObjects for this service and the requesting bundle.
/** * Create a new ServiceObjects for the requesting bundle. * * @param user The requesting bundle. * @return A new ServiceObjects for this service and the requesting bundle. */
ServiceObjectsImpl<S> getServiceObjects(BundleContextImpl user) { if (isUnregistered()) { /* service unregistered */ return null; } if (registry.debug.DEBUG_SERVICES) { Debug.println("getServiceObjects[" + user.getBundleImpl() + "](" + this + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } return new ServiceObjectsImpl<>(user, this); }
Create a new ServiceUse object for this service and user.
Params:
  • user – The bundle using this service.
Returns:The ServiceUse object for the bundle using this service.
/** * Create a new ServiceUse object for this service and user. * * @param user The bundle using this service. * @return The ServiceUse object for the bundle using this service. */
private ServiceUse<S> newServiceUse(BundleContextImpl user) { if (service instanceof ServiceFactory) { if (service instanceof PrototypeServiceFactory) { return new PrototypeServiceFactoryUse<>(user, this); } return new ServiceFactoryUse<>(user, this); } return new ServiceUse<>(user, this); }
Unget a service for the using BundleContext.
Params:
  • user – BundleContext using service.
  • consumer – The closure for the consumer type.
  • serviceObject – The service object to release for prototype consumers.
Returns:false if the context bundle's use count for the service is zero or if the service has been unregistered, otherwise true.
/** * Unget a service for the using BundleContext. * * @param user BundleContext using service. * @param consumer The closure for the consumer type. * @param serviceObject The service object to release for prototype consumers. * @return <code>false</code> if the context bundle's use count for the service * is zero or if the service has been unregistered, * otherwise <code>true</code>. */
boolean ungetService(BundleContextImpl user, ServiceConsumer consumer, S serviceObject) { if (isUnregistered()) { return false; } Map<ServiceRegistrationImpl<?>, ServiceUse<?>> servicesInUse = user.getServicesInUseMap(); if (servicesInUse == null) { return false; } if (registry.debug.DEBUG_SERVICES) { Debug.println("ungetService[" + user.getBundleImpl() + "](" + this + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } ServiceUse<S> use; synchronized (servicesInUse) { @SuppressWarnings("unchecked") ServiceUse<S> u = (ServiceUse<S>) servicesInUse.get(this); use = u; if (use == null) { return false; } } boolean result; synchronized (use) { result = consumer.ungetService(use, serviceObject); if (use.isEmpty()) { /* service use can be discarded */ synchronized (servicesInUse) { synchronized (registrationLock) { servicesInUse.remove(this); contextsUsing.remove(user); } } } } return result; }
Release the service for the using BundleContext.
Params:
  • user – BundleContext using service.
/** * Release the service for the using BundleContext. * * @param user BundleContext using service. */
void releaseService(BundleContextImpl user) { synchronized (registrationLock) { if (reference == null) { /* registration dead */ return; } } if (registry.debug.DEBUG_SERVICES) { Debug.println("releaseService[" + user.getBundleImpl() + "](" + this + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } Map<ServiceRegistrationImpl<?>, ServiceUse<?>> servicesInUse = user.getServicesInUseMap(); if (servicesInUse == null) { return; } ServiceUse<S> use; synchronized (servicesInUse) { synchronized (registrationLock) { @SuppressWarnings("unchecked") ServiceUse<S> u = (ServiceUse<S>) servicesInUse.remove(this); use = u; if (use == null) { return; } contextsUsing.remove(user); } } synchronized (use) { use.release(); } }
Return the list of bundle which are using this service.
Returns:Array of Bundles using this service.
/** * Return the list of bundle which are using this service. * * @return Array of Bundles using this service. */
Bundle[] getUsingBundles() { synchronized (registrationLock) { if (state == UNREGISTERED) /* service unregistered */ return null; int size = contextsUsing.size(); if (size == 0) return null; /* Copy list of BundleContext into an array of Bundle. */ Bundle[] bundles = new Bundle[size]; for (int i = 0; i < size; i++) bundles[i] = contextsUsing.get(i).getBundleImpl(); return bundles; } } boolean isAssignableTo(Bundle client, String className) { return PackageSource.isServiceAssignableTo(bundle, client, className, service.getClass(), context.getContainer()); }
Return a String representation of this object.
Returns:String representation of this object.
/** * Return a String representation of this object. * * @return String representation of this object. */
@Override public String toString() { int size = clazzes.length; StringBuilder sb = new StringBuilder(50 * size); sb.append('{'); for (int i = 0; i < size; i++) { if (i > 0) { sb.append(", "); //$NON-NLS-1$ } sb.append(clazzes[i]); } sb.append("}="); //$NON-NLS-1$ sb.append(getProperties().toString()); return sb.toString(); }
Compares this ServiceRegistrationImpl with the specified ServiceRegistrationImpl for order.

This does a reverse comparison so that the highest item is sorted to the left. We keep ServiceRegistationImpls in sorted lists such that the highest ranked service is at element 0 for quick retrieval.

Params:
  • other – The ServiceRegistrationImpl to be compared.
Returns:Returns a negative integer, zero, or a positive integer if this ServiceRegistrationImpl is greater than, equal to, or less than the specified ServiceRegistrationImpl.
/** * Compares this <code>ServiceRegistrationImpl</code> with the specified * <code>ServiceRegistrationImpl</code> for order. * * <p> * This does a reverse comparison so that the highest item is sorted to the left. * We keep ServiceRegistationImpls in sorted lists such that the highest * ranked service is at element 0 for quick retrieval. * * @param other The <code>ServiceRegistrationImpl</code> to be compared. * @return Returns a negative integer, zero, or a positive integer if this * <code>ServiceRegistrationImpl</code> is greater than, equal to, or * less than the specified <code>ServiceRegistrationImpl</code>. */
@Override public int compareTo(ServiceRegistrationImpl<?> other) { final int thisRanking = this.getRanking(); final int otherRanking = other.getRanking(); if (thisRanking != otherRanking) { if (thisRanking < otherRanking) { return 1; } return -1; } final long thisId = this.getId(); final long otherId = other.getId(); if (thisId == otherId) { return 0; } if (thisId < otherId) { return -1; } return 1; } }