View Javadoc

1   package org.apache.onami.spi.core;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import static java.lang.System.getProperty;
23  import static java.lang.ClassLoader.getSystemClassLoader;
24  import static java.lang.String.format;
25  import static java.lang.Thread.currentThread;
26  
27  import java.io.IOException;
28  import java.net.URL;
29  import java.util.Enumeration;
30  import java.util.Iterator;
31  
32  import com.google.inject.ProvisionException;
33  
34  /**
35   * A simple service-provider loading facility.
36   *
37   * @param <S> The type of the service to be loaded by this loader.
38   */
39  public final class ServiceLoader<S>
40      implements Iterable<Class<? extends S>>
41  {
42  
43      private static final String SERVICE_PREFIX = "META-INF/services/%s";
44  
45      /**
46       * Creates a new service loader for the given service type, using the current thread's
47       * {@linkplain java.lang.Thread#getContextClassLoader context class loader}.
48       *
49       * @param <S> The type of the service to be loaded by this loader.
50       * @param service The class or interface representing the service being loaded.
51       * @return A new service loader.
52       */
53      public static <S> ServiceLoader<S> load( Class<S> service )
54      {
55          return load( service, currentThread().getContextClassLoader() );
56      }
57  
58      /**
59       * Creates a new service loader for the given service type and class loader.
60       *
61       * @param <S> The type of the service to be loaded by this loader.
62       * @param service The class or interface representing the service being loaded.
63       * @param classLoader The class loader used to locate, load, and instantiate providers.
64       * @return A new service loader.
65       */
66      public static <S> ServiceLoader<S> load( Class<S> service, ClassLoader classLoader )
67      {
68          if ( service == null )
69          {
70              throw new IllegalArgumentException( "Parameter 'service' must not be null" );
71          }
72          if ( classLoader == null )
73          {
74              throw new IllegalArgumentException( "Parameter 'classLoader' must not be null" );
75          }
76  
77          return new ServiceLoader<S>( service, classLoader );
78      }
79  
80      /**
81       * Creates a new service loader for the given service type, using the extension class loader.
82       *
83       * @param <S> The type of the service to be loaded by this loader.
84       * @param service The class or interface representing the service being loaded.
85       * @return A new service loader.
86       */
87      public static <S> ServiceLoader<S> loadInstalled( Class<S> service )
88      {
89          ClassLoader parent = getSystemClassLoader();
90          ClassLoader current = null;
91          while ( parent != null )
92          {
93              current = parent;
94              parent = parent.getParent();
95          }
96          return load( service, current );
97      }
98  
99      /**
100      * The class or interface representing the service being loaded.
101      */
102     private final String serviceName;
103 
104     /**
105      * The current lazy-lookup class iterator.
106      */
107     private Iterator<Class<? extends S>> serviceClassIterator;
108 
109     /**
110      * This class can't be instantiate directly, use static methods instead.
111      *
112      * @param service the class or interface representing the service being loaded.
113      * @param classLoader the class loader used to locate, load, and instantiate providers.
114      */
115     private ServiceLoader( Class<S> service, ClassLoader classLoader )
116     {
117         serviceName = service.getName();
118 
119         String systemServiceName = getProperty( serviceName );
120 
121         if ( systemServiceName != null )
122         {
123             serviceClassIterator = new PropertyServiceClassIterator<S>( service, classLoader, systemServiceName );
124         }
125         else
126         {
127             String fullName = format( SERVICE_PREFIX, serviceName );
128             try
129             {
130                 Enumeration<URL> serviceResources = classLoader.getResources( fullName );
131                 serviceClassIterator = new URLServiceNamesIterator<S>( service, classLoader, serviceResources );
132             }
133             catch ( IOException e )
134             {
135                 throw new ProvisionException( format( "An error occurred while loading '%s' Service resource(s) from classpath: %s",
136                                                       fullName, e.getMessage() ) );
137             }
138         }
139     }
140 
141     /**
142      * Returns an iterator over a set of elements of type {@code Class<? extends S>}.
143      *
144      * @return an iterator over a set of elements of type {@code Class<? extends S>}.
145      */
146     public Iterator<Class<? extends S>> iterator()
147     {
148         return serviceClassIterator;
149     }
150 
151     /**
152      * {@inheritDoc}
153      */
154     @Override
155     public String toString()
156     {
157         return format( "%s[%s]", getClass().getSimpleName(), serviceName );
158     }
159 
160 }