001/* 002 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package jdk.internal.jvmci.service; 024 025import java.util.*; 026 027import sun.reflect.*; 028 029/** 030 * An mechanism for accessing service providers via JVMCI. These providers are loaded via a JVMCI 031 * class loader that is hidden from application code. Hence the {@link SecurityManager} checks in 032 * {@link #load(Class)} and {@link #loadSingle(Class, boolean)}. 033 */ 034public final class Services { 035 036 private Services() { 037 } 038 039 private static final String SUPPRESS_PROPERTY_NAME = "jvmci.service.suppressNoClassDefFoundError"; 040 041 /** 042 * Determines whether to suppress the {@link NoClassDefFoundError} raised if a service provider 043 * class specified in a {@code <jre>/jvmci/services/*} file is missing. 044 */ 045 private static final boolean SuppressNoClassDefFoundError = Boolean.getBoolean(SUPPRESS_PROPERTY_NAME); 046 047 private static final ClassValue<List<?>> cache = new ClassValue<List<?>>() { 048 @Override 049 protected List<?> computeValue(Class<?> type) { 050 try { 051 return Arrays.asList(getServiceImpls(type)); 052 } catch (NoClassDefFoundError e) { 053 if (SuppressNoClassDefFoundError) { 054 return Collections.emptyList(); 055 } else { 056 NoClassDefFoundError newEx = new NoClassDefFoundError(e.getMessage() + " (suppress with -D" + SUPPRESS_PROPERTY_NAME + "=true)"); 057 if (e.getCause() != null) { 058 newEx.initCause(e.getCause()); 059 } 060 throw newEx; 061 } 062 } 063 } 064 }; 065 066 /** 067 * Gets an {@link Iterable} of the JVMCI providers available for a given service. 068 * 069 * @throws SecurityException if a security manager is present and it denies 070 * <tt>{@link RuntimePermission}("jvmciServices")</tt> 071 */ 072 @SuppressWarnings("unchecked") 073 @CallerSensitive 074 public static <S> Iterable<S> load(Class<S> service) { 075 SecurityManager sm = System.getSecurityManager(); 076 if (sm != null) { 077 sm.checkPermission(new RuntimePermission("jvmciServices")); 078 } 079 try { 080 return (Iterable<S>) cache.get(service); 081 } catch (UnsatisfiedLinkError e) { 082 return Collections.emptyList(); 083 } 084 } 085 086 /** 087 * Gets the JVMCI provider for a given service for which at most one provider must be available. 088 * 089 * @param service the service whose provider is being requested 090 * @param required specifies if an {@link InternalError} should be thrown if no provider of 091 * {@code service} is available 092 * @throws SecurityException if a security manager is present and it denies 093 * <tt>{@link RuntimePermission}("jvmciServices")</tt> 094 */ 095 @SuppressWarnings({"unchecked"}) 096 @CallerSensitive 097 public static <S> S loadSingle(Class<S> service, boolean required) { 098 SecurityManager sm = System.getSecurityManager(); 099 if (sm != null) { 100 sm.checkPermission(new RuntimePermission("jvmciServices")); 101 } 102 Iterable<S> providers; 103 try { 104 providers = (Iterable<S>) cache.get(service); 105 } catch (UnsatisfiedLinkError e) { 106 providers = Collections.emptyList(); 107 } 108 109 S singleProvider = null; 110 for (S provider : providers) { 111 if (singleProvider != null) { 112 throw new InternalError(String.format("Multiple %s providers found: %s, %s", service.getName(), singleProvider.getClass().getName(), provider.getClass().getName())); 113 } 114 singleProvider = provider; 115 } 116 if (singleProvider == null && required) { 117 String javaHome = System.getProperty("java.home"); 118 String vmName = System.getProperty("java.vm.name"); 119 Formatter errorMessage = new Formatter(); 120 errorMessage.format("The VM does not expose required service %s.%n", service.getName()); 121 errorMessage.format("Currently used Java home directory is %s.%n", javaHome); 122 errorMessage.format("Currently used VM configuration is: %s", vmName); 123 throw new UnsupportedOperationException(errorMessage.toString()); 124 } 125 return singleProvider; 126 } 127 128 static { 129 Reflection.registerMethodsToFilter(Services.class, "getServiceImpls"); 130 Reflection.registerFieldsToFilter(Services.class, "cache"); 131 } 132 133 private static native <S> S[] getServiceImpls(Class<?> service); 134}