1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package fr.cnes.doi.server;
20
21 import fr.cnes.doi.exception.ClientMdsException;
22 import java.io.BufferedReader;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.io.Reader;
29 import java.nio.charset.StandardCharsets;
30 import java.nio.file.Files;
31 import java.nio.file.Paths;
32 import java.util.stream.Collectors;
33
34 import org.apache.logging.log4j.LogManager;
35 import org.apache.logging.log4j.Logger;
36
37 import fr.cnes.doi.exception.DoiRuntimeException;
38 import fr.cnes.doi.security.TokenSecurity;
39 import fr.cnes.doi.security.UtilsCryptography;
40 import fr.cnes.doi.settings.Consts;
41 import fr.cnes.doi.settings.DoiSettings;
42 import fr.cnes.doi.utils.spec.Requirement;
43 import gnu.getopt.Getopt;
44 import gnu.getopt.LongOpt;
45 import java.nio.charset.Charset;
46
47
48
49
50
51
52 @Requirement(reqId = Requirement.DOI_DEV_010, reqName = Requirement.DOI_DEV_010_NAME)
53 @Requirement(reqId = Requirement.DOI_DEV_020, reqName = Requirement.DOI_DEV_020_NAME)
54 public final class Starter {
55
56
57
58
59 public static final int BITS_16 = 16;
60
61
62
63
64 private static final Logger LOG = LogManager.getLogger(Starter.class.getName());
65
66
67
68
69 private static DoiServer doiServer;
70
71 static {
72 final java.util.logging.Logger rootLogger = java.util.logging.LogManager.getLogManager()
73 .getLogger("");
74 final java.util.logging.Handler[] handlers = rootLogger.getHandlers();
75 rootLogger.removeHandler(handlers[0]);
76 }
77
78 private static void displayHelp() {
79 LOG.traceEntry();
80 final DoiSettings settings = DoiSettings.getInstance();
81 final StringBuilder help = new StringBuilder();
82 help.append("\n------------ Help for DOI Server -----------\n");
83 help.append("\n");
84 help.append("Usage: java -jar ").append(settings.getString(Consts.APP_NAME)).append("-")
85 .append(settings.getString(Consts.VERSION))
86 .append(".jar [--secret <key>] [OPTIONS] [-s]\n");
87 help.append("\n\n");
88 help.append("with :\n");
89 help.append(" --secret <key> : The 16 bits secret key to crypt/decrypt\n");
90 help.append(" --key-sign-secret <key> : The key to sign the token\n");
91 help.append(" If not provided, a default one is used\n");
92 help.append(" -s|--start : Starts the server\n");
93 help.append(" -t|--stop : Stops the server\n");
94 help.append(" -l|--status : Status of the server\n");
95 help.append("with OPTIONS:\n");
96 help.append(" -h|--help : This output\n");
97 help.append(" -k|--key-sign : Creates a key to sign JWT token\n");
98 help.append(" -c <string> : Crypts a string in the standard output\n");
99 help.append(" -e <string> : Decrypts a string in the standard output\n");
100 help.append(" -d : Displays the configuration file\n");
101 help.append(" -f <path> : Loads the configuation file\n");
102 help.append(
103 " -y|--cryptProperties <path> : crypts the properties file on the output standard\n");
104 help.append(
105 " -z|--decryptProperties <path>: Decrypts the properties on the output standard\n");
106 help.append(" -v|--version : DOI server version\n");
107 help.append("\n");
108 help.append("\n");
109 LOG.info(help.toString());
110 LOG.traceExit();
111 }
112
113
114
115
116
117
118 private static void stopServer(final Thread server) {
119 LOG.traceEntry();
120 try {
121 try {
122 doiServer.stop();
123 } catch (Exception ex) {
124 LOG.fatal("Unable to stop the server", ex);
125 } finally {
126 LOG.info("Interrups the server, which is stopping");
127 server.interrupt();
128 server.join();
129 }
130 } catch (InterruptedException e) {
131 LOG.fatal("Cannot interrupt the server", e);
132 }
133 LOG.traceExit();
134 }
135
136
137
138
139
140
141 @Requirement(reqId = Requirement.DOI_ARCHI_040, reqName = Requirement.DOI_ARCHI_040_NAME)
142 private static void startServer(final DoiServer server) {
143 final DoiSettings settings = DoiSettings.getInstance();
144 final String progName = settings.getString(Consts.APP_NAME) + "-" + settings.getString(
145 Consts.VERSION) + ".jar";
146 final String stopPid = getCurrentPid(progName);
147 if (stopPid == null) {
148 try {
149 server.start();
150 infoProject();
151 } catch (Exception ex) {
152 LOG.info("Unable to start the server");
153 }
154 } else {
155 LOG.info("The server is already started");
156 }
157 }
158
159
160
161
162 private static void infoProject() {
163 LOG.info("-------------------------------------------------");
164 LOG.info(" DOI-server project");
165 LOG.info("-------------------------------------------------");
166 LOG.info("Project : https://cnes.github.io/DOI-server");
167 LOG.info("Source : https://github.com/cnes/DOI-server");
168 LOG.info("Issues : https://github.com/CNES/DOI-server");
169 LOG.info("Copyright (C) 2017-2019 Centre National d'Etudes Spatiales (CNES)");
170 LOG.info("License : LGPLV3");
171
172 }
173
174
175
176
177
178 private static void stopServer() {
179 try {
180 final DoiSettings settings = DoiSettings.getInstance();
181 final String progName = settings.getString(Consts.APP_NAME) + "-" + settings.getString(
182 Consts.VERSION) + ".jar";
183 final String stopPid = getCurrentPid(progName);
184 if (stopPid != null) {
185 Runtime.getRuntime().exec("kill -9 " + stopPid);
186 LOG.info("Stopping the DOI server .... OK");
187 } else {
188 LOG.info("The DOI server is already stopped");
189 }
190 } catch (IOException e) {
191 LOG.info("Stopping the DOI server .... Failed");
192 }
193 }
194
195
196
197
198
199 private static void statusServer() {
200 final DoiSettings settings = DoiSettings.getInstance();
201 final String progName = settings.getString(Consts.APP_NAME) + "-" + settings.getString(
202 Consts.VERSION) + ".jar";
203 final String stopPid = getCurrentPid(progName);
204 if (stopPid != null) {
205 LOG.info("The DOI server is running with the pid {}", stopPid);
206 } else {
207 LOG.info("The DOI server is stopped");
208 }
209 }
210
211
212
213
214
215
216 private static void launchServer(final DoiSettings settings) {
217 LOG.traceEntry();
218 try {
219 settings.validConfigurationFile();
220 doiServer = new DoiServer(settings);
221 final Thread server = new Thread() {
222 @Override
223 public void run() {
224 startServer(doiServer);
225 }
226 };
227
228 Runtime.getRuntime().addShutdownHook(new Thread() {
229 @Override
230 public void run() {
231 LOG.info("interrupt received, killing server…");
232 stopServer(server);
233 }
234 });
235
236 server.start();
237 } catch (ClientMdsException| DoiRuntimeException ex) {
238 LOG.info("Error when starting the server: " + ex.getMessage());
239 }
240 LOG.traceExit();
241 }
242
243
244
245
246 private static void displayVersion() {
247 LOG.traceEntry();
248 final DoiSettings settings = DoiSettings.getInstance();
249 final String appName = settings.getString(Consts.APP_NAME);
250 final String version = settings.getString(Consts.VERSION);
251 final String copyright = settings.getString(Consts.COPYRIGHT);
252 LOG.info("{} ({}) - Version:{}\n", appName, copyright, version);
253 LOG.traceExit();
254 }
255
256
257
258
259
260
261
262 private static String getCurrentPid(final String serverName) {
263 LOG.traceEntry("Parameter\n\tserverName:{}", serverName);
264 String stopPid = null;
265 BufferedReader reader = null;
266 Process pro = null;
267 try {
268 pro = Runtime.getRuntime().exec("ps aux");
269 reader = new BufferedReader(new InputStreamReader(pro.
270 getInputStream(), Charset.defaultCharset()));
271 final String processName = java.lang.management.ManagementFactory.getRuntimeMXBean().
272 getName();
273 final String myPid = processName.split("@")[0];
274 String line;
275 while ((line = reader.readLine()) != null) {
276 if (line.contains(serverName) && line.contains("java")) {
277 final String[] split = line.split("\\s+");
278 final String currentPid = split[1].trim();
279 if (!currentPid.equals(myPid)) {
280 stopPid = currentPid;
281 break;
282 }
283 }
284 }
285 } catch (IOException ex) {
286 } finally {
287 if (reader != null) {
288 try {
289 reader.close();
290 } catch (IOException ex) {
291 }
292 }
293 if (pro != null) {
294 pro.destroy();
295 }
296 }
297 return LOG.traceExit(stopPid);
298 }
299
300
301
302
303
304
305 public static void main(final String[] argv) {
306 final DoiSettings settings = DoiSettings.getInstance();
307 final String progName = settings.getString(Consts.APP_NAME) + "-" + settings.getString(
308 Consts.VERSION) + ".jar";
309 final String progNameWithJar = "java -jar " + progName;
310
311 int c;
312 String arg;
313
314 final StringBuffer sb = new StringBuffer();
315 final LongOpt[] longopts = new LongOpt[10];
316 longopts[0] = new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h');
317 longopts[1] = new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'v');
318 longopts[2] = new LongOpt("secret", LongOpt.REQUIRED_ARGUMENT, sb, 0);
319 longopts[3] = new LongOpt("decryptProperties", LongOpt.REQUIRED_ARGUMENT, null, 'z');
320 longopts[4] = new LongOpt("cryptProperties", LongOpt.REQUIRED_ARGUMENT, null, 'y');
321 longopts[5] = new LongOpt("key-sign", LongOpt.NO_ARGUMENT, null, 'k');
322 longopts[6] = new LongOpt("key-sign-secret", LongOpt.REQUIRED_ARGUMENT, null, 'a');
323 longopts[7] = new LongOpt("start", LongOpt.NO_ARGUMENT, null, 's');
324 longopts[8] = new LongOpt("stop", LongOpt.NO_ARGUMENT, null, 't');
325 longopts[8] = new LongOpt("status", LongOpt.NO_ARGUMENT, null, 'l');
326
327 final Getopt g = new Getopt(progNameWithJar, argv, "hvdstke:c:f:y:z:a:b:", longopts);
328
329 while ((c = g.getopt()) != -1) {
330 switch (c) {
331 case 0:
332 final String secretKey = g.getOptarg();
333 if (secretKey.length() != BITS_16) {
334 throw new IllegalArgumentException(
335 "The secret key must have 16 characters.");
336 } else {
337 settings.setSecretKey(secretKey);
338 }
339 break;
340
341 case 'a':
342 LOG.debug("a option is selected");
343 final String secretSignToken = g.getOptarg();
344 TokenSecurity.getInstance().setTokenKey(secretSignToken);
345 break;
346 case 'h':
347 LOG.debug("h option is selected");
348 displayHelp();
349 break;
350
351 case 's':
352 LOG.debug("s option is selected");
353 launchServer(settings);
354 break;
355 case 't':
356 LOG.debug("t option is selected");
357 stopServer();
358 break;
359 case 'l':
360 LOG.debug("l option is selected");
361 statusServer();
362 break;
363
364 case 'k':
365 LOG.debug("k option is selected");
366 LOG.info(TokenSecurity.createKeySignatureHS256());
367 break;
368
369 case 'e':
370 LOG.debug("e option is selected");
371 arg = g.getOptarg();
372 try {
373 LOG.info(UtilsCryptography.decrypt(arg, settings.getSecretKey()));
374 } catch (DoiRuntimeException ex) {
375 LOG.fatal("Unable to decrypt {} : {}", arg, ex.getMessage());
376 }
377 break;
378
379 case 'c':
380 LOG.debug("c option is selected");
381 arg = g.getOptarg();
382 try {
383 LOG.info(UtilsCryptography.encrypt(arg, settings.getSecretKey()));
384 } catch (DoiRuntimeException ex) {
385 LOG.fatal("Unable to encrypt {} : {}", arg, ex.getMessage());
386 }
387 break;
388
389 case 'd':
390 LOG.debug("d option is selected");
391 settings.displayConfigFile();
392 break;
393
394 case 'f':
395 LOG.debug("f option is selected");
396 try {
397 settings.setPropertiesFile(g.getOptarg());
398 } catch (IOException ex) {
399 LOG.fatal(ex.getMessage());
400 }
401 break;
402
403 case 'v':
404 LOG.debug("v option is selected");
405 displayVersion();
406 break;
407
408 case 'y':
409 LOG.debug("y option is selected");
410 try {
411 final byte[] encodedFile = Files.readAllBytes(Paths.get(g.getOptarg()));
412 String contentFile = new String(encodedFile, StandardCharsets.UTF_8);
413 contentFile = UtilsCryptography.encrypt(contentFile,
414 settings.getSecretKey());
415 LOG.info(contentFile);
416 } catch (IOException ex) {
417 LOG.fatal("Error: {}", ex.getMessage());
418 }
419 break;
420
421 case 'z':
422 LOG.debug("z option is selected");
423 InputStream inputStream = null;
424 BufferedReader reader = null;
425 Reader inputReader = null;
426
427 try {
428 inputStream = new FileInputStream(g.getOptarg());
429 inputReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
430 reader = new BufferedReader(inputReader);
431 String content = reader.lines().collect(Collectors.joining("\n"));
432 content = UtilsCryptography.decrypt(content, settings.getSecretKey());
433 LOG.info(content);
434 } catch (FileNotFoundException ex) {
435 LOG.fatal("Error: {}", ex.getMessage());
436 } finally {
437 if (inputStream != null) {
438 try {
439 inputStream.close();
440 } catch (IOException e) {
441 LOG.fatal("Error closing inputstream: {}", e.getMessage(), e);
442 }
443 }
444 if (inputReader != null) {
445 try {
446 inputReader.close();
447 } catch (IOException ex) {
448 LOG.fatal("Error closing inputReader: {}", ex.getMessage(), ex);
449 }
450 }
451 if (reader != null) {
452 try {
453 reader.close();
454 } catch (IOException ex) {
455 LOG.fatal("Error closing reader: {}", ex.getMessage(), ex);
456 }
457 }
458 }
459 break;
460 case '?':
461 break;
462
463 default:
464 LOG.debug("getopt() returned {}\n", c);
465 }
466 }
467
468 for (int i = g.getOptind(); i < argv.length; i++) {
469 LOG.info("Non option argv element: {}\n", argv[i]);
470 }
471
472 if (argv.length == 0) {
473 displayHelp();
474 }
475
476 }
477
478
479
480
481 private Starter() {
482 }
483
484 }
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522