package org.cache2k.spi;
/*
* #%L
* cache2k API
* %%
* Copyright (C) 2000 - 2017 headissue GmbH, Munich
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Resolves a service provider by its interface. This is used for the cache2k
* internal providers.
*
* <p>This class is in principle similar to the {@link java.util.ServiceLoader}.
* The design is a little bit simpler, because there is always one provider for each
* interface. We cannot use the original ServiceLoader, since it is not working on
* android.
*
* @author Jens Wilke; created: 2014-06-19
* @see <a href="https://code.google.com/p/android/issues/detail?id=59658">android service loader issue</a>
*/
public class SingleProviderResolver {
private static Map<Class, Object> providers = new HashMap<Class, Object>();
/**
* Return a provider for this interface.
*
* @param c the provider interface that is implemented
* @param <T> type of provider interface
*
* @return instance of the provider, never null
* @throws java.lang.LinkageError if there is a problem instantiating the provider
* or no provider was specified
*/
public static <T> T resolveMandatory(Class<T> c) {
T obj = resolve(c);
if (obj == null) {
Error err = new LinkageError("No implementation found for: " + c.getName());
err.printStackTrace();
throw err;
}
return obj;
}
/**
* Return a provider for this interface.
*
* @param c the provider interface that is implemented
* @param <T> type of provider interface
*
* @return instance of the provider or null if not found
* @throws java.lang.LinkageError if there is a problem instantiating the provider
*/
public synchronized static <T> T resolve(Class<T> c) {
if (providers.containsKey(c)) {
return (T) providers.get(c);
}
try {
String _className = readFile("org/cache2k/services/" + c.getName());
if (_className == null) {
return null;
}
T obj = (T) SingleProviderResolver.class.getClassLoader().loadClass(_className).newInstance();
providers.put(c, obj);
return obj;
} catch (Exception ex) {
Error err = new LinkageError("Error instantiating " + c.getName(), ex);
err.printStackTrace();
throw err;
}
}
private static String readFile(String _name) throws IOException {
InputStream in = SingleProviderResolver.class.getClassLoader().getResourceAsStream(_name);
if (in == null) {
return null;
}
LineNumberReader r = new LineNumberReader(new InputStreamReader(in));
String l = r.readLine();
while (l != null) {
if (!l.startsWith("#")) {
return l;
}
l = r.readLine();
}
throw new IOException("No class file name in resource: " + _name);
}
}