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

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

Go to download

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

There is a newer version: 3.6.2
Show newest version
/*
 * Copyright (c) 2018-2020 "Graph Foundation,"
 * Graph Foundation, Inc. [https://graphfoundation.org]
 *
 * This file is part of ONgDB.
 *
 * ONgDB 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 .
 */
/*
 * Copyright (c) 2002-2020 "Neo4j,"
 * Neo4j Sweden AB [http://neo4j.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.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.ServiceLoader;
import java.util.Set;

import org.neo4j.helpers.collection.PrefetchingIterator;

import static org.neo4j.util.FeatureToggles.flag;

/**
 * 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 { /** * Enabling this is useful for debugging why services aren't loaded where you would expect them to. */ private static final boolean printServiceLoaderStackTraces = flag( Service.class, "printServiceLoaderStackTraces", false ); private final Set keys; /** * 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. *

* This annotation is deprecated and will be removed in a future release. * * @author Tobias Ivarsson */ @Target( ElementType.TYPE ) @Retention( RetentionPolicy.SOURCE ) @Deprecated public @interface Implementation { /** * The service(s) this class implements. * * @return the services this class implements. */ Class[] value(); } /** * 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; } return Collections.emptyList(); } /** * Load the Service implementation with the specified key. This method will return null if requested service not * found. * * @param type the type of the Service to load * @param key the key that identifies the desired implementation * @param the type of the Service to load * @return requested service */ public static T loadSilently( Class type, String key ) { for ( T service : load( type ) ) { if ( service.matches( key ) ) { return service; } } return null; } /** * Load the Service implementation with the specified key. This method should never return null. * * @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 ) { T service = loadSilently( type, key ); if ( service == null ) { throw new NoSuchElementException( String.format( "Could not find any implementation of %s with a key=\"%s\"", type.getName(), key ) ); } return 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 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; return keys.equals( service.keys ); } @Override public int hashCode() { return keys.hashCode(); } private static Iterable filterExceptions( final Iterable iterable ) { return () -> new PrefetchingIterator() { private final Iterator iterator = iterable.iterator(); @Override protected T fetchNextOrNull() { while ( iterator.hasNext() ) { try { return iterator.next(); } catch ( Throwable e ) { if ( printServiceLoaderStackTraces ) { e.printStackTrace(); } } } return null; } }; } private static Iterable java6Loader( Class type ) { try { HashMap services = new HashMap<>(); ClassLoader currentCL = Service.class.getClassLoader(); ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); Iterable contextClassLoaderServices = ServiceLoader.load( type, contextCL ); if ( currentCL != contextCL ) { // 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 = ServiceLoader.load( type, currentCL ); // Combine services loaded by both context and module class loaders. // Service instances compared by full class name ( we cannot use // equals for instances or classes because they can came from // different class loaders ). putAllInstancesToMap( currentClassLoaderServices, services ); // Services from context class loader have higher precedence, // so we load those later. } putAllInstancesToMap( contextClassLoaderServices, services ); return services.values(); } catch ( Exception | LinkageError e ) { if ( printServiceLoaderStackTraces ) { e.printStackTrace(); } return null; } } private static void putAllInstancesToMap( Iterable services, Map servicesMap ) { for ( T instance : filterExceptions( services ) ) { if ( null != instance ) { servicesMap.put( instance.getClass().getName(), instance ); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy