1 /***********************************************************************
2  * Copyright (c) 2013-2025 General Atomics Integrated Intelligence, Inc.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Apache License, Version 2.0
5  * which accompanies this distribution and is available at
6  * https://www.apache.org/licenses/LICENSE-2.0
7  ***********************************************************************/
8 
9 package org.locationtech.geomesa.utils.classpath
10 
11 import com.typesafe.scalalogging.LazyLogging
12 
13 import scala.reflect.ClassTag
14 
15 /**
16   * Scala SPI loader helper
17   */
18 object ServiceLoader extends LazyLogging {
19 
20   import scala.collection.JavaConverters._
21 
22   /**
23    * Load all services
24    *
25    * @param loader optional classloader to use
26    * @param ct classtag
27    * @tparam T service type
28    * @return list of services
29    */
30   def load[T](loader: Option[ClassLoader] = None)(implicit ct: ClassTag[T]): List[T] = {
31     // check if the current class is a child of the context classloader
32     // this fixes service loading in Accumulo's per-namespace classpaths
33     val clas = ct.runtimeClass.asInstanceOf[Class[T]]
34     val ldr = loader.getOrElse {
35       def chain(cl: ClassLoader): Stream[ClassLoader] =
36         if (cl == null) { Stream.empty } else { cl #:: chain(cl.getParent) }
37       val ccl = Thread.currentThread().getContextClassLoader
38       if (ccl == null || chain(clas.getClassLoader).contains(ccl)) {
39         clas.getClassLoader
40       } else {
41         logger.warn(s"Using a context ClassLoader that does not contain the class to load (${clas.getName}): $ccl")
42         ccl
43       }
44     }
45     java.util.ServiceLoader.load(clas, ldr).asScala.toList
46   }
47 
48   /**
49     * Load a service. If there is not exactly 1 implementation found, throws an exception
50     *
51     * @param ct classtag
52     * @tparam T service type
53     * @throws IllegalStateException if there is not exactly 1 service found
54     * @return service
55     */
56   @throws[IllegalStateException]
57   def loadExactlyOne[T](loader: Option[ClassLoader] = None)(implicit ct: ClassTag[T]): T = {
58     val all = load[T](loader)
59     if (all.lengthCompare(1) != 0) {
60       throw new IllegalStateException(s"Expected 1 instance of ${ct.runtimeClass.getName} but found ${all.length}")
61     }
62     all.head
63   }
64 
65   /**
66     * Load a service. If there is not exactly 0 or 1 implementations found, throws an exception
67     *
68     * @param ct classtag
69     * @tparam T service type
70     * @throws IllegalStateException if there is not exactly 0 or 1 service found
71     * @return service, if found
72     */
73   @throws[IllegalStateException]
74   def loadAtMostOne[T](loader: Option[ClassLoader] = None)(implicit ct: ClassTag[T]): Option[T] = {
75     val all = load[T](loader)
76     if (all.lengthCompare(1) > 0) {
77       throw new IllegalStateException(s"Expected 0 or 1 instances of ${ct.runtimeClass.getName} but found ${all.length}")
78     }
79     all.headOption
80   }
81 }
Line Stmt Id Pos Tree Symbol Tests Code
33 2648 1138 - 1176 TypeApply scala.Any.asInstanceOf ct.runtimeClass.asInstanceOf[Class[T]]
34 2661 1191 - 1657 Apply scala.Option.getOrElse loader.getOrElse[ClassLoader]({ def chain(cl: ClassLoader): Stream[ClassLoader] = if (cl.==(null)) scala.`package`.Stream.empty[Nothing] else { <synthetic> <artifact> val x$1: ClassLoader = cl; immutable.this.Stream.consWrapper[ClassLoader](chain(cl.getParent())).#::[ClassLoader](x$1) }; val ccl: ClassLoader = java.lang.Thread.currentThread().getContextClassLoader(); if (ccl.==(null).||(chain(clas.getClassLoader()).contains[ClassLoader](ccl))) clas.getClassLoader() else { (if (ServiceLoader.this.logger.underlying.isWarnEnabled()) ServiceLoader.this.logger.underlying.warn("Using a context ClassLoader that does not contain the class to load ({}): {}", (scala.Array.apply[AnyRef]((clas.getName(): AnyRef), (ccl: AnyRef))((ClassTag.AnyRef: scala.reflect.ClassTag[AnyRef])): _*)) else (): Unit); ccl } })
36 2649 1278 - 1288 Apply java.lang.Object.== cl.==(null)
36 2650 1292 - 1304 TypeApply scala.collection.immutable.Stream.empty scala.`package`.Stream.empty[Nothing]
36 2651 1292 - 1304 Block scala.collection.immutable.Stream.empty scala.`package`.Stream.empty[Nothing]
36 2652 1314 - 1340 Apply scala.collection.immutable.Stream.ConsWrapper.#:: immutable.this.Stream.consWrapper[ClassLoader](chain(cl.getParent())).#::[ClassLoader](x$1)
36 2653 1314 - 1340 Block <nosymbol> { <synthetic> <artifact> val x$1: ClassLoader = cl; immutable.this.Stream.consWrapper[ClassLoader](chain(cl.getParent())).#::[ClassLoader](x$1) }
37 2654 1359 - 1403 Apply java.lang.Thread.getContextClassLoader java.lang.Thread.currentThread().getContextClassLoader()
38 2655 1421 - 1425 Literal <nosymbol> null
38 2656 1429 - 1469 Apply scala.collection.LinearSeqOptimized.contains chain(clas.getClassLoader()).contains[ClassLoader](ccl)
38 2657 1414 - 1469 Apply scala.Boolean.|| ccl.==(null).||(chain(clas.getClassLoader()).contains[ClassLoader](ccl))
39 2658 1481 - 1500 Apply java.lang.Class.getClassLoader clas.getClassLoader()
39 2659 1481 - 1500 Block java.lang.Class.getClassLoader clas.getClassLoader()
40 2660 1514 - 1651 Block <nosymbol> { (if (ServiceLoader.this.logger.underlying.isWarnEnabled()) ServiceLoader.this.logger.underlying.warn("Using a context ClassLoader that does not contain the class to load ({}): {}", (scala.Array.apply[AnyRef]((clas.getName(): AnyRef), (ccl: AnyRef))((ClassTag.AnyRef: scala.reflect.ClassTag[AnyRef])): _*)) else (): Unit); ccl }
45 2662 1662 - 1701 Apply java.util.ServiceLoader.load java.util.ServiceLoader.load[T](clas, ldr)
45 2663 1662 - 1716 Select scala.collection.TraversableOnce.toList scala.collection.JavaConverters.iterableAsScalaIterableConverter[T](java.util.ServiceLoader.load[T](clas, ldr)).asScala.toList
58 2664 2123 - 2138 ApplyToImplicitArgs org.locationtech.geomesa.utils.classpath.ServiceLoader.load ServiceLoader.this.load[T](loader)(ct)
59 2665 2147 - 2172 Apply scala.Int.!= all.lengthCompare(1).!=(0)
59 2668 2143 - 2143 Literal <nosymbol> ()
59 2669 2143 - 2143 Block <nosymbol> ()
60 2666 2182 - 2291 Throw <nosymbol> throw new java.lang.IllegalStateException(scala.StringContext.apply("Expected 1 instance of ", " but found ", "").s(ct.runtimeClass.getName(), all.length))
60 2667 2182 - 2291 Block <nosymbol> throw new java.lang.IllegalStateException(scala.StringContext.apply("Expected 1 instance of ", " but found ", "").s(ct.runtimeClass.getName(), all.length))
62 2670 2302 - 2310 Select scala.collection.IterableLike.head all.head
75 2671 2745 - 2760 ApplyToImplicitArgs org.locationtech.geomesa.utils.classpath.ServiceLoader.load ServiceLoader.this.load[T](loader)(ct)
76 2672 2769 - 2793 Apply scala.Int.> all.lengthCompare(1).>(0)
76 2675 2765 - 2765 Literal <nosymbol> ()
76 2676 2765 - 2765 Block <nosymbol> ()
77 2673 2803 - 2918 Throw <nosymbol> throw new java.lang.IllegalStateException(scala.StringContext.apply("Expected 0 or 1 instances of ", " but found ", "").s(ct.runtimeClass.getName(), all.length))
77 2674 2803 - 2918 Block <nosymbol> throw new java.lang.IllegalStateException(scala.StringContext.apply("Expected 0 or 1 instances of ", " but found ", "").s(ct.runtimeClass.getName(), all.length))
79 2677 2929 - 2943 Select scala.collection.TraversableLike.headOption all.headOption