diff options
author | Alex Blewitt | 2020-07-16 15:38:16 +0000 |
---|---|---|
committer | Alex Blewitt | 2020-07-16 15:38:16 +0000 |
commit | 65adf381d99a379be38b4d9ea6f4e136cc9f2683 (patch) | |
tree | 6e1ddf103609ad39f29429f0cd6b135e929f3b65 | |
parent | 81083c1d65abe31c8c5ec8fe866ebee96980a84d (diff) | |
download | rt.equinox.bundles-65adf381d99a379be38b4d9ea6f4e136cc9f2683.tar.gz rt.equinox.bundles-65adf381d99a379be38b4d9ea6f4e136cc9f2683.tar.xz rt.equinox.bundles-65adf381d99a379be38b4d9ea6f4e136cc9f2683.zip |
Bug 565276 - Add documentation for sneakyThrow to ServiceCallerI20200716-1800
When using `ServiceCaller` to invoke a consumer that throws a
checked exception, the exception itself is not trivially thrown back
again. However, using the sneakyThrow technique, it is possible to fool
the compiler into throwing a checked exception as an unchecked
exception.
Document this case in the JavaDoc so that others who experience a
similar pattern can implement it in a similar way. This isn't API
on `ServiceCaller` because generally the implementation is not related
to this particular class and should not be used as such.
Change-Id: I48553ccea9d14f6ea852e8ca4d9a6fb87b129e92
Signed-off-by: Alex Blewitt <alex.blewitt@gmail.com>
-rw-r--r-- | bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/ServiceCaller.java | 45 |
1 files changed, 35 insertions, 10 deletions
diff --git a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/ServiceCaller.java b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/ServiceCaller.java index 7afe74e6e..cabf4e15d 100644 --- a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/ServiceCaller.java +++ b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/ServiceCaller.java @@ -30,7 +30,7 @@ import org.osgi.util.tracker.ServiceTracker; * <li> Multiple invocations that happen often and rapidly. In this case, maintaining * a cache of the service is worth the overhead.</li> * </ul> - * + * <p> * For single invocations of a service the static method * {@link ServiceCaller#callOnce(Class, Class, Consumer)} can be used. * This method will wrap a call to the consumer of the service with @@ -38,21 +38,24 @@ import org.osgi.util.tracker.ServiceTracker; * exists and will do the proper get and release service operations * surround the calls to the service. By wrapping a call around the * service we can ensure that it is correctly released after use. + * </p> * <p> * Single invocation example: + * </p> * <pre> * ServiceCaller.callOnce(MyClass.class, ILog.class, (logger) -> logger.info("All systems go!")); * </pre> + * <p> * Note that it is generally more efficient to use a long-running service * utility, such as {@link ServiceTracker} or declarative services, but there * are cases where a single one-shot lookup is preferable, especially if the * service is not required after use. Examples might include logging unlikely * conditions or processing debug options that are only read once. - * - * + * </p> + * <p> * This allows boilerplate code to be reduced at call sites, which would * otherwise have to do something like: - * + * </p> * <pre> * Bundle bundle = FrameworkUtil.getBundle(BadExample.class); * BundleContext context = bundle == null ? null : bundle.getBundleContext(); @@ -65,32 +68,53 @@ import org.osgi.util.tracker.ServiceTracker; * context.ungetService(reference); * } * </pre> + * <p> * For cases where a service is used much more often a {@code ServiceCaller} instance * can be used to cache and track the available service. This may be useful for cases * that cannot use declarative services and that want to avoid using something like * a {@link ServiceTracker} that does not easily allow for lazy instantiation of the service * instance. For example, if logging is used more often then something like the following * could be used: + * </p> * <pre> * static final ServiceCaller<ILog> log = new ServiceCaller(MyClass.class, ILog.class); * static void info(String msg) { - * log.call(logger -> logger.info(msg); + * log.call(logger -> logger.info(msg)); * } * </pre> - * + * <p> * Note that this class is intended for simple service usage patterns only. More advanced cases * should use other mechanisms such as the {@link ServiceTracker} or declarative services. - * + * </p> * @param <Service> the service type for this caller * @since 3.13 */ public class ServiceCaller<Service> { /** * Calls an OSGi service by dynamically looking it up and passing it to the given consumer. - * + * <p> * If not running under OSGi, the caller bundle is not active or the service is not available, return false. - * Any exception thrown by the consumer is rethrown by this method. * If the service is found, call the service and return true. + * </p> + * <p> + * Any runtime exception thrown by the consumer is rethrown by this method. + * If the consumer throws a checked exception, it can be propagated using a <em>sneakyThrow</em> + * inside a try/catch block: + * </p> + * <pre> + * callOnce(MyClass.class, Callable.class, (callable) -> { + * try { + * callable.call(); + * } catch (Exception e) { + * sneakyThrow(e); + * } + * }); + * ... + * @SuppressWarnings("unchecked") + * static <E extends Throwable> void sneakyThrow(Throwable e) throws E { + * throw (E) e; + * } + * </pre> * @param caller a class from the bundle that will use service * @param serviceType the OSGi service type to look up * @param consumer the consumer of the OSGi service @@ -266,7 +290,8 @@ public class ServiceCaller<Service> { /** * Calls an OSGi service by dynamically looking it up and passing it to the given consumer. * If not running under OSGi, the caller bundle is not active or the service is not available, return false. - * Any exception thrown by the consumer is rethrown by this method. + * Any runtime exception thrown by the consumer is rethrown by this method. + * (For handling checked exceptions, see {@link #callOnce(Class, Class, Consumer)} for a solution.) * Subsequent calls to this method will attempt to reuse the previously acquired service instance until one * of the following occurs: * <ul> |