diff --git a/block-explorer.html b/block-explorer.html
index 4e724fb4..6048e391 100644
--- a/block-explorer.html
+++ b/block-explorer.html
@@ -88,7 +88,7 @@
document.body.innerHTML = html;
XHR({
- url: "http://localhost:9085/transactions/address/" + address,
+ url: "/transactions/address/" + address,
onload: renderAddressTransactions,
responseType: "json"
});
@@ -96,7 +96,7 @@
function fetchAddressInfo(address) {
XHR({
- url: "http://localhost:9085/addresses/assets/" + address,
+ url: "/addresses/assets/" + address,
onload: renderAddressInfo,
responseType: "json"
});
@@ -181,7 +181,7 @@
// Fetch block's transactions
XHR({
- url: "http://localhost:9085/transactions/block/" + blockData.signature,
+ url: "/transactions/block/" + blockData.signature,
onload: renderBlockTransactions,
responseType: "json"
});
@@ -189,7 +189,7 @@
function fetchBlockInfo(height) {
XHR({
- url: "http://localhost:9085/blocks/byheight/" + height,
+ url: "/blocks/byheight/" + height,
onload: renderBlockInfo,
responseType: "json"
});
@@ -227,7 +227,7 @@
function shutdownAPI() {
XHR({
- url: "http://localhost:9085/admin/stop",
+ url: "/admin/stop",
onload: showShutdown,
responseType: "json"
});
@@ -247,7 +247,7 @@
for (var h = height; h > 0 && h >= height - 20; --h)
XHR({
- url: "http://localhost:9085/blocks/byheight/" + h,
+ url: "/blocks/byheight/" + h,
onload: listBlock,
responseType: "json"
});
@@ -264,7 +264,7 @@
function windowOnLoad() {
XHR({
- url: "http://localhost:9085/blocks/height",
+ url: "/blocks/height",
onload: initialBlocks,
responseType: "json"
});
diff --git a/pom.xml b/pom.xml
index 4ce392db..7a781439 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,6 +4,7 @@
org.qora
qora-core
2.0.0-SNAPSHOT
+ jar
UTF-8
3.19.0
@@ -20,36 +21,6 @@
1.8
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
- package
-
- single
-
-
-
-
-
- Start
-
- true
-
-
- . ..
-
-
-
- jar-with-dependencies
-
- Qora
- false
-
-
-
-
maven-dependency-plugin
@@ -118,6 +89,63 @@
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.0.2
+
+
+
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.4.3
+
+ false
+
+
+
+ org.webjars:swagger-ui
+
+
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+
+
+
+ package
+
+ shade
+
+
+
+
+
+ controller.Controller
+
+ . ..
+
+
+
+
+
+
+
diff --git a/settings.json b/settings.json
new file mode 100644
index 00000000..e60b3697
--- /dev/null
+++ b/settings.json
@@ -0,0 +1,6 @@
+{
+ "rpcallowed": [
+ "::/0",
+ "0.0.0.0/0"
+ ]
+}
diff --git a/src/Start.java b/src/Start.java
deleted file mode 100644
index 637899a6..00000000
--- a/src/Start.java
+++ /dev/null
@@ -1,25 +0,0 @@
-
-import api.ApiService;
-import repository.DataException;
-import repository.RepositoryFactory;
-import repository.RepositoryManager;
-import repository.hsqldb.HSQLDBRepositoryFactory;
-
-public class Start {
-
- private static final String connectionUrl = "jdbc:hsqldb:file:db/test;create=true";
-
- public static void main(String args[]) throws DataException {
- RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
- RepositoryManager.setRepositoryFactory(repositoryFactory);
-
- ApiService apiService = ApiService.getInstance();
- apiService.start();
-
- //// testing the API client
- //ApiClient client = ApiClient.getInstance();
- //String test = client.executeCommand("GET blocks/first");
- //System.out.println(test);
- }
-
-}
diff --git a/src/api/AdminResource.java b/src/api/AdminResource.java
index 6b904ff5..0f8a006d 100644
--- a/src/api/AdminResource.java
+++ b/src/api/AdminResource.java
@@ -15,8 +15,8 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
-import repository.DataException;
-import repository.RepositoryManager;
+
+import controller.Controller;
@Path("admin")
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
@@ -95,20 +95,14 @@ public class AdminResource {
public String shutdown() {
Security.checkApiCallAllowed("GET admin/stop", request);
- try {
- RepositoryManager.closeRepositoryFactory();
- } catch (DataException e) {
- e.printStackTrace();
- }
-
new Thread(new Runnable() {
@Override
public void run() {
- ApiService.getInstance().stop();
+ Controller.shutdown();
}
- }).start();
+ }); // disabled for now: .start();
- return "true";
+ return "false";
}
}
diff --git a/src/api/ApiService.java b/src/api/ApiService.java
index c9e521b6..f68453e0 100644
--- a/src/api/ApiService.java
+++ b/src/api/ApiService.java
@@ -2,7 +2,6 @@ package api;
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
-import java.io.File;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jetty.rewrite.handler.RedirectPatternRule;
@@ -26,32 +25,33 @@ public class ApiService {
private final Set> resources;
public ApiService() {
- // resources to register
+ // Resources to register
this.resources = new HashSet>();
this.resources.add(AddressesResource.class);
this.resources.add(AdminResource.class);
this.resources.add(BlocksResource.class);
this.resources.add(TransactionsResource.class);
+ this.resources.add(BlockExplorerResource.class);
this.resources.add(OpenApiResource.class); // swagger
this.resources.add(ApiDefinition.class); // for API definition
this.resources.add(AnnotationPostProcessor.class); // for API resource annotations
ResourceConfig config = new ResourceConfig(this.resources);
- // create RPC server
+ // Create RPC server
this.server = new Server(Settings.getInstance().getRpcPort());
- // whitelist
+ // IP address based access control
InetAccessHandler accessHandler = new InetAccessHandler();
for (String pattern : Settings.getInstance().getRpcAllowed()) {
accessHandler.include(pattern);
}
this.server.setHandler(accessHandler);
- // url rewriting
+ // URL rewriting
RewriteHandler rewriteHandler = new RewriteHandler();
accessHandler.setHandler(rewriteHandler);
- // context
+ // Context
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
context.setContextPath("/");
rewriteHandler.setHandler(context);
@@ -69,9 +69,8 @@ public class ApiService {
// Swagger-UI static content
ClassLoader loader = this.getClass().getClassLoader();
- File swaggerUIResourceLocation = new File(loader.getResource("resources/swagger-ui/").getFile());
ServletHolder swaggerUIServlet = new ServletHolder("static-swagger-ui", DefaultServlet.class);
- swaggerUIServlet.setInitParameter("resourceBase", swaggerUIResourceLocation.getAbsolutePath());
+ swaggerUIServlet.setInitParameter("resourceBase", loader.getResource("resources/swagger-ui/").toString());
swaggerUIServlet.setInitParameter("dirAllowed", "true");
swaggerUIServlet.setInitParameter("pathInfoOnly", "true");
context.addServlet(swaggerUIServlet, "/api-documentation/*");
@@ -96,19 +95,20 @@ public class ApiService {
public void start() {
try {
- // START RPC
+ // Start server
server.start();
} catch (Exception e) {
- // FAILED TO START RPC
+ // Failed to start
+ throw new RuntimeException("Failed to start API", e);
}
}
public void stop() {
try {
- // STOP RPC
+ // Stop server
server.stop();
} catch (Exception e) {
- // FAILED TO STOP RPC
+ // Failed to stop
}
}
}
diff --git a/src/api/BlockExplorerResource.java b/src/api/BlockExplorerResource.java
new file mode 100644
index 00000000..e8720c11
--- /dev/null
+++ b/src/api/BlockExplorerResource.java
@@ -0,0 +1,35 @@
+package api;
+
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+
+@Path("/")
+@Produces({ MediaType.TEXT_HTML })
+public class BlockExplorerResource {
+
+ @Context
+ HttpServletRequest request;
+
+ public BlockExplorerResource() {
+ }
+
+ @GET
+ @Path("/block-explorer.html")
+ public String getBlockExplorer() {
+ try {
+ byte[] htmlBytes = Files.readAllBytes(FileSystems.getDefault().getPath("block-explorer.html"));
+ return new String(htmlBytes, "UTF-8");
+ } catch (IOException e) {
+ return "block-explorer.html not found";
+ }
+ }
+
+}
diff --git a/src/controller/Controller.java b/src/controller/Controller.java
new file mode 100644
index 00000000..37c01250
--- /dev/null
+++ b/src/controller/Controller.java
@@ -0,0 +1,59 @@
+package controller;
+
+import api.ApiService;
+import repository.DataException;
+import repository.RepositoryFactory;
+import repository.RepositoryManager;
+import repository.hsqldb.HSQLDBRepositoryFactory;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+public class Controller {
+
+ private static final Logger LOGGER = LogManager.getLogger(Controller.class);
+
+ private static final String connectionUrl = "jdbc:hsqldb:file:db/test;create=true";
+ private static final Object shutdownLock = new Object();
+ private static boolean isStopping = false;
+
+ public static void main(String args[]) throws DataException {
+ LOGGER.info("Starting up...");
+
+ LOGGER.info("Starting repository");
+ RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
+ RepositoryManager.setRepositoryFactory(repositoryFactory);
+
+ LOGGER.info("Starting API");
+ ApiService apiService = ApiService.getInstance();
+ apiService.start();
+
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ Controller.shutdown();
+ }
+ });
+ }
+
+ public static void shutdown() {
+ synchronized (shutdownLock) {
+ if (!isStopping) {
+ isStopping = true;
+
+ LOGGER.info("Shutting down API");
+ ApiService.getInstance().stop();
+
+ try {
+ LOGGER.info("Shutting down repository");
+ RepositoryManager.closeRepositoryFactory();
+ } catch (DataException e) {
+ e.printStackTrace();
+ }
+
+ LOGGER.info("Shutdown complete!");
+ }
+ }
+ }
+
+}