All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.helpers.Service Maven / Gradle / Ivy

Go to download

Neo4j kernel is a lightweight, embedded Java database designed to store data structured as graphs rather than tables. For more information, see http://neo4j.org.

There is a newer version: 5.26.0
Show newest version
/**
 * Copyright (c) 2002-2013 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.helpers;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import org.neo4j.helpers.collection.FilteringIterable;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.helpers.collection.NestingIterable;
import org.neo4j.helpers.collection.PrefetchingIterator;

/**
 * A utility for locating services. This implements the same functionality as 
 * the Java 6 ServiceLoader interface, in fact it uses the
 * ServiceLoader if available, but backports the functionality to
 * previous Java versions and adds some error handling to ignore misconfigured
 * service implementations.
 * 

* Additionally this class can be used as a base class for implementing services * that are differentiated by a String key. An example implementation might be: *

*

 * 
 * public abstract class StringConverter extends org.neo4j.commons.Service
 * {
 *     protected StringConverter(String id)
 *     {
 *         super( id );
 *     }
 *
 *     public abstract String convert( String input );
 *
 *     public static StringConverter load( String id )
 *     {
 *         return org.neo4j.commons.Service.load( StringConverter.class, id );
 *     }
 * }
 * 
 * 
*

* With for example these implementations: *

*

 * 
 * public final class UppercaseConverter extends StringConverter
 * {
 *     public UppercaseConverter()
 *     {
 *         super( "uppercase" );
 *     }
 *
 *     public String convert( String input )
 *     {
 *         return input.toUpperCase();
 *     }
 * }
 *
 * public final class ReverseConverter extends StringConverter
 * {
 *     public ReverseConverter()
 *     {
 *         super( "reverse" );
 *     }
 *
 *     public String convert( String input )
 *     {
 *         char[] chars = input.toCharArray();
 *         for ( int i = 0; i < chars.length/2; i++ )
 *         {
 *             char intermediate = chars[i];
 *             chars[i] = chars[chars.length-1-i];
 *             chars[chars.length-1-i] = chars[i];
 *         }
 *         return new String( chars );
 *     }
 * }
 * 
 * 
*

* This would then be used as: *

*

 * 
 * String atad = StringConverter.load( "reverse" ).convert( "data" );
 * 
 * 
* * @author Tobias Ivarsson */ public abstract class Service { /** * Designates that a class implements the specified service and should be * added to the services listings file (META-INF/services/[service-name]). *

* The annotation in itself does not provide any functionality for adding * the implementation class to the services listings file. But it serves as * a handle for an Annotation Processing Tool to utilize for performing that * task. * * @author Tobias Ivarsson */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface Implementation { /** * The service(s) this class implements. * * @return the services this class implements. */ Class[] value(); } /** * A base class for services, similar to {@link Service}, that compares keys * using case insensitive comparison instead of exact comparison. * * @author Tobias Ivarsson */ public static abstract class CaseInsensitiveService extends Service { /** * Create a new instance of a service implementation identified with the * specified key(s). * * @param key the main key for identifying this service implementation * @param altKeys alternative spellings of the identifier of this * service implementation */ protected CaseInsensitiveService( String key, String... altKeys ) { super( key, altKeys ); } @Override final public boolean matches( String key ) { for ( String id : keys ) { if ( id.equalsIgnoreCase( key ) ) { return true; } } return false; } } /** * Load all implementations of a Service. * * @param the type of the Service * @param type the type of the Service to load * @return all registered implementations of the Service */ public static Iterable load( Class type ) { Iterable loader; if ( null != (loader = java6Loader( type )) ) { return loader; } if ( null != (loader = sunJava5Loader( type )) ) { return loader; } if ( null != (loader = ourOwnLoader( type )) ) { return loader; } return Collections.emptyList(); } /** * Load the Service implementation with the specified key. * * @param the type of the Service * @param type the type of the Service to load * @param key the key that identifies the desired implementation * @return the matching Service implementation */ public static T load( Class type, String key ) { for ( T impl : load( type ) ) { if ( impl.matches( key ) ) { return impl; } } throw new NoSuchElementException( String.format( "Could not find any implementation of %s with a key=\"%s\"", type.getName(), key ) ); } final Set keys; /** * Create a new instance of a service implementation identified with the * specified key(s). * * @param key the main key for identifying this service implementation * @param altKeys alternative spellings of the identifier of this service * implementation */ protected Service( String key, String... altKeys ) { if ( altKeys == null || altKeys.length == 0 ) { this.keys = Collections.singleton( key ); } else { this.keys = new HashSet( Arrays.asList( altKeys ) ); this.keys.add( key ); } } @Override public String toString() { return getClass().getSuperclass().getName() + "" + keys; } public boolean matches( String key ) { return keys.contains( key ); } public Iterable getKeys() { return keys; } @Override public boolean equals( Object o ) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } Service service = (Service) o; if ( !keys.equals( service.keys ) ) { return false; } return true; } @Override public int hashCode() { return keys.hashCode(); } private static Iterable filterExceptions( final Iterable iterable ) { return new Iterable() { public Iterator iterator() { return new PrefetchingIterator() { final Iterator iterator = iterable.iterator(); @Override protected T fetchNextOrNull() { while ( iterator.hasNext() ) { try { return iterator.next(); } catch ( Throwable e ) { } } return null; } }; } }; } private static Iterable java6Loader( Class type ) { try { Class serviceLoaderClass = Class .forName( "java.util.ServiceLoader" ); Iterable contextClassLoaderServices = (Iterable) serviceLoaderClass .getMethod( "load", Class.class ).invoke( null, type ); // Jboss 7 does not export content of META-INF/services to context // class loader, // so this call adds implementations defined in Neo4j libraries from // the same module. Iterable currentClassLoaderServices = (Iterable) serviceLoaderClass .getMethod( "load", Class.class, ClassLoader.class ).invoke( null, type, Service.class.getClassLoader() ); // Combine services loaded by both context and module classloaders. // Service instances compared by full class name ( we cannot use // equals for instances or classes because they can came from // different classloaders ). HashMap services = new HashMap(); putAllInstancesToMap( currentClassLoaderServices, services ); // Services from context class loader have higher precedence putAllInstancesToMap( contextClassLoaderServices, services ); return services.values(); } catch ( Exception e ) { return null; } catch ( LinkageError e ) { return null; } } /** * @param services * @param servicesMap */ private static void putAllInstancesToMap( Iterable services, Map servicesMap ) { for ( T instance : filterExceptions( services ) ) { if ( null != instance ) { servicesMap.put( instance.getClass().getName(), instance ); } } } private static Iterable sunJava5Loader( final Class type ) { final Method providers; try { providers = Class.forName( "sun.misc.Service" ).getMethod( "providers", Class.class ); } catch ( Exception e ) { return null; } catch ( LinkageError e ) { return null; } return filterExceptions( new Iterable() { public Iterator iterator() { try { @SuppressWarnings("unchecked") Iterator result = (Iterator) providers.invoke( null, type ); return result; } catch ( Exception e ) { throw new RuntimeException( "Failed to invoke sun.misc.Service.providers(forClass)", e ); } } } ); } private static Iterable ourOwnLoader( final Class type ) { Collection urls = new HashSet(); try { Enumeration resources = Thread.currentThread().getContextClassLoader() .getResources( "META-INF/services/" + type.getName() ); while ( resources.hasMoreElements() ) { urls.add( resources.nextElement() ); } } catch ( IOException e ) { return null; } return new NestingIterable( FilteringIterable.notNull( new IterableWrapper( urls ) { @Override protected BufferedReader underlyingObjectToObject( URL url ) { try { return new BufferedReader( new InputStreamReader( url.openStream(), "utf-8" ) ); } catch ( IOException e ) { return null; } } } ) ) { @Override protected Iterator createNestedIterator( final BufferedReader input ) { return new PrefetchingIterator() { @Override protected T fetchNextOrNull() { try { String line; while ( null != (line = input.readLine()) ) { try { return type.cast( Class.forName( line ).newInstance() ); } catch ( Exception e ) { } catch ( LinkageError e ) { } } input.close(); return null; } catch ( IOException e ) { return null; } } /* Finalizer - close the input stream. * Prevent leakage of open files. Finalizers impact GC performance, * but there are expected to be few of these objects. */ @Override protected void finalize() throws Throwable { input.close(); } }; } }; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy