diff --git a/core/src/main/java/io/grpc/ManagedChannelBuilder.java b/core/src/main/java/io/grpc/ManagedChannelBuilder.java index dda4f8f2e5..cf3ef38ac7 100644 --- a/core/src/main/java/io/grpc/ManagedChannelBuilder.java +++ b/core/src/main/java/io/grpc/ManagedChannelBuilder.java @@ -31,6 +31,8 @@ package io.grpc; +import io.grpc.ManagedChannelProvider; + import java.util.List; import java.util.concurrent.Executor; @@ -40,6 +42,9 @@ import java.util.concurrent.Executor; * @param The concrete type of this builder. */ public abstract class ManagedChannelBuilder> { + public static ManagedChannelBuilder forAddress(String name, int port) { + return ManagedChannelProvider.provider().builderForAddress(name, port); + } /** * Provides a custom executor. diff --git a/core/src/main/java/io/grpc/ManagedChannelProvider.java b/core/src/main/java/io/grpc/ManagedChannelProvider.java new file mode 100644 index 0000000000..6eba28e5fe --- /dev/null +++ b/core/src/main/java/io/grpc/ManagedChannelProvider.java @@ -0,0 +1,114 @@ +/* + * Copyright 2015, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc; + +import com.google.common.annotations.VisibleForTesting; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.ServiceLoader; + +/** + * Provider of managed channels for transport agnostic consumption. + * + *

Implementations should not throw. If they do, it may interrupt class loading. If + * exceptions may reasonably occur for implementation-specific reasons, implementations should + * generally handle the exception gracefully and return {@code false} from {@link #isAvailable()}. + */ +@Internal +public abstract class ManagedChannelProvider { + private static final ManagedChannelProvider provider + = load(Thread.currentThread().getContextClassLoader()); + + @VisibleForTesting + static ManagedChannelProvider load(ClassLoader classLoader) { + ServiceLoader providers + = ServiceLoader.load(ManagedChannelProvider.class, classLoader); + List list = new ArrayList(); + for (ManagedChannelProvider current : providers) { + if (!current.isAvailable()) { + continue; + } + list.add(current); + } + if (list.isEmpty()) { + return null; + } else { + return Collections.max(list, new Comparator() { + @Override + public int compare(ManagedChannelProvider f1, ManagedChannelProvider f2) { + return f1.priority() - f2.priority(); + } + }); + } + } + + /** + * Returns the ClassLoader-wide default channel. + * + * @throws ProviderNotFoundException if no provider is available + */ + public static ManagedChannelProvider provider() { + if (provider == null) { + throw new ProviderNotFoundException("No functional channel service provider found. " + + "Try adding a dependency on the grpc-okhttp or grpc-netty artifact"); + } + return provider; + } + + /** + * Whether this provider is available for use, taking the current environment into consideration. + * If {@code false}, no other methods are safe to be called. + */ + protected abstract boolean isAvailable(); + + /** + * A priority, from 0 to 10 that this provider should be used, taking the current environment into + * consideration. 5 should be considered the default, and then tweaked based on environment + * detection. A priority of 0 does not imply that the provider wouldn't work; just that it should + * be last in line. + */ + protected abstract int priority(); + + /** + * Creates a new builder with the given host and port. + */ + protected abstract ManagedChannelBuilder builderForAddress(String name, int port); + + public static final class ProviderNotFoundException extends RuntimeException { + public ProviderNotFoundException(String msg) { + super(msg); + } + } +} diff --git a/core/src/test/java/io/grpc/ManagedChannelProviderTest.java b/core/src/test/java/io/grpc/ManagedChannelProviderTest.java new file mode 100644 index 0000000000..f228499ea5 --- /dev/null +++ b/core/src/test/java/io/grpc/ManagedChannelProviderTest.java @@ -0,0 +1,146 @@ +/* + * Copyright 2015, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +/** Unit tests for {@link ManagedChannelProvider}. */ +@RunWith(JUnit4.class) +public class ManagedChannelProviderTest { + @Test(expected = ManagedChannelProvider.ProviderNotFoundException.class) + public void noProvider() { + ManagedChannelProvider.provider(); + } + + @Test + public void multipleProvider() { + ClassLoader cl = new ServicesClassLoader(getClass().getClassLoader(), + "io/grpc/ManagedChannelProviderTest-multipleProvider.txt"); + assertSame(Available7Provider.class, ManagedChannelProvider.load(cl).getClass()); + } + + @Test + public void unavailableProvider() { + ClassLoader cl = new ServicesClassLoader(getClass().getClassLoader(), + "io/grpc/ManagedChannelProviderTest-unavailableProvider.txt"); + assertNull(ManagedChannelProvider.load(cl)); + } + + private static class ServicesClassLoader extends ClassLoader { + private final String serviceFile = "META-INF/services/io.grpc.ManagedChannelProvider"; + private final String resourceName; + + public ServicesClassLoader(ClassLoader parent, String resourceName) { + super(parent); + this.resourceName = resourceName; + } + + @Override + protected URL findResource(String name) { + if (serviceFile.equals(name)) { + return getParent().getResource(resourceName); + } + return super.findResource(name); + } + + @Override + protected Enumeration findResources(String name) throws IOException { + if (serviceFile.equals(name)) { + return getParent().getResources(resourceName); + } + return super.findResources(name); + } + } + + private static class BaseProvider extends ManagedChannelProvider { + private final boolean isAvailable; + private final int priority; + + public BaseProvider(boolean isAvailable, int priority) { + this.isAvailable = isAvailable; + this.priority = priority; + } + + @Override + protected boolean isAvailable() { + return isAvailable; + } + + @Override + protected int priority() { + return priority; + } + + @Override + protected ManagedChannelBuilder builderForAddress(String host, int port) { + throw new UnsupportedOperationException(); + } + } + + public static class Available0Provider extends BaseProvider { + public Available0Provider() { + super(true, 0); + } + } + + public static class Available5Provider extends BaseProvider { + public Available5Provider() { + super(true, 5); + } + } + + public static class Available7Provider extends BaseProvider { + public Available7Provider() { + super(true, 7); + } + } + + public static class UnavailableProvider extends BaseProvider { + public UnavailableProvider() { + super(false, 10); + } + + @Override + protected int priority() { + throw new RuntimeException("purposefully broken"); + } + } +} diff --git a/core/src/test/resources/io/grpc/ManagedChannelProviderTest-multipleProvider.txt b/core/src/test/resources/io/grpc/ManagedChannelProviderTest-multipleProvider.txt new file mode 100644 index 0000000000..e048430544 --- /dev/null +++ b/core/src/test/resources/io/grpc/ManagedChannelProviderTest-multipleProvider.txt @@ -0,0 +1,3 @@ +io.grpc.ManagedChannelProviderTest$Available5Provider +io.grpc.ManagedChannelProviderTest$Available7Provider +io.grpc.ManagedChannelProviderTest$Available0Provider diff --git a/core/src/test/resources/io/grpc/ManagedChannelProviderTest-unavailableProvider.txt b/core/src/test/resources/io/grpc/ManagedChannelProviderTest-unavailableProvider.txt new file mode 100644 index 0000000000..b24a346532 --- /dev/null +++ b/core/src/test/resources/io/grpc/ManagedChannelProviderTest-unavailableProvider.txt @@ -0,0 +1 @@ +io.grpc.ManagedChannelProviderTest$UnavailableProvider diff --git a/netty/src/main/java/io/grpc/netty/NettyChannelProvider.java b/netty/src/main/java/io/grpc/netty/NettyChannelProvider.java new file mode 100644 index 0000000000..628f803a92 --- /dev/null +++ b/netty/src/main/java/io/grpc/netty/NettyChannelProvider.java @@ -0,0 +1,54 @@ +/* + * Copyright 2015, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.netty; + +import io.grpc.Internal; +import io.grpc.ManagedChannelProvider; + +/** Provider for {@link NettyChannelBuilder} instances. */ +@Internal +public class NettyChannelProvider extends ManagedChannelProvider { + @Override + protected boolean isAvailable() { + return true; + } + + @Override + protected int priority() { + return 5; + } + + @Override + protected NettyChannelBuilder builderForAddress(String name, int port) { + return NettyChannelBuilder.forAddress(name, port); + } +} diff --git a/netty/src/main/resources/META-INF/services/io.grpc.ManagedChannelProvider b/netty/src/main/resources/META-INF/services/io.grpc.ManagedChannelProvider new file mode 100644 index 0000000000..ebd1bcdf02 --- /dev/null +++ b/netty/src/main/resources/META-INF/services/io.grpc.ManagedChannelProvider @@ -0,0 +1 @@ +io.grpc.netty.NettyChannelProvider diff --git a/netty/src/test/java/io/grpc/netty/NettyChannelProviderTest.java b/netty/src/test/java/io/grpc/netty/NettyChannelProviderTest.java new file mode 100644 index 0000000000..87f181ed49 --- /dev/null +++ b/netty/src/test/java/io/grpc/netty/NettyChannelProviderTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2015, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.netty; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import io.grpc.ManagedChannelProvider; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link NettyChannelProvider}. */ +@RunWith(JUnit4.class) +public class NettyChannelProviderTest { + private NettyChannelProvider provider = new NettyChannelProvider(); + + @Test + public void provided() { + assertSame(NettyChannelProvider.class, ManagedChannelProvider.provider().getClass()); + } + + @Test + public void basicMethods() { + assertTrue(provider.isAvailable()); + assertEquals(5, provider.priority()); + } + + @Test + public void builderIsANettyBuilder() { + assertSame(NettyChannelBuilder.class, provider.builderForAddress("localhost", 443).getClass()); + } +} diff --git a/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelProvider.java b/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelProvider.java new file mode 100644 index 0000000000..c915dcc1ea --- /dev/null +++ b/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelProvider.java @@ -0,0 +1,64 @@ +/* + * Copyright 2015, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.okhttp; + +import io.grpc.Internal; +import io.grpc.ManagedChannelProvider; + +/** Provider for {@link OkHttpChannelBuilder} instances. */ +@Internal +public class OkHttpChannelProvider extends ManagedChannelProvider { + @Override + protected boolean isAvailable() { + return true; + } + + @Override + protected int priority() { + return isAndroid() ? 8 : 3; + } + + private static boolean isAndroid() { + try { + Class.forName("android.app.Application", /*initialize=*/ false, null); + return true; + } catch (Exception e) { + // If Application isn't loaded, it might as well not be Android. + return false; + } + } + + @Override + protected OkHttpChannelBuilder builderForAddress(String name, int port) { + return OkHttpChannelBuilder.forAddress(name, port); + } +} diff --git a/okhttp/src/main/resources/META-INF/services/io.grpc.ManagedChannelProvider b/okhttp/src/main/resources/META-INF/services/io.grpc.ManagedChannelProvider new file mode 100644 index 0000000000..8dea07f01b --- /dev/null +++ b/okhttp/src/main/resources/META-INF/services/io.grpc.ManagedChannelProvider @@ -0,0 +1 @@ +io.grpc.okhttp.OkHttpChannelProvider diff --git a/okhttp/src/test/java/io/grpc/okhttp/OkHttpChannelProviderTest.java b/okhttp/src/test/java/io/grpc/okhttp/OkHttpChannelProviderTest.java new file mode 100644 index 0000000000..1f14516d7c --- /dev/null +++ b/okhttp/src/test/java/io/grpc/okhttp/OkHttpChannelProviderTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.okhttp; + +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import io.grpc.ManagedChannelProvider; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link OkHttpChannelProvider}. */ +@RunWith(JUnit4.class) +public class OkHttpChannelProviderTest { + private OkHttpChannelProvider provider = new OkHttpChannelProvider(); + + @Test + public void provided() { + assertSame(OkHttpChannelProvider.class, ManagedChannelProvider.provider().getClass()); + } + + @Test + public void isAvailable() { + assertTrue(provider.isAvailable()); + } + + @Test + public void builderIsAOkHttpBuilder() { + assertSame(OkHttpChannelBuilder.class, provider.builderForAddress("localhost", 443).getClass()); + } +}