From 19daaae739bf3dc510a8d69e211e0ff7d84bf73e Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 20 Apr 2017 12:23:13 -0400 Subject: [PATCH] http-service: use guice for dependency injection --- http-service/pom.xml | 11 ++ .../net/runelite/http/service/Service.java | 100 ++++-------------- .../runelite/http/service/ServiceModule.java | 76 +++++++++++++ .../updatecheck/UpdateCheckService.java | 11 +- .../http/service/xtea/XteaService.java | 18 ++-- .../runelite/http/service/ServiceTest.java | 50 +++++---- 6 files changed, 152 insertions(+), 114 deletions(-) create mode 100644 http-service/src/main/java/net/runelite/http/service/ServiceModule.java diff --git a/http-service/pom.xml b/http-service/pom.xml index 69c2219786..55f65235d3 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -63,6 +63,11 @@ guava 21.0 + + com.google.inject + guice + 4.1.0 + org.slf4j @@ -87,6 +92,12 @@ 1.10.19 test + + com.google.inject.extensions + guice-testlib + 4.1.0 + test + diff --git a/http-service/src/main/java/net/runelite/http/service/Service.java b/http-service/src/main/java/net/runelite/http/service/Service.java index d7193f1527..f3c383df26 100644 --- a/http-service/src/main/java/net/runelite/http/service/Service.java +++ b/http-service/src/main/java/net/runelite/http/service/Service.java @@ -24,10 +24,8 @@ */ package net.runelite.http.service; -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NamingException; -import javax.sql.DataSource; +import com.google.inject.Guice; +import com.google.inject.Inject; import net.runelite.http.api.RuneliteAPI; import net.runelite.http.service.hiscore.HiscoreService; import net.runelite.http.service.updatecheck.UpdateCheckService; @@ -42,20 +40,29 @@ public class Service implements SparkApplication { private static final Logger logger = LoggerFactory.getLogger(Service.class); - private DataSource dataSource; - private final JsonTransformer transformer = new JsonTransformer(); - private HiscoreService hiscores = new HiscoreService(); - private UpdateCheckService updateCheck = new UpdateCheckService(this); - private WorldsService worlds = new WorldsService(); - private XteaService xtea = new XteaService(this); + @Inject + private HiscoreService hiscores; + + @Inject + private UpdateCheckService updateCheck; + + @Inject + private WorldsService worlds; + + @Inject + private XteaService xtea; @Override public void init() { - loadDatasource(); + Guice.createInjector(new ServiceModule(this)); + setupRoutes(); + } + public void setupRoutes() + { xtea.init(); get("/version", (request, response) -> RuneliteAPI.getVersion()); @@ -68,75 +75,4 @@ public class Service implements SparkApplication exception(Exception.class, (exception, request, response) -> logger.warn(null, exception)); } - private void loadDatasource() - { - if (dataSource != null) - { - return; // unit test? - } - - try - { - // It is difficult to inject things into Spark - Context initCtx = new InitialContext(); - Context envCtx = (Context) initCtx.lookup("java:comp/env"); - - dataSource = (DataSource) envCtx.lookup("jdbc/runelite"); - } - catch (NamingException ex) - { - throw new RuntimeException(ex); - } - } - - public HiscoreService getHiscores() - { - return hiscores; - } - - public void setHiscores(HiscoreService hiscores) - { - this.hiscores = hiscores; - } - - public UpdateCheckService getUpdateCheck() - { - return updateCheck; - } - - public void setUpdateCheck(UpdateCheckService updateCheck) - { - this.updateCheck = updateCheck; - } - - public WorldsService getWorlds() - { - return worlds; - } - - public void setWorlds(WorldsService worlds) - { - this.worlds = worlds; - } - - public XteaService getXtea() - { - return xtea; - } - - public void setXtea(XteaService xtea) - { - this.xtea = xtea; - } - - public DataSource getDataSource() - { - return dataSource; - } - - public void setDataSource(DataSource dataSource) - { - this.dataSource = dataSource; - } - } diff --git a/http-service/src/main/java/net/runelite/http/service/ServiceModule.java b/http-service/src/main/java/net/runelite/http/service/ServiceModule.java new file mode 100644 index 0000000000..fa572fe090 --- /dev/null +++ b/http-service/src/main/java/net/runelite/http/service/ServiceModule.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. 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. + * + * 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 net.runelite.http.service; + +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import com.google.inject.name.Named; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.sql.DataSource; +import net.runelite.http.service.hiscore.HiscoreService; +import net.runelite.http.service.updatecheck.UpdateCheckService; +import net.runelite.http.service.worlds.WorldsService; +import net.runelite.http.service.xtea.XteaService; + +public class ServiceModule extends AbstractModule +{ + private final Service service; + + public ServiceModule(Service service) + { + this.service = service; + } + + @Provides + @Named("Runelite JDBC") + DataSource provideDataSource() + { + try + { + // It is difficult to inject things into Spark + Context initCtx = new InitialContext(); + Context envCtx = (Context) initCtx.lookup("java:comp/env"); + + return (DataSource) envCtx.lookup("jdbc/runelite"); + } + catch (NamingException ex) + { + throw new RuntimeException(ex); + } + } + + @Override + protected void configure() + { + bind(Service.class).toInstance(service); + + bind(HiscoreService.class); + bind(UpdateCheckService.class); + bind(WorldsService.class); + bind(XteaService.class); + } +} diff --git a/http-service/src/main/java/net/runelite/http/service/updatecheck/UpdateCheckService.java b/http-service/src/main/java/net/runelite/http/service/updatecheck/UpdateCheckService.java index 1c75be2d6a..e0b58f0194 100644 --- a/http-service/src/main/java/net/runelite/http/service/updatecheck/UpdateCheckService.java +++ b/http-service/src/main/java/net/runelite/http/service/updatecheck/UpdateCheckService.java @@ -25,6 +25,7 @@ package net.runelite.http.service.updatecheck; import com.google.common.base.Suppliers; +import com.google.inject.Inject; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -38,7 +39,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import net.runelite.http.api.RuneliteAPI; import net.runelite.http.api.worlds.WorldResult; -import net.runelite.http.service.Service; import net.runelite.http.service.worlds.WorldsService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,12 +56,13 @@ public class UpdateCheckService private static final int RESPONSE_OK = 0; private static final int RESPONSE_OUTDATED = 6; - private final Service service; + private final WorldsService worldsService; private final Supplier updateAvailable = Suppliers.memoizeWithExpiration(this::checkUpdate, 1, TimeUnit.MINUTES); - public UpdateCheckService(Service service) + @Inject + public UpdateCheckService(WorldsService worldsService) { - this.service = service; + this.worldsService = worldsService; } public Boolean check(Request request, Response response) @@ -121,8 +122,6 @@ public class UpdateCheckService private int randomWorld() { - WorldsService worldsService = service.getWorlds(); - try { WorldResult worlds = worldsService.listWorlds(); diff --git a/http-service/src/main/java/net/runelite/http/service/xtea/XteaService.java b/http-service/src/main/java/net/runelite/http/service/xtea/XteaService.java index 4fa31cd453..3863ac02c3 100644 --- a/http-service/src/main/java/net/runelite/http/service/xtea/XteaService.java +++ b/http-service/src/main/java/net/runelite/http/service/xtea/XteaService.java @@ -25,11 +25,13 @@ package net.runelite.http.service.xtea; import com.google.gson.Gson; +import com.google.inject.Inject; +import com.google.inject.name.Named; import java.util.List; import java.util.stream.Collectors; +import javax.sql.DataSource; import net.runelite.http.api.xtea.XteaKey; import net.runelite.http.api.xtea.XteaRequest; -import net.runelite.http.service.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sql2o.Connection; @@ -52,17 +54,19 @@ public class XteaService + " PRIMARY KEY (`rev`,`region`,`key1`,`key2`,`key3`,`key4`)\n" + ") ENGINE=InnoDB;"; - private final Service service; + + private final DataSource datasource; private final Gson gson = new Gson(); - public XteaService(Service service) + @Inject + public XteaService(@Named("Runelite JDBC") DataSource datasource) { - this.service = service; + this.datasource = datasource; } public void init() { - Sql2o sql2o = new Sql2o(service.getDataSource()); + Sql2o sql2o = new Sql2o(datasource); try (Connection con = sql2o.beginTransaction()) { @@ -75,7 +79,7 @@ public class XteaService { XteaRequest xteaRequest = gson.fromJson(request.body(), XteaRequest.class); - Sql2o sql2o = new Sql2o(service.getDataSource()); + Sql2o sql2o = new Sql2o(datasource); try (Connection con = sql2o.beginTransaction()) { @@ -104,7 +108,7 @@ public class XteaService String revStr = request.params("rev"); int revision = Integer.parseInt(revStr); - Sql2o sql2o = new Sql2o(service.getDataSource()); + Sql2o sql2o = new Sql2o(datasource); try (Connection con = sql2o.open()) { diff --git a/http-service/src/test/java/net/runelite/http/service/ServiceTest.java b/http-service/src/test/java/net/runelite/http/service/ServiceTest.java index 1a5e3f0a3c..206a47ae59 100644 --- a/http-service/src/test/java/net/runelite/http/service/ServiceTest.java +++ b/http-service/src/test/java/net/runelite/http/service/ServiceTest.java @@ -25,43 +25,60 @@ package net.runelite.http.service; import com.google.gson.Gson; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.name.Named; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; -import java.sql.SQLException; import javax.sql.DataSource; import net.runelite.http.api.hiscore.HiscoreResult; import net.runelite.http.api.hiscore.Skill; import net.runelite.http.service.hiscore.HiscoreService; -import org.junit.AfterClass; +import org.junit.After; import org.junit.Assert; -import org.junit.BeforeClass; +import org.junit.Before; import org.junit.Test; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; -import static org.mockito.Mockito.mock; +import org.mockito.Answers; +import org.mockito.Mock; import static org.mockito.Mockito.when; +import org.mockito.MockitoAnnotations; import spark.Spark; public class ServiceTest { private static final String URL_BASE = "http://localhost:4567"; - private static Service service; + private Service service; - @BeforeClass - public static void before() throws SQLException + @Bind + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + @Named("Runelite JDBC") + private DataSource dataSource; + + @Bind + @Mock + private HiscoreService hiscoreService; + + @Before + public void before() { - DataSource dataSource = mock(DataSource.class, RETURNS_DEEP_STUBS); + // Init mocks first, else we're binding null objects + MockitoAnnotations.initMocks(this); + // Inject everything in the test object + Injector injector = Guice.createInjector(BoundFieldModule.of(this)); + injector.injectMembers(this); - service = new Service(); - service.setDataSource(dataSource); - service.init(); + service = injector.getInstance(Service.class); + service.setupRoutes(); Spark.awaitInitialization(); } - @AfterClass - public static void after() + @After + public void after() { Spark.stop(); } @@ -69,9 +86,6 @@ public class ServiceTest @Test public void testInit() throws Exception { - HiscoreService hiscoreService = mock(HiscoreService.class); - service.setHiscores(hiscoreService); - HiscoreResult result = new HiscoreResult(); result.setAttack(new Skill(1, 99, 42)); @@ -86,8 +100,6 @@ public class ServiceTest HiscoreResult res = gson.fromJson(new InputStreamReader(connection.getInputStream()), HiscoreResult.class); Assert.assertEquals(result, res); - - Spark.stop(); } }