View Javadoc
1   /*
2    * Copyright 2020-2023 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.demo;
6   
7   import java.io.BufferedOutputStream;
8   import java.io.BufferedReader;
9   import java.io.IOException;
10  import java.io.InputStreamReader;
11  import java.io.OutputStreamWriter;
12  import java.io.PrintWriter;
13  import java.net.ServerSocket;
14  import java.net.Socket;
15  import java.nio.charset.StandardCharsets;
16  import java.time.Instant;
17  import java.util.Locale;
18  import java.util.StringTokenizer;
19  
20  import org.slf4j.Logger;
21  import org.slf4j.LoggerFactory;
22  
23  import com.fasterxml.jackson.databind.ObjectMapper;
24  
25  import oshi.SystemInfo;
26  
27  /**
28   * Demo class to vend OSHI JSON data via an HTTP Webserver
29   * <p>
30   * This is for demonstration only not to be used in production code.
31   * <p>
32   * Code based on tutorial found on SSaurel's Blog : https://www.ssaurel.com/blog/create-a-simple-http-web-server-in-java
33   * Each Client Connection will be managed in a dedicated Thread
34   */
35  public class OshiHTTPServer implements Runnable {
36      // port to listen connection
37      private static final int PORT = 8080;
38  
39      private static final Logger logger = LoggerFactory.getLogger(OshiHTTPServer.class);
40  
41      // Client Connection via Socket Class
42      private Socket connect;
43  
44      public OshiHTTPServer(Socket c) {
45          connect = c;
46          logger.debug("Connecton opened.");
47      }
48  
49      public static void main(String[] args) {
50          try (ServerSocket serverConnect = new ServerSocket(PORT)) {
51              logger.info("Server started. Listening for connections on port {}", PORT);
52  
53              // we listen until user halts server execution
54              while (true) { // NOSONAR squid:S2189
55                  OshiHTTPServer myServer = new OshiHTTPServer(serverConnect.accept());
56  
57                  // create dedicated thread to manage the client connection
58                  Thread thread = new Thread(myServer);
59                  thread.start();
60              }
61          } catch (IOException e) {
62              logger.error("Server Connection error: {}", e.getMessage());
63          }
64      }
65  
66      @Override
67      public void run() {
68          try ( // read characters from the client via input stream on the socket
69                  BufferedReader in = new BufferedReader(
70                          new InputStreamReader(connect.getInputStream(), StandardCharsets.UTF_8));
71                  // get character output stream to client (for headers)
72                  PrintWriter out = new PrintWriter(
73                          new OutputStreamWriter(connect.getOutputStream(), StandardCharsets.UTF_8));
74                  // get binary output stream to client (for requested data)
75                  BufferedOutputStream dataOut = new BufferedOutputStream(connect.getOutputStream())) {
76  
77              // get first line of the request from the client
78              String input = in.readLine();
79              if (input == null) {
80                  throw new IOException("No characters read from input stream.");
81              }
82              // we parse the request with a string tokenizer
83              StringTokenizer parse = new StringTokenizer(input);
84              String method = parse.nextToken().toUpperCase(Locale.ROOT); // we get the HTTP method of the client
85              // we get fields requested
86              String fileRequested = parse.nextToken().toLowerCase(Locale.ROOT);
87  
88              // we support only GET and HEAD methods, we check
89              if (!method.equals("GET") && !method.equals("HEAD")) {
90                  logger.debug("501 Not Implemented: {}", method);
91                  String contentMimeType = "text/html";
92  
93                  // we send HTTP Headers with data to client
94                  out.println("HTTP/1.1 501 Not Implemented");
95                  out.println("Server: OSHI HTTP Server");
96                  out.println("Date: " + Instant.now());
97                  out.println("Content-type: " + contentMimeType);
98                  out.println("Content-length: " + 0);
99                  out.println(); // blank line between headers and content, very important !
100                 out.flush(); // flush character output stream buffer
101 
102                 // Could return other information here...
103             } else {
104                 // Possibly could use the fileRequested value from user input to work down
105                 // OSHI's JSON tree and only return the relevant info instead of the entire
106                 // SystemInfo object.
107                 SystemInfo si = new SystemInfo();
108                 ObjectMapper mapper = new ObjectMapper();
109                 byte[] content = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(si)
110                         .getBytes(StandardCharsets.UTF_8);
111 
112                 if (method.equals("GET")) { // GET method so we return content
113                     // send HTTP Headers
114                     out.println("HTTP/1.1 200 OK");
115                     out.println("Server: OSHI HTTP Server");
116                     out.println("Date: " + Instant.now());
117                     out.println("Content-type: application/json");
118                     out.println("Content-length: " + content.length);
119                     out.println(); // blank line between headers and content, very important !
120                     out.flush(); // flush character output stream buffer
121 
122                     dataOut.write(content, 0, content.length);
123                     dataOut.flush();
124                 }
125 
126                 logger.debug("Data {} returned", fileRequested);
127             }
128         } catch (IOException ioe) {
129             logger.error("Server error: {}", ioe.getMessage());
130         } finally {
131             try {
132                 // close socket connection, defined for this thread
133                 connect.close();
134             } catch (Exception e) {
135                 logger.error("Error closing connection: {}", e.getMessage());
136             }
137             logger.debug("Connection closed.");
138         }
139     }
140 }