1
\$\begingroup\$

My goal was to create a Java service on Windows that runs locally and communicates with local programs using JSON. The service should be reliable and robust.

The service runs a ServerSocket and receives receives and responds to JSON messages. The following is a skeleton, where appropriate "commands" will be added to the SigningController class. The service will likely stay small-scale, and will contain ~10 different commands.

Questions

Is the service reliable (i.e. no hangups, resource leaks etc)?

Critique of the overall design/implementation is welcome.

The service is installed with nssm and the bat files

install.bat (as administrator)

@echo off
pushd %~dp0
nssm.exe stop JavaSigningService
nssm.exe remove JavaSigningService confirm
nssm.exe install JavaSigningService %~dp0start.bat
nssm.exe start JavaSigningService
popd
pause

start.bat

java -Xmx512m -jar signingservice.jar

POM file and classes below.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <mainClass>Server.Main</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <finalName>signingservice</finalName>
                    <appendAssemblyId>false</appendAssemblyId>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <modelVersion>4.0.0</modelVersion>
    <groupId>...</groupId>
    <artifactId>signingservice</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>
    </dependencies>

</project>

Main

package Server;

import java.net.ServerSocket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {

    public static void main(String[] args) {
        int port = Integer.parseInt(args[0]);
        try (ServerSocket listener = new ServerSocket(port)) {
            System.out.println("Server is running...");
            ExecutorService pool = Executors.newFixedThreadPool(10);
            while (true) {
                try {
                    pool.execute(new Signer(listener.accept()));
                } catch (Exception e){
                    // Log
                }
            }
        } catch (Exception e) {
            // Log
        } finally {
            System.exit(0);
        }
    }
}

Utils

package Server;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.PrintWriter;
import java.io.StringWriter;

public class Utils {
    private static JsonParser parser = new JsonParser();

    public static JsonObject getJsonFromString(String str) throws Exception{
        JsonObject jsonObject = parser.parse(str).getAsJsonObject();
        return jsonObject;
    }
    public static String getJsonProperty(JsonObject json, String property){
        String value = null;
        try {
            value = json.get(property).getAsString();
        } catch (Exception e) {}
        return value;
    }

    public static JsonObject errorMessageTemplate(Exception e){
        JsonObject result = new JsonObject();
        result.addProperty("result", "ERROR");
        result.addProperty("errormessage", e.getMessage());
        result.addProperty("stacktrace", stackTraceAsString(e));
        return result;
    }

    public static String stackTraceAsString(Exception e){
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        return sw.toString();
    }
}

SigningController

package Server;

import com.google.gson.JsonObject;

public class SigningController {
    public static JsonObject handle(JsonObject received) throws Exception{
        String command = Utils.getJsonProperty(received, "command");
        if("echo".equals(command)){
            return simpleEcho(received);
        }
        return Utils.errorMessageTemplate(new Exception("Command not specified or unknown."));
    }

public static JsonObject simpleEcho(JsonObject received) throws Exception{
    String data = "" + Utils.getJsonProperty(received, "data");
    JsonObject result = new JsonObject();
    result.addProperty("result", "OK");
    result.addProperty("data", data);
    return result;
}
}

Signer

package Server;

import com.google.gson.JsonObject;
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class Signer implements Runnable{
    private Socket socket;
    private BufferedReader socketIn;
    private PrintWriter socketOut;

    Signer(Socket socket) throws IOException {
        this.socket = socket;
        this.socket.setSoTimeout(60*1000); // read timeout in milliseconds
        socketIn = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
        socketOut = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true);
    }

    @Override
    public void run() {
        System.out.println("Connected: " + socket);
        try {
            socketOut.println("hello");
            String line = socketIn.readLine();
            JsonObject json = Utils.getJsonFromString(line);
            JsonObject result = SigningController.handle(json);
            socketOut.println(result);
        } catch (Exception e){
            // Log
            System.out.println("Error: " + socket);
            e.printStackTrace();
            try {
                socketOut.println(Utils.errorMessageTemplate(e));
            } catch (Exception e1) {}
        } finally{
            // Cleanup
            try{
                socketIn.close();
            } catch (IOException e){
                e.printStackTrace();
            }
            try{
                socketOut.close();
            } catch (Exception e){
                e.printStackTrace();
            }
            try{
                socket.close();
                System.out.println("Closed: " + socket);
            } catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}
\$\endgroup\$
  • 3
    \$\begingroup\$ I appreciate the well formatted post, but unfortunately the questions you present are off topic in code review. \$\endgroup\$ – TorbenPutkonen Apr 15 at 11:36
  • \$\begingroup\$ Except for the Maybe Off-topic paragraph this is a well asked question. How to questions are more for our sister website stackoverflow.com. Could you perhaps ask it there instead? \$\endgroup\$ – pacmaninbw Apr 16 at 12:50
  • 1
    \$\begingroup\$ The question might need to be rephrased a little, but in essence this is perfectly reviewable. There's a memory concern, ok, we can deal with memory/performance/readability concerns just fine. user3050748, please take a look at our FAQ on How to get the best value out of Code Review - Asking Questions \$\endgroup\$ – Mast Apr 17 at 11:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.