Compare commits

...

304 Commits

Author SHA1 Message Date
1f8de26c2d [Jenkins] Dependency Updates 2025-01-09 12:19:03 +01:00
2e041f5585 [Jenkins] Dependency Updates 2025-01-09 04:38:09 +01:00
d3f633d2e4 [Jenkins] Dependency Updates 2024-10-17 12:19:03 +02:00
aad8c04a4f [Jenkins] Dependency Updates 2024-10-17 04:38:16 +02:00
69dd4ed41e [Jenkins] Dependency Updates 2024-10-03 12:19:11 +02:00
ab7fe1759c [Jenkins] Dependency Updates 2024-10-03 04:38:15 +02:00
69d9a052bc [Jenkins] Dependency Updates 2024-09-05 12:19:08 +02:00
219c21ebcb [Jenkins] Dependency Updates 2024-09-05 04:38:12 +02:00
85f53c9da9 [Jenkins] Dependency Updates 2024-05-16 12:19:02 +02:00
79a9a50174 [Jenkins] Dependency Updates 2024-05-16 04:38:08 +02:00
de9523cc82 [Jenkins] Dependency Updates 2024-04-14 08:36:03 +02:00
4b169cfdf5 [Jenkins] Dependency Updates 2024-04-11 12:19:02 +02:00
ca66c652cd [Jenkins] Dependency Updates 2024-04-10 05:36:02 +02:00
Emmanuel Bigeon
1f642ec91d [update] Update to junit 5 2024-04-04 14:41:50 +02:00
8b0efef54f [Jenkins] Dependency Updates 2024-03-07 12:19:11 +01:00
905c499c01 [Jenkins] Dependency Updates 2024-03-07 04:38:16 +01:00
05a353d5a5 [Jenkins] Dependency Updates 2024-02-01 12:19:03 +01:00
c7f2db9814 [Jenkins] Dependency Updates 2024-02-01 04:38:08 +01:00
5e47a68169 [Jenkins] Dependency Updates 2024-01-18 12:19:03 +01:00
14c4382560 [Jenkins] Dependency Updates 2024-01-18 04:38:08 +01:00
e930f60d0e [Jenkins] Dependency Updates 2023-12-07 12:19:03 +01:00
93dba8bea8 [Jenkins] Dependency Updates 2023-12-07 04:38:10 +01:00
12370856e9 [Jenkins] Dependency Updates 2023-11-09 12:19:03 +01:00
1c5f87aee1 [Jenkins] Dependency Updates 2023-11-09 04:38:10 +01:00
57177d30c1 [Jenkins] Dependency Updates 2023-10-12 12:19:02 +02:00
bdd1bec9ce [Jenkins] Dependency Updates 2023-10-12 04:38:09 +02:00
cebb30965d [Jenkins] Dependency Updates 2023-08-24 12:19:02 +02:00
63d21acb4f [Jenkins] Dependency Updates 2023-08-24 04:38:09 +02:00
0c76ac826b [Jenkins] Dependency Updates 2023-07-02 08:36:03 +02:00
4ba4cee1cf [Jenkins] Dependency Updates 2023-06-29 12:19:03 +02:00
84176998a2 [Jenkins] Dependency Updates 2023-06-28 05:36:02 +02:00
5e1dba6244 [Jenkins] Dependency Updates 2023-06-22 12:19:03 +02:00
2a1c8393ba [Jenkins] Dependency Updates 2023-06-22 04:38:06 +02:00
09635365a5 [Jenkins] Dependency Updates 2023-04-27 12:19:03 +02:00
b7d86da484 [Jenkins] Dependency Updates 2023-04-27 04:38:07 +02:00
021193b640 [Jenkins] Dependency Updates 2023-04-13 12:19:03 +02:00
6a0f321e16 [Jenkins] Dependency Updates 2023-04-13 04:38:06 +02:00
662ea7ddc0 [Jenkins] Dependency Updates 2023-04-06 12:19:03 +02:00
c8a0897cbb [Jenkins] Dependency Updates 2023-04-05 05:36:02 +02:00
9fa3bc7d10 [Jenkins] Dependency Updates 2023-04-02 08:36:03 +02:00
992b3b9f09 [Jenkins] Dependency Updates 2023-03-16 12:19:03 +01:00
3434210d0a [Jenkins] Dependency Updates 2023-03-16 04:38:07 +01:00
bd33923aa7 [Jenkins] Dependency Updates 2023-02-02 12:19:02 +01:00
e7f26bcb8c [Jenkins] Dependency Updates 2023-02-02 04:38:06 +01:00
cbf0f79d91 [Jenkins] Dependency Updates 2023-01-19 12:19:02 +01:00
82a3d260b9 [Jenkins] Dependency Updates 2023-01-19 04:38:06 +01:00
5147c19bd2 [Jenkins] Dependency Updates 2023-01-05 12:19:02 +01:00
e4260f591d [Jenkins] Dependency Updates 2023-01-05 04:38:06 +01:00
a352538dbb [Jenkins] Dependency Updates 2022-12-22 12:19:02 +01:00
c0fbcf44a3 [Jenkins] Dependency Updates 2022-12-22 04:38:06 +01:00
7b88a2e4b4 [Jenkins] Dependency Updates 2022-11-17 12:19:02 +01:00
62f637d73e [Jenkins] Dependency Updates 2022-11-17 04:38:06 +01:00
3300344819 [Jenkins] Dependency Updates 2022-10-27 12:19:02 +02:00
61da33e7fe [Jenkins] Dependency Updates 2022-10-27 04:38:07 +02:00
5d3d5ce795 [Jenkins] Dependency Updates 2022-09-15 12:19:03 +02:00
285d94460e [Jenkins] Dependency Updates 2022-09-15 04:38:49 +02:00
2f76312c60 [maven-release-plugin] prepare for next development iteration 2022-09-10 12:58:34 +02:00
48d4db4086 [maven-release-plugin] prepare release swt-1.2.1 2022-09-10 12:58:31 +02:00
6a7d1f4636 [fix] Fix runnable superclass location
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2022-09-10 12:56:50 +02:00
9748b14872 [Jenkins] Dependency Updates 2022-08-18 12:19:03 +02:00
3219729b92 [Jenkins] Dependency Updates 2022-08-18 04:38:06 +02:00
e8eb56287e [Jenkins] Dependency Updates 2022-06-09 12:19:03 +02:00
6bc567d3b9 [Jenkins] Dependency Updates 2022-06-09 04:38:08 +02:00
cf2a90d2b7 [Jenkins] Dependency Updates 2022-06-02 12:19:03 +02:00
0dee4837ed [Jenkins] Dependency Updates 2022-04-28 12:19:02 +02:00
d605ed7aa1 [Jenkins] Dependency Updates 2022-04-28 04:38:08 +02:00
e570680ef8 [Jenkins] Dependency Updates 2022-04-21 12:19:02 +02:00
8da0a10616 [Jenkins] Dependency Updates 2022-04-21 04:38:06 +02:00
0839487620 [Jenkins] Dependency Updates 2022-03-19 08:47:06 +01:00
e37e84019a [Jenkins] Dependency Updates 2022-03-10 12:19:02 +01:00
bf593d4989 [Jenkins] Dependency Updates 2022-03-10 04:38:08 +01:00
0f120f7330 [Jenkins] Dependency Updates 2022-02-27 13:51:27 +01:00
52c76b4633 [Jenkins] Dependency Updates 2022-02-10 12:19:02 +01:00
3393294ddc [Jenkins] Dependency Updates 2022-02-10 04:38:07 +01:00
990b89848d [Jenkins] Dependency Updates 2022-02-09 05:36:03 +01:00
a74a909011 [maven-release-plugin] prepare for next development iteration 2022-02-05 10:53:58 +01:00
999c97dbf8 [maven-release-plugin] prepare release gclc-2.1.5 2022-02-05 10:53:55 +01:00
a9916eacab [chore] Update pom scm. Skip gclc-2.1.4
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2022-02-05 10:51:39 +01:00
fe942533cd [maven-release-plugin] prepare release gclc-2.1.4 2022-02-05 10:47:50 +01:00
a31c13ef1b [chore] Pom update scm. skip gclc 2.1.3 release.
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2022-02-05 10:45:42 +01:00
c7c53d15c5 [maven-release-plugin] prepare release gclc-2.1.3 2022-02-05 10:42:37 +01:00
d86a254604 Merge branch 'master' into dev 2022-02-05 10:39:23 +01:00
3d9b0d039a [Jenkins] Dependency Updates 2022-01-27 12:20:02 +01:00
39ae13e01a [Jenkins] Dependency Updates 2022-01-27 04:39:08 +01:00
eeea0148f6 Merge branch 'master' into dev 2022-01-05 17:14:28 +01:00
a25360f50f [Jenkins] Dependency Updates 2021-12-23 12:19:02 +01:00
4212776bb9 [Jenkins] Dependency Updates 2021-12-23 04:38:08 +01:00
4d42dff80a [Jenkins] Dependency Updates 2021-11-25 12:19:02 +01:00
d680694d0a [Jenkins] Dependency Updates 2021-11-25 04:38:07 +01:00
c174e90562 [test] Remove use of deprecated code in test
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-22 08:20:51 +01:00
6bb3ad2354 [test] Fix invalid cast
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-22 08:19:53 +01:00
0f731270d5 [test] Fix the test of history text listener
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-13 19:13:00 +01:00
45fa90c75e Merge branch 'master' into dev 2021-11-13 19:00:38 +01:00
da107f0a40 [doc] Added comments on a brain overloading method.
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-12 10:06:48 +01:00
14007f4d9a [fix] Remove use of deprecated code
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-12 10:00:06 +01:00
c500c3c046 [fix] Specify command type
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-12 09:53:35 +01:00
0c1cfe3946 [style] Set abstract class constructor as protected
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-12 09:52:11 +01:00
66d25697a8 [fix] Set waits inside loops
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-12 09:50:51 +01:00
a9c97a7ebc [chore] Remove commented out code
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-12 09:31:49 +01:00
0acc900a7c [Jenkins] Dependency Updates 2021-11-11 12:19:03 +01:00
8025122906 [test] Complete tests.
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-10 11:14:08 +01:00
c8804fcd74 [Jenkins] Dependency Updates 2021-11-10 05:36:03 +01:00
80b8993e1f [Jenkins] Dependency Updates 2021-11-08 10:09:38 +01:00
625cacb198 [fix] Fix use of computeIfPresent with HashMap with possible null values
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-07 12:22:11 +01:00
ea01d346d3 [test] Use assert equals
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-07 12:13:13 +01:00
b38980eeb5 [chore] Move InterruptedIOException full name into documentation.
This avoids unnecessarily importing it.

Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-07 11:59:32 +01:00
7b7d0e6747 [style] protected context for constructor of abstract classes
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-07 11:57:13 +01:00
8643b69054 [refactor] Use mapping computeIfPresent/Absent for conditional branch
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-07 11:55:21 +01:00
d726312341 [fix] Use string concatenation in delegated function for logging
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-07 11:45:29 +01:00
2ece273148 [fix] Put wait in while loops to consider spurious interruptions.
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-07 11:44:20 +01:00
63de5448de [refactor] Reduced complexity of argument reading by one by refactoring
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-07 11:32:33 +01:00
51d8caa51e [fix] Unclosed stream wrapped in delegating stream.
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-07 11:22:40 +01:00
72f768e6de [fix] Bug in Test for interactive
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2021-11-07 11:20:38 +01:00
8f386b4abd [tests] Add test on input contract
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2020-04-10 15:58:30 -04:00
1454938db8 [fix] Fix imports in new version of tests
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2020-04-10 15:53:39 -04:00
2f24c30d44 [jenkins] Update configuration and dependency 2020-04-06 06:25:29 -04:00
c97e5de32e [chore] update dependencies
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2020-04-04 12:45:33 -04:00
3df6482a64 [tests] Fix failing test because of incoherent assertion
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2020-04-04 11:48:53 -04:00
5c7635f550 Fix cycle
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-06-09 12:13:41 -04:00
54a267092d Moved util class to the right package
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-06-08 20:13:39 -04:00
c1ce1277d3 Extract interface as method return
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-06-08 20:09:03 -04:00
ba61efaa4a Add test with long execution command
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-06-08 19:59:26 -04:00
7a2991acc1 Reduce line duplication due to moved class
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-06-08 12:05:50 -04:00
fe8f0212ea [maven-release-plugin] prepare for next development iteration 2019-06-08 11:34:22 -04:00
a0a1dfe91e [maven-release-plugin] prepare release gclc-2.1.2 2019-06-08 11:34:17 -04:00
04902c9868 Remove unecessary method call
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-06-08 11:27:20 -04:00
9cf30ef7d2 #4 #5 Update exception of script command
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-06-08 10:55:35 -04:00
3960b10e8e #3 Add null chack for command name
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-06-08 10:50:51 -04:00
d3bb6fa5a0 Add eof new line
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-05-07 09:08:23 -04:00
b6b6ab9d36 Cut cycle
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-05-06 21:19:42 -04:00
92a664d193 Fix javadoc
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-05-06 21:14:26 -04:00
c230a23f81 Fix toString ternary operator presence
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-05-06 21:14:18 -04:00
e5c3bb0152 Replace ConstantString by lambda expression
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-05-06 20:51:12 -04:00
24e57eca2f Adding shell start up wait method.
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-05-06 20:31:37 -04:00
da8765dd7b [maven-release-plugin] prepare for next development iteration 2019-05-06 20:00:30 -04:00
ce4254941a [maven-release-plugin] prepare release gclc-2.1.1 2019-05-06 20:00:25 -04:00
f459f78a75 #2 add removal of escaped character from last argument in line.
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-05-06 19:53:55 -04:00
8cd0b01490 [maven-release-plugin] prepare for next development iteration 2019-04-22 17:52:41 -04:00
698b3d0e5e [maven-release-plugin] prepare release socket-1.1.18 2019-04-22 17:52:38 -04:00
9ad4a1e456 Add accesses to the listener list
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-04-22 17:52:18 -04:00
e560fcb92d [maven-release-plugin] prepare for next development iteration 2019-04-22 17:46:19 -04:00
56f3d604a1 [maven-release-plugin] prepare release socket-1.1.17 2019-04-22 17:46:16 -04:00
73317186df Update tests
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-04-22 17:45:40 -04:00
80bd7c0ac9 Add listener for brutal disconnection
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-04-22 17:44:23 -04:00
dc71070dab [maven-release-plugin] prepare for next development iteration 2019-04-22 16:07:42 -04:00
78b5926af1 [maven-release-plugin] prepare release socket-1.1.16 2019-04-22 16:07:39 -04:00
a3d2c2c07e Update disconnection
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-04-22 16:04:40 -04:00
9df812273c [maven-release-plugin] prepare for next development iteration 2019-04-22 15:33:47 -04:00
e953c2e659 [maven-release-plugin] prepare release socket-1.1.15 2019-04-22 15:33:44 -04:00
0f4fd6109d Stop console closing on client dirty disconnection
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-04-22 15:29:11 -04:00
69a8fd2533 [maven-release-plugin] prepare for next development iteration 2019-04-14 17:20:28 -04:00
0b772ddeb3 [maven-release-plugin] prepare release swt-1.2.0 2019-04-14 17:20:24 -04:00
55f13ae004 Use public configuration
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-04-14 17:19:44 -04:00
295075ca37 Update versions and configuration
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-04-14 17:14:04 -04:00
75bbba9884 [maven-release-plugin] prepare for next development iteration 2019-04-14 17:10:09 -04:00
82daa84bdf [maven-release-plugin] prepare release socket-1.1.14 2019-04-14 17:10:05 -04:00
10a0858d81 Update configuration
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-04-14 17:07:04 -04:00
dfd3645497 Update configuration
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-04-14 17:03:57 -04:00
bd2da741f2 [maven-release-plugin] prepare for next development iteration 2019-04-02 09:41:05 -04:00
314db5b82f [maven-release-plugin] prepare release process-0.0.6 2019-04-02 09:40:56 -04:00
981668d3a7 Update dependency on collections
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2019-02-13 13:19:11 -05:00
9c8866827d Reduce prompting method complexity
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-03 11:25:01 -05:00
43c9faaee7 Added test for interruption and task failure
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-03 11:12:23 -05:00
8436e8926c Factor prompting methods code
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-03 11:12:13 -05:00
0cef23e17b Reduce branching depth
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-02 14:19:54 -05:00
6b2a25674d Fix interruption mechanic
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-02 13:57:48 -05:00
eec660e089 Improved message to test failure
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-02 13:57:21 -05:00
901469792d Added interruption flag
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-02 13:33:15 -05:00
79ee5394d4 Move inner classes into tool package
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-02 13:32:29 -05:00
acf4484eb3 [maven-release-plugin] prepare for next development iteration 2018-12-01 12:04:16 -05:00
012ef6e668 [maven-release-plugin] prepare release gclc-2.1.0 2018-12-01 12:04:13 -05:00
583f3146fd Removed declaration of runtime exception
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-01 12:01:44 -05:00
8b52ba5d49 Licensing
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-01 11:48:34 -05:00
a3051c6f03 Update next release version.
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-01 11:48:07 -05:00
18b98997d3 Move console IO implemetations. #1 fixed
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-01 11:45:16 -05:00
0e1c69dab7 Moved output forward runnable
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-01 11:38:53 -05:00
c206b5b22c Changed prompt interruption contract
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-01 10:54:51 -05:00
c4bbfd8434 Moved console input/output implementations
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-12-01 10:54:30 -05:00
b671474f64 Added a delai for the task initialization
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 22:56:51 -05:00
6443790532 Change port for test server
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 22:35:38 -05:00
2df99debf8 Added test. removed implementation of shell
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 22:31:45 -05:00
d87aabd465 Change internal classes as private definitions
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 22:03:43 -05:00
0562faad70 Javadoc
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 22:02:44 -05:00
bb06b2a799 Removed suppress warning
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 21:56:08 -05:00
626f557aa0 Warning removal
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 21:47:10 -05:00
185d19bfcf Added documentation
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 21:35:17 -05:00
7fd1bf90bd Remove unused attributes
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 21:32:20 -05:00
1fa1f168c8 Added documentation
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 21:30:13 -05:00
09e9c69fe0 Use inet address rather than host name
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 16:02:22 -05:00
9f273f7595 Fix host name in test
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 14:18:50 -05:00
143eb7036a Added junit dependency, added input contract
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 12:18:16 -05:00
9c64c708eb Removed uneeded line
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 12:17:53 -05:00
8449eddab8 Extract model of view from graphics of view
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 12:17:38 -05:00
d0a286d45a Allow access to output forward to subclasses
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 10:44:05 -05:00
d6698a22fb Initial sharing of test. Moved gitignore to global ignore
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 09:21:46 -05:00
5e1050a9d7 Update test server to use getLocalHost rather than loopback.
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-30 09:05:40 -05:00
9374c5a681 Added message notifications.
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-26 10:40:40 -05:00
4c1e28b04f Update gclc version
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-25 12:07:04 -05:00
80933bb636 Update test
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-25 12:03:06 -05:00
48b4375565 removed logger
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-25 12:03:00 -05:00
ba7b80689c [maven-release-plugin] prepare for next development iteration 2018-11-25 12:00:44 -05:00
bd311d185e [maven-release-plugin] prepare release gclc-2.0.12 2018-11-25 12:00:41 -05:00
aa26173f26 Update port
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-25 11:37:07 -05:00
df688b1a85 Code factoring
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-25 11:31:52 -05:00
bcd0faceef Fixed runnable to allow acces to pending messages
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-25 11:22:37 -05:00
5e5cc2a1cd Added tests
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-25 11:05:06 -05:00
b24b72f3e2 Fixed console input
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-25 11:05:00 -05:00
ad79e3ccb6 Change to Supplier. Fix tests
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-25 09:16:38 -05:00
ddd818d93b Fix test
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-25 00:40:54 -05:00
6993513f38 Improve exception mechanics. remove closure of stream.
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-25 00:27:30 -05:00
b9a7142c5c Added comments
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-24 23:37:27 -05:00
85ef68f6ae Added error storing in the forwarding runnable. Added tests
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-11-24 21:07:28 -05:00
dfca63b179 [maven-release-plugin] prepare for next development iteration 2018-10-27 17:17:47 -04:00
787f951ff7 [maven-release-plugin] prepare release process-0.0.5 2018-10-27 17:17:44 -04:00
a364b6442b Added javadoc
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-27 17:16:15 -04:00
211b965e9e [maven-release-plugin] prepare for next development iteration 2018-10-27 16:39:13 -04:00
e1d030c6c5 [maven-release-plugin] prepare release swt-1.1.6 2018-10-27 16:39:10 -04:00
8bc2b13a92 [maven-release-plugin] prepare for next development iteration 2018-10-27 15:58:12 -04:00
c1050b1b4f [maven-release-plugin] prepare release socket-1.1.13 2018-10-27 15:58:09 -04:00
a3e4758d1b [maven-release-plugin] prepare for next development iteration 2018-10-27 14:54:06 -04:00
c436b5af2f [maven-release-plugin] prepare release process-0.0.4 2018-10-27 14:54:03 -04:00
3272261be1 [maven-release-plugin] prepare for next development iteration 2018-10-27 14:42:49 -04:00
f82f0b9f16 [maven-release-plugin] prepare release system-0.0.2 2018-10-27 14:42:41 -04:00
1fa9ca213e Fix process forwarding of
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-27 14:42:22 -04:00
265f90526a Update dependency versions
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-27 14:38:07 -04:00
71b325508f [maven-release-plugin] prepare for next development iteration 2018-10-27 14:27:47 -04:00
98857e425a [maven-release-plugin] prepare release gclc-2.0.11 2018-10-27 14:27:44 -04:00
8c6b1d8884 minor conventions
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-27 14:18:32 -04:00
2972e82f4a fix interlocks
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-27 14:17:19 -04:00
0ab39b24f6 Fix available message test
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-27 14:02:31 -04:00
171f79518d Made port public constant
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-27 14:02:21 -04:00
0e422a81ce Added equals
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-27 14:01:53 -04:00
ea4164fbfb Added tests
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-27 14:01:42 -04:00
e2f45c77d9 Comment
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-27 09:56:07 -04:00
55bb54ca43 Added tool methods for most common uses
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-27 09:50:28 -04:00
cf29eb37cc Move test thread in test runnable
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-27 09:40:56 -04:00
579e85dcb3 Update pom
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-27 09:40:42 -04:00
7e36a378c4 documentation
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 16:09:04 -04:00
80242c79e8 Added test of command on linux system
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 15:39:54 -04:00
ab9a4c474b Made method static
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 15:37:25 -04:00
b8e5ea0b78 Fix pom order
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 11:27:09 -04:00
2637d99bce Update pom
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 11:14:54 -04:00
cfd3450afe [maven-release-plugin] prepare for next development iteration 2018-10-26 11:12:48 -04:00
20d5a84269 [maven-release-plugin] prepare release socket-1.1.12 2018-10-26 11:12:46 -04:00
637cfb8f43 increment in separate statement. string construction
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 11:11:24 -04:00
d7fbdfb66b Fix pom order
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 11:10:17 -04:00
22741104f5 update depencies
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 10:16:38 -04:00
45c5375118 [maven-release-plugin] prepare for next development iteration 2018-10-26 10:11:30 -04:00
d01b1608f3 [maven-release-plugin] prepare release gclc-2.0.10 2018-10-26 10:11:27 -04:00
674333a42c Added comment and test
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 10:09:33 -04:00
af0c8e91f8 Order pom elements
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 09:31:01 -04:00
461dec8894 Update poms
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 09:15:43 -04:00
f590542b3f Add jenkins file
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 09:07:08 -04:00
c21517a53d license fix
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 09:06:49 -04:00
7e3b727024 Add echo command
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 09:06:28 -04:00
bd5b67c312 Move runnable to external class definition
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-26 09:05:40 -04:00
216cd41dc8 format and commands
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-16 19:07:33 -04:00
1df33afdbe Name convention
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 12:22:47 -04:00
8d51733590 Made shell invisible in tests. added shell style access
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 12:19:23 -04:00
d49a2474e0 Format
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 12:19:09 -04:00
d7ecd75678 Clean up
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 12:01:10 -04:00
31ad72567a Javadoc
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 11:42:40 -04:00
99134c1831 Update config
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 11:42:32 -04:00
dc988a07ac Clean up.
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 10:57:47 -04:00
4d31bbacbf Removed unecessary throw declarations
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 10:57:22 -04:00
2145473706 Added test for command
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 10:19:39 -04:00
3f8cee20e1 move protected to private
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 09:48:39 -04:00
977c9a8ec8 Method factoring
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 09:48:10 -04:00
889433d800 [maven-release-plugin] prepare for next development iteration 2018-10-15 09:36:03 -04:00
89847738e5 [maven-release-plugin] prepare release gclc-socket-1.1.11 2018-10-15 09:35:58 -04:00
cd76d438c1 Update config
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 09:34:59 -04:00
fa46939c54 Added doc. Clean up code
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 09:34:51 -04:00
fe4851053a Update configurations
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 09:14:06 -04:00
6a7d0b11a0 Removed thread sleeping lock
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-15 09:10:31 -04:00
93589c750e Reformatting
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-14 23:07:41 -04:00
c23af20b17 Update version of configuration
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-14 19:05:49 -04:00
c7625c8006 Update dependencies, organize imports
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-14 18:58:17 -04:00
ad65165169 [maven-release-plugin] prepare for next development iteration 2018-10-14 18:28:48 -04:00
0814ab5740 [maven-release-plugin] prepare release gclc-2.0.9 2018-10-14 18:28:43 -04:00
ba26a70daa Minor javadoc
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-14 18:24:54 -04:00
4301f2a15e Fix test and wait
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-14 18:15:49 -04:00
2a05366e31 REmoved unnecessary boolean variable
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-14 11:04:13 -04:00
bd44b5bf85 Added empty else for readability
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-14 11:02:14 -04:00
4e804325e6 Removed initialisation to default
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-14 11:02:05 -04:00
ae5dc1aeba Move help command to package of defined commands
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-14 10:55:55 -04:00
d5a7d4a16f Avoid contains and get calls on same key in map
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-14 10:04:08 -04:00
de593c00a1 Update config, remove site descriptor, to use inherited one 2018-10-14 09:29:42 -04:00
50ac6eec06 Moved out increments
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-14 09:28:04 -04:00
74c66dcbdd Moved listeners from list to set 2018-10-14 09:26:55 -04:00
7c94dea7b5 Avoid multiple creation of empty array 2018-10-14 09:26:27 -04:00
a580133945 Removed method with constant return 2018-10-14 09:26:07 -04:00
38fe457f47 Formating in logs 2018-10-11 12:50:19 -04:00
159805701c Fixed run lockings
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-11 12:43:17 -04:00
f7876dc964 Remove unused final modifiers 2018-10-11 12:38:09 -04:00
c4db9f43fb Clean up and format 2018-10-11 12:29:49 -04:00
67abd91f72 Use concurrent hash map, where possible
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
2018-10-11 12:13:21 -04:00
7934ab7435 Removed unnecessary exception 2018-10-11 12:09:42 -04:00
761d640f0b Synchronization organisation 2018-10-11 12:09:24 -04:00
98d22782c1 Collections initial size 2018-10-11 12:08:59 -04:00
f8da1c0119 Move from list to linked set for order conservation and use efficiency 2018-10-11 11:33:15 -04:00
438727e7b9 Fix stream for efficient garbage collection 2018-10-11 11:32:49 -04:00
a2a87eb0d7 Added site definition 2018-10-11 11:32:32 -04:00
7ba8b38624 Added synchronize block 2018-10-11 11:32:23 -04:00
6bcf2b0c91 [maven-release-plugin] prepare for next development iteration 2018-10-11 11:11:23 -04:00
165 changed files with 7295 additions and 4714 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
# maven files
target/
# Eclipse files
.settings/
.classpath
.project

65
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,65 @@
pipeline {
agent any
stages {
stage('Site GCLC') {
when {
tag "gclc-*"
}
steps {
dir('gclc') {
sh 'mvn site:site'
sh 'mkdir -p /home/maven-sites/net/bigeon/gclc'
sh 'cp -r target/site /home/maven-sites/net/bigeon/gclc/gclc'
}
}
}
stage('Site GCLC-Socket') {
when {
tag "socket-*"
}
steps {
dir('gclc-socket') {
sh 'mvn site:site'
sh 'mkdir -p /home/maven-sites/net/bigeon/gclc'
sh 'cp -r target/site /home/maven-sites/net/bigeon/gclc/gclc-socket'
}
}
}
stage('Site GCLC-Swt') {
when {
tag "swt-*"
}
steps {
dir('gclc-swt') {
sh 'mvn site:site'
sh 'mkdir -p /home/maven-sites/net/bigeon/gclc'
sh 'cp -r target/site /home/maven-sites/net/bigeon/gclc/gclc-swt'
}
}
}
stage('Site GCLC-Processes') {
when {
tag "process-*"
}
steps {
dir('gclc-process') {
sh 'mvn site:site'
sh 'mkdir -p /home/maven-sites/net/bigeon/gclc'
sh 'cp -r target/site /home/maven-sites/net/bigeon/gclc/gclc-process'
}
}
}
stage('Site GCLC-System') {
when {
tag "system-*"
}
steps {
dir('gclc.system') {
sh 'mvn site:site'
sh 'mkdir -p /home/maven-sites/net/bigeon/gclc'
sh 'cp -r target/site /home/maven-sites/net/bigeon/gclc/gclc-system'
}
}
}
}
}

View File

@@ -1,64 +1,23 @@
<!-- process, Distribution repositories and basic setup for Emmanuel Bigeon projects -->
<!-- Copyright (C) 2014-2018 E. Bigeon -->
<!-- mailto:emmanuel@bigeon.fr -->
<!-- -->
<!-- This software is governed by the CeCILL license under French law and -->
<!-- abiding by the rules of distribution of free software. You can use, -->
<!-- modify and/or redistribute the software under the terms of the CeCILL -->
<!-- license as circulated by CEA, CNRS and INRIA at the following URL -->
<!-- "http://www.cecill.info". -->
<!-- -->
<!-- As a counterpart to the access to the source code and rights to copy, -->
<!-- modify and redistribute granted by the license, users are provided only -->
<!-- with a limited warranty and the software's author, the holder of the -->
<!-- economic rights, and the successive licensors have only limited -->
<!-- liability. -->
<!-- -->
<!-- In this respect, the user's attention is drawn to the risks associated -->
<!-- with loading, using, modifying and/or developing or reproducing the -->
<!-- software by the user in light of its specific status of free software, -->
<!-- that may mean that it is complicated to manipulate, and that also -->
<!-- therefore means that it is reserved for developers and experienced -->
<!-- professionals having in-depth computer knowledge. Users are therefore -->
<!-- encouraged to load and test the software's suitability as regards their -->
<!-- requirements in conditions enabling the security of their systems and/or -->
<!-- data to be ensured and, more generally, to use and operate it in the -->
<!-- same conditions as regards security. -->
<!-- -->
<!-- The fact that you are presently reading this means that you have had -->
<!-- knowledge of the CeCILL license and that you accept its terms. -->
<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"> <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">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>net.bigeon.gclc</groupId>
<artifactId>process</artifactId>
<version>0.0.4-SNAPSHOT</version>
<packaging>jar</packaging>
<name>process</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<copyright.email>emmanuel@bigeon.fr</copyright.email>
<license.licenseName>cecill_2.1</license.licenseName>
</properties>
<parent> <parent>
<groupId>net.bigeon.config</groupId> <groupId>net.bigeon.config</groupId>
<artifactId>ebigeon-config</artifactId> <artifactId>ebigeon-config</artifactId>
<version>1.8.9</version> <version>1.8.33</version>
</parent> </parent>
<dependencies>
<dependency>
<groupId>net.bigeon</groupId>
<artifactId>gclc</artifactId>
<version>2.0.7</version>
</dependency>
</dependencies>
<groupId>net.bigeon.gclc</groupId>
<artifactId>process</artifactId>
<version>0.0.7-SNAPSHOT</version>
<packaging>jar</packaging>
<name>process</name>
<description>A library to handle processes in a generic console application.</description><url>https://bigeon.net/projects/gclc.html</url>
<inceptionYear>2017</inceptionYear>
<organization>
<name>Bigeon</name>
<url>https://bigeon.net</url>
</organization>
<licenses> <licenses>
<license> <license>
<distribution>manual</distribution> <distribution>manual</distribution>
@@ -66,6 +25,39 @@
<url>https://cecill.info/licences/Licence_CeCILL_V2.1-en.html</url> <url>https://cecill.info/licences/Licence_CeCILL_V2.1-en.html</url>
</license> </license>
</licenses> </licenses>
<developers>
<developer>
<email>emmanuel@bigeon.fr</email>
<name>Emmanuel Bigeon</name>
<url>bigeon.net</url>
<roles>
<role>PM</role>
</roles>
</developer>
</developers>
<scm>
<tag>HEAD</tag>
<developerConnection>scm:git:gitea@git.code.bigeon.net:emmanuel/gclc-core.git</developerConnection>
</scm>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<copyright.email>emmanuel@bigeon.fr</copyright.email>
<license.licenseName>cecill_2.1</license.licenseName>
</properties>
<dependencies>
<dependency>
<groupId>net.bigeon</groupId>
<artifactId>gclc</artifactId>
<version>2.1.5</version>
</dependency>
<dependency>
<groupId>net.bigeon.test</groupId>
<artifactId>junitmt</artifactId>
<version>1.0.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<reporting> <reporting>
<plugins> <plugins>
<plugin> <plugin>
@@ -80,19 +72,5 @@
</plugin> </plugin>
</plugins> </plugins>
</reporting> </reporting>
<developers>
<developer>
<email>emmanuel@bigeon.fr</email>
<name>Emmanuel Bigeon</name>
<url>bigeon.net</url>
<roles>
<role>PM</role>
</roles>
</developer>
</developers>
<scm>
<tag>process-0.0.1</tag>
<developerConnection>scm:git:gogs@git.code.bigeon.net:emmanuel/gclc.git</developerConnection>
</scm>
</project> </project>

View File

@@ -69,6 +69,8 @@ package net.bigeon.gclc.process;
* #L% * #L%
*/ */
import java.util.List; import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.bigeon.gclc.command.CommandParameters; import net.bigeon.gclc.command.CommandParameters;
import net.bigeon.gclc.command.ParametrizedCommand; import net.bigeon.gclc.command.ParametrizedCommand;
@@ -92,28 +94,33 @@ import net.bigeon.gclc.manager.ConsoleOutput;
* @author Emmanuel Bigeon */ * @author Emmanuel Bigeon */
public class CommandForeground extends ParametrizedCommand { public class CommandForeground extends ParametrizedCommand {
/** Number of milliseconds in a second. */
private static final int MILLIS_IN_A_SEC = 1000;
/** The class logger. */
private static final Logger LOGGER = Logger
.getLogger(CommandForeground.class.getName());
/** The task pool to fetch task from. */
private final TaskPool pool; private final TaskPool pool;
/** Add the fork command. /** Add the fork command.
* *
* @param name the command name * @param name the command name
* @param pool The pool to get joinable tasks from */ * @param pool The pool to get joinable tasks from */
public CommandForeground(final String name, TaskPool pool) { public CommandForeground(final String name, final TaskPool pool) {
super(name, false); super(name, false);
this.pool = pool; this.pool = pool;
addParameters(); addParameters();
} }
/** /** Add the parameters of the command. */
*
*/
private void addParameters() { private void addParameters() {
try { try {
addStringParameter("pid", false); addStringParameter("pid", false);
addStringParameter("delai", false); addStringParameter("delai", false);
} catch (final InvalidParameterException e) { } catch (final InvalidParameterException e) {
// TODO Auto-generated catch block // Cannot be reached unless GCLC base framework has an error
e.printStackTrace(); LOGGER.log(Level.SEVERE, "unexpected parameter error!", e);
} }
} }
@@ -125,26 +132,34 @@ public class CommandForeground extends ParametrizedCommand {
protected void doExecute(final ConsoleOutput out, final ConsoleInput in, protected void doExecute(final ConsoleOutput out, final ConsoleInput in,
final CommandParameters parameters) throws CommandRunException { final CommandParameters parameters) throws CommandRunException {
String string = parameters.get("pid"); String string = parameters.get("pid");
final List<String> additionals = parameters.getAdditionals();
if (string == null && !additionals.isEmpty()) {
string = additionals.get(0);
}
if (string == null) { if (string == null) {
final List<String> additionals = parameters.getAdditionals();
if (additionals.isEmpty()) {
throw new CommandRunException(CommandRunExceptionType.USAGE, throw new CommandRunException(CommandRunExceptionType.USAGE,
"Missing process id"); "Missing process id");
} }
string = additionals.get(0);
}
Integer pid;
try {
pid = Integer.valueOf(string);
} catch (final NumberFormatException e) {
throw new CommandRunException(CommandRunExceptionType.USAGE,
"PID should be an integer");
}
// Join the command. // Join the command.
final Task cmd = pool.get(string); final Task cmd = pool.get(pid);
if (!(cmd instanceof ForkTask)) { if (!(cmd instanceof ForkTask)) {
throw new CommandRunException("No such forked process"); throw new CommandRunException("No such forked process");
} }
long delai = 0; long delai = 0;
final String delaiOpt = parameters.get("delai"); final String delaiOpt = parameters.get("delai");
if (delaiOpt != null) { if (delaiOpt != null) {
delai = Long.parseLong(delaiOpt) * 1000; delai = Long.parseLong(delaiOpt) * MILLIS_IN_A_SEC;
} }
if (delai < 0) { if (delai < 0) {
throw new CommandRunException("Join delai cannot be negative"); throw new CommandRunException(CommandRunExceptionType.USAGE,
"Join delai cannot be negative");
} }
((ForkTask) cmd).join(out, in, delai); ((ForkTask) cmd).join(out, in, delai);
} }

View File

@@ -1,40 +1,10 @@
/*
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
* Copyright (C) 2014-2018 E. Bigeon
* mailto:emmanuel@bigeon.fr
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
/** /**
* gclc-process:net.bigeon.gclc.process.CommandFork.java * gclc-process:net.bigeon.gclc.process.CommandFork.java
* Created on: Nov 13, 2017 * Created on: Nov 13, 2017
*/ */
package net.bigeon.gclc.process; package net.bigeon.gclc.process;
import java.text.MessageFormat;
/*- /*-
* #%L * #%L
* process * process
@@ -74,6 +44,7 @@ import net.bigeon.gclc.command.Command;
import net.bigeon.gclc.command.ICommand; import net.bigeon.gclc.command.ICommand;
import net.bigeon.gclc.command.ICommandProvider; import net.bigeon.gclc.command.ICommandProvider;
import net.bigeon.gclc.exception.CommandRunException; import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput; import net.bigeon.gclc.manager.ConsoleOutput;
@@ -91,15 +62,19 @@ import net.bigeon.gclc.manager.ConsoleOutput;
* @author Emmanuel Bigeon */ * @author Emmanuel Bigeon */
public class CommandFork extends Command { public class CommandFork extends Command {
/** The task pool containing the tasks. */
private final TaskPool pool; private final TaskPool pool;
/** The command provider. */
private final ICommandProvider provider; private final ICommandProvider provider;
/** The number of lines stored in the commands. */
private final int lines; private final int lines;
/** Add the fork command. /** Add the fork command.
* *
* @param name the command name * @param name the command name
* @param provider the allowed command collection */ * @param provider the allowed command collection */
public CommandFork(final String name, ICommandProvider provider, TaskPool pool) { public CommandFork(final String name, final ICommandProvider provider,
final TaskPool pool) {
this(name, provider, pool, -1); this(name, provider, pool, -1);
} }
@@ -107,24 +82,33 @@ public class CommandFork extends Command {
* *
* @param name the command name * @param name the command name
* @param provider the allowed command collection */ * @param provider the allowed command collection */
public CommandFork(final String name, ICommandProvider provider, TaskPool pool, public CommandFork(final String name, final ICommandProvider provider,
int lines) { final TaskPool pool, final int lines) {
super(name); super(name);
this.provider = provider; this.provider = provider;
this.pool = pool; this.pool = pool;
this.lines = lines; this.lines = lines;
} }
/* (non-Javadoc)
* @see net.bigeon.gclc.command.ICommand#execute(net.bigeon.gclc.manager.
* ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[]) */
@Override @Override
public void execute(final ConsoleOutput out, final ConsoleInput in, String... args) public void execute(final ConsoleOutput out, final ConsoleInput in,
throws CommandRunException { final String... args) throws CommandRunException {
if (args.length < 1) { if (args.length < 1) {
throw new CommandRunException("No command to fork"); throw new CommandRunException(CommandRunExceptionType.USAGE,
"No command to fork");
}
final String string = args[0];
final ICommand cmd = provider.get(string);
if (cmd == null) {
throw new CommandRunException(CommandRunExceptionType.USAGE,
MessageFormat.format("No such command {0}", string));
} }
final ICommand cmd = provider.get(args[0]);
final String[] inner = Arrays.copyOfRange(args, 1, args.length); final String[] inner = Arrays.copyOfRange(args, 1, args.length);
final ForkTask task = new ForkCommandTask(cmd, inner, lines); final ForkTask task = new ForkCommandTask(cmd, inner, lines);
final Thread th = new Thread(task); final Thread th = new Thread(task, MessageFormat.format("fork [{0}]", string));
pool.add(task); pool.add(task);
th.start(); th.start();
} }

View File

@@ -72,10 +72,7 @@ import java.util.Arrays;
import net.bigeon.gclc.command.ICommand; import net.bigeon.gclc.command.ICommand;
import net.bigeon.gclc.exception.CommandRunException; import net.bigeon.gclc.exception.CommandRunException;
/** /** @author Emmanuel Bigeon */
* @author Emmanuel Bigeon
*
*/
public class ForkCommandTask extends ForkTask { public class ForkCommandTask extends ForkTask {
private final ICommand command; private final ICommand command;
private final String[] args; private final String[] args;
@@ -89,13 +86,6 @@ public class ForkCommandTask extends ForkTask {
this.args = Arrays.copyOf(args, args.length); this.args = Arrays.copyOf(args, args.length);
} }
/* (non-Javadoc)
* @see net.bigeon.gclc.process.Task#getName() */
@Override
public String getName() {
return command.getCommandName();
}
/* (non-Javadoc) /* (non-Javadoc)
* @see net.bigeon.gclc.process.ForkTask#doRun() */ * @see net.bigeon.gclc.process.ForkTask#doRun() */
@Override @Override
@@ -103,4 +93,11 @@ public class ForkCommandTask extends ForkTask {
command.execute(out, in, args); command.execute(out, in, args);
} }
/* (non-Javadoc)
* @see net.bigeon.gclc.process.Task#getName() */
@Override
public String getName() {
return command.getCommandName();
}
} }

View File

@@ -77,23 +77,30 @@ import net.bigeon.gclc.manager.ConsoleOutput;
import net.bigeon.gclc.process.io.ConnectingConsoleInput; import net.bigeon.gclc.process.io.ConnectingConsoleInput;
import net.bigeon.gclc.process.io.ConnectingConsoleOutput; import net.bigeon.gclc.process.io.ConnectingConsoleOutput;
/** /** A task that is made to run into a thread.
* <p>
* TODO
* *
* @author Emmanuel Bigeon */ * @author Emmanuel Bigeon */
public abstract class ForkTask implements Task { public abstract class ForkTask implements Task {
/** THe listeners. */
private final Set<InterruptionListener> listeners = new HashSet<>(); private final Set<InterruptionListener> listeners = new HashSet<>();
private boolean running = false; /** The running state. */
private boolean running = true;
/** The running state. */
private boolean started = false;
/** The connecting input for this task */
protected final ConnectingConsoleInput in = new ConnectingConsoleInput(); protected final ConnectingConsoleInput in = new ConnectingConsoleInput();
/** The connecting output for this task */
protected final ConnectingConsoleOutput out; protected final ConnectingConsoleOutput out;
/** The exception of the run. */
private CommandRunException exception; private CommandRunException exception;
/** The synchronization lock. */
private final Object runLock = new Object(); private final Object runLock = new Object();
/** @param lines the number of print to store in the output */ /** Create the task.
public ForkTask(int lines) { *
* @param lines the number of print to store in the output */
protected ForkTask(final int lines) {
out = new ConnectingConsoleOutput(ConnectingConsoleOutput.PERSIST, lines); out = new ConnectingConsoleOutput(ConnectingConsoleOutput.PERSIST, lines);
} }
@@ -105,6 +112,20 @@ public abstract class ForkTask implements Task {
listeners.add(listener); listeners.add(listener);
} }
/** Actually run the fork. */
protected abstract void doRun() throws CommandRunException;
/** Get the excepion that caused a failure.
*
* @return the exception */
public final CommandRunException getException() {
return exception;
}
protected final Object getRunningLock() {
return runLock;
}
/* (non-Javadoc) /* (non-Javadoc)
* @see net.bigeon.gclc.process.Task#isRunning() */ * @see net.bigeon.gclc.process.Task#isRunning() */
@Override @Override
@@ -114,21 +135,26 @@ public abstract class ForkTask implements Task {
} }
} }
/** @param out the console output /** Join the task.
*
* @param out the console output
* @param in the console input * @param in the console input
* @param timeout the maximal wait (0 for ever) */ * @param timeout the maximal time to join for (0 for ever) */
public final void join(final ConsoleOutput out, final ConsoleInput in, long timeout) { public final void join(final ConsoleOutput out, final ConsoleInput in,
final long timeout) {
final long tic = System.currentTimeMillis();
synchronized (runLock) { synchronized (runLock) {
this.out.connect(out); this.out.connect(out);
this.in.connect(in); this.in.connect(in);
long tac = System.currentTimeMillis() - tic;
while (running && tac < timeout) {
try { try {
if (running) {
runLock.wait(timeout); runLock.wait(timeout);
}
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
// TODO log.
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
tac = System.currentTimeMillis() - tic;
}
this.out.disconnect(); this.out.disconnect();
this.in.disconnect(); this.in.disconnect();
} }
@@ -148,7 +174,7 @@ public abstract class ForkTask implements Task {
@Override @Override
public final void run() { public final void run() {
synchronized (runLock) { synchronized (runLock) {
running = true; started = true;
} }
try { try {
doRun(); doRun();
@@ -162,16 +188,6 @@ public abstract class ForkTask implements Task {
} }
} }
/** Actually run the fork. */
protected abstract void doRun() throws CommandRunException;
/** Get the excepion that caused a failure.
*
* @return the exception */
public final CommandRunException getException() {
return exception;
}
/* (non-Javadoc) /* (non-Javadoc)
* @see net.bigeon.gclc.process.Task#setRunning(boolean) */ * @see net.bigeon.gclc.process.Task#setRunning(boolean) */
@Override @Override
@@ -182,7 +198,10 @@ public abstract class ForkTask implements Task {
} }
} }
protected final Object getRunningLock() { /** @return the started */
return runLock; public boolean isStarted() {
synchronized (runLock) {
return started;
}
} }
} }

View File

@@ -68,9 +68,10 @@ package net.bigeon.gclc.process;
* knowledge of the CeCILL license and that you accept its terms. * knowledge of the CeCILL license and that you accept its terms.
* #L% * #L%
*/ */
/** A listener for interruption /** A listener for interruption.
* *
* @author Emmanuel Bigeon */ * @author Emmanuel Bigeon */
@FunctionalInterface
public interface InterruptionListener { public interface InterruptionListener {
/** Notification of an interuption of a listened object */ /** Notification of an interuption of a listened object */
void interrupted(); void interrupted();

View File

@@ -71,10 +71,7 @@ import net.bigeon.gclc.ApplicationAttachement;
import net.bigeon.gclc.command.ICommandProvider; import net.bigeon.gclc.command.ICommandProvider;
import net.bigeon.gclc.exception.InvalidCommandName; import net.bigeon.gclc.exception.InvalidCommandName;
/** /** @author Emmanuel Bigeon */
* @author Emmanuel Bigeon
*
*/
public class ProcessAttachement implements ApplicationAttachement { public class ProcessAttachement implements ApplicationAttachement {
private final TaskPool pool; private final TaskPool pool;
@@ -86,8 +83,8 @@ public class ProcessAttachement implements ApplicationAttachement {
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.ApplicationAttachement#attach(fr.bigeon.gclc.ConsoleApplication) * @see fr.bigeon.gclc.ApplicationAttachement#attach(fr.bigeon.gclc.
*/ * ConsoleApplication) */
@Override @Override
public void attach(ICommandProvider application) throws InvalidCommandName { public void attach(ICommandProvider application) throws InvalidCommandName {
application.add(new ProcessKill("kill", pool)); application.add(new ProcessKill("kill", pool));

View File

@@ -69,7 +69,6 @@ package net.bigeon.gclc.process;
* #L% * #L%
*/ */
import net.bigeon.gclc.command.Command; import net.bigeon.gclc.command.Command;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput; import net.bigeon.gclc.manager.ConsoleOutput;
@@ -89,32 +88,30 @@ public final class ProcessClear extends Command {
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager. * @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager.
* ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, * ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, java.lang.String[]) */
* java.lang.String[]) */
@Override @Override
public void execute(final ConsoleOutput out, final ConsoleInput in, public void execute(final ConsoleOutput out, final ConsoleInput in,
final String... args) throws CommandRunException { final String... args) {
for (final String id : pool.getPIDs()) { for (final Integer id : pool.getPIDs()) {
if (!pool.get(id).isRunning()) { if (!pool.get(id).isRunning()) {
pool.remove(id); pool.remove(id);
} }
} }
return;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#tip() */ * @see fr.bigeon.gclc.command.ICommand#tip() */
@SuppressWarnings("nls")
@Override @Override
public String tip() { public String tip() {
return "Request a process to stop (softly)"; return "Remove non running processes from pool";
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.Command#usageDetail() */ * @see fr.bigeon.gclc.command.Command#usageDetail() */
@Override @Override
protected String usageDetail() { protected String usageDetail() {
return null; return " All processes in the pool have their running status checked and the "
+ "non running ones are removed from the list of tasks.";
} }
} }

View File

@@ -70,6 +70,7 @@ package net.bigeon.gclc.process;
*/ */
import net.bigeon.gclc.command.Command; import net.bigeon.gclc.command.Command;
import net.bigeon.gclc.exception.CommandRunException; import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput; import net.bigeon.gclc.manager.ConsoleOutput;
@@ -89,17 +90,19 @@ public final class ProcessKill extends Command {
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager. * @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager.
* ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, * ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, java.lang.String[]) */
* java.lang.String[]) */
@Override @Override
public void execute(final ConsoleOutput out, final ConsoleInput in, public void execute(final ConsoleOutput out, final ConsoleInput in,
final String... args) throws CommandRunException { final String... args) throws CommandRunException {
pool.get(args[0]).setRunning(false); if (args.length < 1) {
throw new CommandRunException(CommandRunExceptionType.USAGE,
"A pid should be specified.");
}
pool.get(Integer.parseInt(args[0])).setRunning(false);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#tip() */ * @see fr.bigeon.gclc.command.ICommand#tip() */
@SuppressWarnings("nls")
@Override @Override
public String tip() { public String tip() {
return "Request a process to stop (softly)"; return "Request a process to stop (softly)";
@@ -109,7 +112,13 @@ public final class ProcessKill extends Command {
* @see fr.bigeon.gclc.command.Command#usageDetail() */ * @see fr.bigeon.gclc.command.Command#usageDetail() */
@Override @Override
protected String usageDetail() { protected String usageDetail() {
return null; return " <pid> is the identification of the process to request the stop to in the pool";
} }
/* (non-Javadoc)
* @see net.bigeon.gclc.command.Command#usagePattern() */
@Override
protected String usagePattern() {
return super.usagePattern() + " <pid>";
}
} }

View File

@@ -95,20 +95,18 @@ public final class ProcessList extends Command {
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager. * @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager.
* ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, * ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, java.lang.String[]) */
* java.lang.String[]) */
@Override @Override
public void execute(final ConsoleOutput out, final ConsoleInput in, public void execute(final ConsoleOutput out, final ConsoleInput in,
final String... args) throws CommandRunException { final String... args) throws CommandRunException {
final ArrayList<String> pids = new ArrayList<>(pool.getPIDs()); final ArrayList<Integer> pids = new ArrayList<>(pool.getPIDs());
Collections.sort(pids); Collections.sort(pids);
for (final String string : pids) { for (final Integer string : pids) {
try { try {
out.println(MessageFormat.format("{0}\t{1}", string, //$NON-NLS-1$ out.println(MessageFormat.format("{0}\t{1}", string, //$NON-NLS-1$
pool.get(string).getName())); pool.get(string).getName()));
} catch (final IOException e) { } catch (final IOException e) {
throw new CommandRunException( throw new CommandRunException(CommandRunExceptionType.INTERACTION,
CommandRunExceptionType.INTERACTION,
"Unable to communicate with user", e); //$NON-NLS-1$ "Unable to communicate with user", e); //$NON-NLS-1$
} }
} }
@@ -117,7 +115,6 @@ public final class ProcessList extends Command {
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#tip() */ * @see fr.bigeon.gclc.command.ICommand#tip() */
@SuppressWarnings("nls")
@Override @Override
public String tip() { public String tip() {
return "List all processes"; return "List all processes";
@@ -127,7 +124,7 @@ public final class ProcessList extends Command {
* @see fr.bigeon.gclc.command.Command#usageDetail() */ * @see fr.bigeon.gclc.command.Command#usageDetail() */
@Override @Override
protected String usageDetail() { protected String usageDetail() {
return null; return " No argument is considered by this command";
} }
} }

View File

@@ -103,8 +103,8 @@ public interface Task extends Runnable {
/** Set the running state. /** Set the running state.
* <p> * <p>
* This method should be only called by external objects with the false * This method should be only called by external objects with the false
* argument. Calling this method with true has unspecified behavior and * argument. Calling this method with true has unspecified behavior and could do
* could do nothing as well as restart the command for example. * nothing as well as restart the command for example.
* *
* @param running the running state */ * @param running the running state */
void setRunning(boolean running); void setRunning(boolean running);

View File

@@ -78,7 +78,7 @@ import java.util.Map;
* @author Emmanuel Bigeon */ * @author Emmanuel Bigeon */
public final class TaskPool { public final class TaskPool {
/** The running processes. */ /** The running processes. */
private final Map<String, Task> running = new HashMap<>(); private final Map<Integer, Task> running = new HashMap<>();
/** The count for process id. */ /** The count for process id. */
private int count = 0; private int count = 0;
/** The lock for pid attribution synchronization. */ /** The lock for pid attribution synchronization. */
@@ -91,41 +91,28 @@ public final class TaskPool {
} }
/** Default constructor. */ /** Default constructor. */
public TaskPool(boolean autoClear) { public TaskPool(final boolean autoClear) {
this.autoClear = autoClear; this.autoClear = autoClear;
} }
/** Remove a task from the pool
*
* @param pid the task id */
public void remove(String pid) {
synchronized (lock) {
running.remove(pid);
count = Math.min(count, Integer.parseInt(pid));
}
}
/** Add a process in the pool. /** Add a process in the pool.
* *
* @param cmd the process * @param cmd the process
* @return the pid */ * @return the pid */
public String add(final Task cmd) { public int add(final Task cmd) {
if (cmd == null) { if (cmd == null) {
throw new IllegalArgumentException("Task cannot be null"); //$NON-NLS-1$ throw new IllegalArgumentException("Task cannot be null"); //$NON-NLS-1$
} }
final String pid; final int pid;
synchronized (lock) { synchronized (lock) {
pid = getPID(); pid = getPID();
running.put(pid, cmd); running.put(pid, cmd);
} }
if (autoClear) { if (autoClear) {
cmd.addInterruptionListener(new InterruptionListener() { cmd.addInterruptionListener(new InterruptionListener() {
@SuppressWarnings("synthetic-access")
@Override @Override
public void interrupted() { public void interrupted() {
synchronized (lock) {
remove(pid); remove(pid);
}
cmd.rmInterruptionListener(this); cmd.rmInterruptionListener(this);
} }
}); });
@@ -137,21 +124,21 @@ public final class TaskPool {
* *
* @param pid the task id * @param pid the task id
* @return the task, if any, associated to this id */ * @return the task, if any, associated to this id */
public Task get(final String pid) { public Task get(final int pid) {
synchronized (lock) { synchronized (lock) {
return running.get(pid); return running.get(Integer.valueOf(pid));
} }
} }
/** Get the next process id. /** Get the next process id.
* *
* @return the process id */ * @return the process id */
private String getPID() { private int getPID() {
synchronized (lock) { synchronized (lock) {
String pid; int pid;
do { do {
pid = Integer.toString(count++); pid = count++;
} while (running.containsKey(pid)); } while (running.containsKey(Integer.valueOf(pid)));
return pid; return pid;
} }
} }
@@ -159,7 +146,7 @@ public final class TaskPool {
/** Get the running processes' identifiers. /** Get the running processes' identifiers.
* *
* @return the pids */ * @return the pids */
public Collection<String> getPIDs() { public Collection<Integer> getPIDs() {
return new HashSet<>(running.keySet()); return new HashSet<>(running.keySet());
} }
@@ -168,6 +155,16 @@ public final class TaskPool {
return autoClear; return autoClear;
} }
/** Remove a task from the pool
*
* @param pid the task id */
public void remove(final int pid) {
synchronized (lock) {
running.remove(Integer.valueOf(pid));
count = Math.min(count, (pid));
}
}
/** Request all task to stop running. /** Request all task to stop running.
* <p> * <p>
* This call does not guaranty end of execution, it is up to the task * This call does not guaranty end of execution, it is up to the task

View File

@@ -84,9 +84,9 @@ public abstract class TaskSpawner extends Command {
private final ExecutorService threadPool; private final ExecutorService threadPool;
/** @param name the command name /** @param name the command name
* @param pool the pool */ * @param threadPool the pool */
public TaskSpawner(final String name, final TaskPool pool, protected TaskSpawner(final String name, final TaskPool pool,
ExecutorService threadPool) { final ExecutorService threadPool) {
super(name); super(name);
this.pool = pool; this.pool = pool;
this.threadPool = threadPool; this.threadPool = threadPool;
@@ -97,19 +97,17 @@ public abstract class TaskSpawner extends Command {
* @param args the arguments * @param args the arguments
* @return the process to start and add to the pool * @return the process to start and add to the pool
* @throws CommandRunException if the task creation failed */ * @throws CommandRunException if the task creation failed */
protected abstract Task createTask(ConsoleOutput out, ConsoleInput in, protected abstract Task createTask(ConsoleOutput out, ConsoleInput in, String... args)
String... args) throws CommandRunException; throws CommandRunException;
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager. * @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager.
* ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, * ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, java.lang.String[]) */
* java.lang.String[]) */
@Override @Override
public final void execute(final ConsoleOutput out, final ConsoleInput in, public final void execute(final ConsoleOutput out, final ConsoleInput in,
final String... args) throws CommandRunException { final String... args) throws CommandRunException {
final Task task = createTask(out, in, args); final Task task = createTask(out, in, args);
final Thread th = new Thread(task);
pool.add(task); pool.add(task);
threadPool.execute(th); threadPool.execute(task);
} }
} }

View File

@@ -68,36 +68,77 @@ package net.bigeon.gclc.process.io;
* #L% * #L%
*/ */
import java.io.IOException; import java.io.IOException;
import java.util.logging.Level; import java.io.InterruptedIOException;
import java.util.logging.Logger; import java.util.function.Supplier;
import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.tools.StringProvider;
/** @author Emmanuel Bigeon */ /** A console input that can be connected to and diconnected from.
*
* @author Emmanuel Bigeon */
public final class ConnectingConsoleInput implements ConsoleInput { public final class ConnectingConsoleInput implements ConsoleInput {
private static final Logger LOGGER = Logger /** If the input is closed. */
.getLogger(ConnectingConsoleInput.class.getName());
private boolean close = false; private boolean close = false;
private StringProvider prompt; /** The prompt string. */
private boolean prompting; private Supplier<String> prompt = () -> "";
/** If the input is currently in prompting state.
* <p>
* To change it you should be in a promptLock. */
private boolean prompting = false;
/** The synchronization lock for the prompting status. */
private final Object promptLock = new Object(); private final Object promptLock = new Object();
/** The synchronization lock for the connection status. */
private final Object connectionLock = new Object(); private final Object connectionLock = new Object();
private ConsoleInput connected; /** The connected console input.
private boolean disconnection; * <p>
* To use it, you should be in a promptLock and connectionLock. */
private ConsoleInput connected = null;
/** The connection state.
* <p>
* To read or modify it, you should be in a connectionLock synchronize block. */
private boolean disconnection = false;
private boolean interrupting = false;
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#close() */ * @see fr.bigeon.gclc.manager.ConsoleInput#close() */
@Override @Override
public void close() throws IOException { public void close() {
close = true; close = true;
} }
/** Connect an input.
*
* @param input the input to connect */
public void connect(final ConsoleInput input) {
disconnect();
synchronized (promptLock) {
promptLock.notifyAll();
synchronized (connectionLock) {
connected = input;
connectionLock.notifyAll();
}
}
}
/** Disconnect the current input. */
public void disconnect() {
synchronized (connectionLock) {
synchronized (promptLock) {
if (connected != null) {
disconnection = true;
connected.interruptPrompt();
connected = null;
}
}
}
}
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#getPrompt() */ * @see fr.bigeon.gclc.manager.ConsoleInput#getPrompt() */
@Override @Override
public StringProvider getPrompt() { public Supplier<String> getPrompt() {
return prompt; return prompt;
} }
@@ -105,14 +146,20 @@ public final class ConnectingConsoleInput implements ConsoleInput {
* @see fr.bigeon.gclc.manager.ConsoleInput#interruptPrompt() */ * @see fr.bigeon.gclc.manager.ConsoleInput#interruptPrompt() */
@Override @Override
public void interruptPrompt() { public void interruptPrompt() {
synchronized (connectionLock) {
synchronized (promptLock) { synchronized (promptLock) {
prompting = false; if (prompting) {
interrupting = true;
}
connectionLock.notifyAll();
if (connected != null) { if (connected != null) {
connected.interruptPrompt(); connected.interruptPrompt();
} }
prompting = false;
promptLock.notifyAll(); promptLock.notifyAll();
} }
} }
}
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#isClosed() */ * @see fr.bigeon.gclc.manager.ConsoleInput#isClosed() */
@@ -125,120 +172,156 @@ public final class ConnectingConsoleInput implements ConsoleInput {
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt() */ * @see fr.bigeon.gclc.manager.ConsoleInput#prompt() */
@Override @Override
public String prompt() throws IOException { public String prompt() throws IOException {
return prompt(prompt.apply()); return prompt(prompt.get());
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#setPrompt(fr.bigeon.gclc.tools.
* StringProvider) */
@Override
public void setPrompt(StringProvider string) {
prompt = string;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(long) */ * @see fr.bigeon.gclc.manager.ConsoleInput#prompt(long) */
@Override @Override
public String prompt(long timeout) throws IOException { public String prompt(final long timeout) throws IOException {
return prompt(prompt.apply(), timeout); return prompt(prompt.get(), timeout);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String) */ * @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String) */
@Override @Override
public String prompt(String message) throws IOException { public String prompt(final String message) throws IOException {
synchronized (promptLock) { synchronized (promptLock) {
prompting = true; prompting = true;
} }
while (prompting) { return doPrompt(message, 0, 0);
synchronized (promptLock) {
if (connected == null) {
try {
promptLock.wait();
} catch (final InterruptedException e) {
LOGGER.log(Level.WARNING, "Inerruption of console thread", e);
Thread.currentThread().interrupt();
}
} else {
final String res = connected.prompt(message);
synchronized (connectionLock) {
if (disconnection) {
disconnection = false;
} else if (prompting) {
return res;
}
}
}
}
}
return null;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String, long) */ * @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String, long) */
@Override @Override
public String prompt(String message, long timeout) throws IOException { public String prompt(final String message, final long timeout) throws IOException {
if (timeout <= 0) { if (timeout <= 0) {
return prompt(message); return prompt(message);
} }
final long end = System.currentTimeMillis() + timeout;
synchronized (promptLock) { synchronized (promptLock) {
prompting = true; prompting = true;
} }
while (prompting) { final long tic = System.currentTimeMillis();
synchronized (promptLock) { return doPrompt(message, timeout, tic);
if (connected == null) {
try {
promptLock.wait();
} catch (final InterruptedException e) {
LOGGER.log(Level.WARNING, "Inerruption of console thread", e);
Thread.currentThread().interrupt();
} }
} else {
final String res = connected.prompt(message, /** Actually do the prompting
end - System.currentTimeMillis()); *
* @param message the prompt message
* @param timeout the time to wait for an answer
* @param tic the moment the wait started
* @return the message provided through the prompting
* @throws IOException if an IO error occurred while prompting. */
private String doPrompt(final String message, final long timeout, final long tic)
throws IOException {
do {
if (!checkPrompt()) {
// We are not prompting... lets stop here.
break;
}
getConnection(getTimeoutLeft(tic, timeout));
boolean connect;
ConsoleInput actualConnected;
synchronized (connectionLock) { synchronized (connectionLock) {
if (disconnection) { connect = connected != null;
disconnection = false; actualConnected = connected;
} else if (prompting) { }
if (!connect) {
// There is no provide of input... So lets start again, until we actually
// DO get a provider of input.
continue;
}
try {
final String res = actualPrompt(message, timeout, tic, actualConnected);
// We got something from the prompting
synchronized (promptLock) {
if (prompting) {
prompting = false;
return res; return res;
} }
} }
} catch (final InterruptedIOException e) {
// The inner console was interrupted. This can mean we are
// disconnecting or actually interrupted.
if (disconnection) {
disconnection = false;
} else {
interrupting = false;
throw e;
} }
} }
} } while (checkTimeout(tic, timeout));
return null; return null;
} }
private static String actualPrompt(final String message, final long timeout,
final long tic, final ConsoleInput actualConnected) throws IOException {
final long timeoutLeft = getTimeoutLeft(tic, timeout);
if (timeoutLeft == 0) {
return actualConnected.prompt(message);
}
return actualConnected.prompt(message, timeoutLeft);
}
private static boolean checkTimeout(final long tic, final long timeout) {
return timeout <= 0 || tic + timeout > System.currentTimeMillis();
}
private static long getTimeoutLeft(final long tic, final long timeout) {
if (timeout > 0) {
return Math.max(timeout + tic - System.currentTimeMillis(), 1);
}
if (timeout < 0) {
return 1;
}
return 0;
}
/** Test if we are in prompting state.
*
* @return if the process is currently in prompting state.
* @throws InterruptedIOException if the prompting state has been interrupted */
private boolean checkPrompt() throws InterruptedIOException {
synchronized (promptLock) {
if (!prompting && interrupting) {
interrupting = false;
throw new InterruptedIOException("Prompt was interrupted");
}
return prompting;
}
}
private void getConnection(final long timeout) throws InterruptedIOException {
final long tic = System.currentTimeMillis();
synchronized (connectionLock) {
while ((connected == null || !interrupting)
&& (tic + timeout) > System.currentTimeMillis()) {
try {
connectionLock.wait(timeout);
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
}
if (interrupting) {
interrupting = false;
throw new InterruptedIOException("Prompt ws interrupted");
}
}
}
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#setPrompt(java.lang.String) */ * @see fr.bigeon.gclc.manager.ConsoleInput#setPrompt(java.lang.String) */
@Override @Override
public void setPrompt(final String prompt) { public void setPrompt(final String prompt) {
this.prompt = new StringProvider() { this.prompt = () -> prompt;
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#setPrompt(fr.bigeon.gclc.tools.
* StringProvider) */
@Override @Override
public String apply() { public void setPrompt(final Supplier<String> string) {
return prompt; prompt = string;
}
};
}
public void connect(ConsoleInput input) {
disconnect();
synchronized (promptLock) {
connected = input;
promptLock.notifyAll();
}
}
public void disconnect() {
synchronized (connectionLock) {
if (connected != null) {
disconnection = true;
synchronized (promptLock) {
connected.interruptPrompt();
}
connected = null;
}
}
} }
} }

View File

@@ -75,26 +75,37 @@ import java.util.logging.Logger;
import net.bigeon.gclc.manager.ConsoleOutput; import net.bigeon.gclc.manager.ConsoleOutput;
/** @author Emmanuel Bigeon */ /** A console output that can be connected and disconnected from.
*
* @author Emmanuel Bigeon */
public class ConnectingConsoleOutput implements ConsoleOutput { public class ConnectingConsoleOutput implements ConsoleOutput {
/** The logger. */
private static final Logger LOGGER = Logger private static final Logger LOGGER = Logger
.getLogger(ConnectingConsoleOutput.class.getName()); .getLogger(ConnectingConsoleOutput.class.getName());
/** If the undelivered message should be stored. */ /** The flag indicating that the un-delivered message should be stored. */
public static final int QUEUE = 1; public static final int QUEUE = 1;
/** If the messages should be stored in all cases. */ /** The flag indicating that the messages should be stored in all cases. */
public static final int PERSIST = 1 << 1; public static final int PERSIST = 1 << 1;
/** The connected output console */
private ConsoleOutput output; private ConsoleOutput output;
/** If the console is closed */
private boolean close = false; private boolean close = false;
/** If the messages should be stored. */
private final boolean persistent; private final boolean persistent;
/** If the message should be stored until delivery. */
private final boolean queued; private final boolean queued;
/** The messages. */
private final Deque<String> messages; private final Deque<String> messages;
/** The number of stored messages. */
private final int lines; private final int lines;
/** @param style the type of redirected output /** Create the console output.
*
* @param style the type of redirected output
* @param lines the number of lines to store */ * @param lines the number of lines to store */
public ConnectingConsoleOutput(int style, int lines) { public ConnectingConsoleOutput(final int style, final int lines) {
super(); super();
this.lines = lines; this.lines = lines;
queued = (style & QUEUE) != 0; queued = (style & QUEUE) != 0;
@@ -106,7 +117,10 @@ public class ConnectingConsoleOutput implements ConsoleOutput {
} }
} }
private synchronized void addMessage(String text) { /** Add a message.
*
* @param text the message */
private synchronized void addMessage(final String text) {
if (persistent || queued && output == null) { if (persistent || queued && output == null) {
if (messages.size() == lines) { if (messages.size() == lines) {
messages.poll(); messages.poll();
@@ -130,35 +144,10 @@ public class ConnectingConsoleOutput implements ConsoleOutput {
close = true; close = true;
} }
/* (non-Javadoc) /** Connect an output.
* @see fr.bigeon.gclc.manager.ConsoleOutput#isClosed() */ *
@Override * @param output the output */
public boolean isClosed() { public synchronized void connect(final ConsoleOutput output) {
return close;
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleOutput#print(java.lang.String) */
@Override
public void print(String text) {
addMessage(text);
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleOutput#println() */
@Override
public void println() {
addMessage(System.lineSeparator());
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleOutput#println(java.lang.String) */
@Override
public void println(String message) {
addMessage(message + System.lineSeparator());
}
public synchronized void connect(ConsoleOutput output) {
this.output = output; this.output = output;
for (final String string : messages) { for (final String string : messages) {
try { try {
@@ -173,7 +162,36 @@ public class ConnectingConsoleOutput implements ConsoleOutput {
} }
} }
/** Disconnect the currently connected output. */
public synchronized void disconnect() { public synchronized void disconnect() {
output = null; output = null;
} }
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleOutput#isClosed() */
@Override
public boolean isClosed() {
return close;
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleOutput#print(java.lang.String) */
@Override
public void print(final String text) {
addMessage(text);
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleOutput#println() */
@Override
public void println() {
addMessage(System.lineSeparator());
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleOutput#println(java.lang.String) */
@Override
public void println(final String message) {
addMessage(message + System.lineSeparator());
}
} }

View File

@@ -0,0 +1,42 @@
/** Connecting Input and Output related classes.
* <p>
* This package groups the Connecting classes that are used to be able to put
* the tasks in background and foreground and reattach the actual console input
* and outputs to the tasks' ones.
*
* @author Emmanuel Bigeon */
package net.bigeon.gclc.process.io;
/*-
* #%L
* process
* %%
* Copyright (C) 2014 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/

View File

@@ -0,0 +1,48 @@
/** This package defines elements for processes inside a console application.
* <p>
* Processes are tasks that are run in background of the application. One may
* want to temporarily connect to such a task (through
* {@link net.bigeon.gclc.process.CommandForeground}), or verify it's actual
* running status (through {@link net.bigeon.gclc.process.ProcessList}. One may
* also want to start such a process (through
* {@link net.bigeon.gclc.process.ForkCommandTask}) or close it (through
* {@link net.bigeon.gclc.process.ProcessKill}).
* <p>
* The task list can be managed by several commands to list or clear it.
*
* @author Emmanuel Bigeon */
package net.bigeon.gclc.process;
/*-
* #%L
* process
* %%
* Copyright (C) 2014 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/

View File

@@ -0,0 +1,90 @@
/**
*
*/
package net.bigeon.gclc.process;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import org.junit.Test;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.process.mocks.ForkTaskMock;
/**
* @author Emmanuel Bigeon
*
*/
public class CommandForegroundTest {
/**
* Test method for {@link net.bigeon.gclc.process.CommandForeground#CommandForeground(java.lang.String, net.bigeon.gclc.process.TaskPool)}.
*/
@Test
public void testCommandForeground() {
final CommandForeground pl = new CommandForeground("fg", new TaskPool());
assertEquals("Command name should be kept as specified", "fg",
pl.getCommandName());
}
/** Test method for
* {@link net.bigeon.gclc.process.CommandForeground#doExecute(net.bigeon.gclc.manager.ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, net.bigeon.gclc.command.CommandParameters)}.
*
* @throws CommandRunException if a valid command failed */
@Test
public void testDoExecuteConsoleOutputConsoleInputCommandParameters()
throws CommandRunException {
final TaskPool pool = new TaskPool(false);
final CommandForeground pl = new CommandForeground("fg", pool);
try {
pl.execute(null, null, "-delai", "1");
fail("PID should be mandatory");
} catch (final CommandRunException e) {
assertEquals("No pid specified is a usage error",
CommandRunExceptionType.USAGE, e.getType());
}
try {
pl.execute(null, null, "-pid", "invalid", "-delai", "1");
fail("PID should be a number");
} catch (final CommandRunException e) {
assertEquals("Invalid pid specified is a usage error",
CommandRunExceptionType.USAGE, e.getType());
}
try {
pl.execute(null, null, "-pid", "2", "-delai", "1");
fail("PID should exist");
} catch (final CommandRunException e) {
assertEquals("Inexistent pid specified is a run error",
CommandRunExceptionType.EXECUTION, e.getType());
}
final ForkTaskMock cmd = new ForkTaskMock();
final int id = pool.add(cmd);
pl.execute(null, null, "-pid", Integer.toString(id), "-delai", "1");
try {
pl.execute(null, null, "-pid", Integer.toString(id), "-delai", "-1");
fail("delai should be a posistive number");
} catch (final CommandRunException e) {
assertEquals("Negative delai specified is a usage error",
CommandRunExceptionType.USAGE, e.getType());
}
assert cmd.getRunCall() == 0;
}
/**
* Test method for {@link net.bigeon.gclc.process.CommandForeground#tip()}.
*/
@Test
public void testTip() {
final CommandForeground pl = new CommandForeground("fg", new TaskPool());
assertNotNull("Command tip should be defined", pl.tip());
assertNotNull("Command usage should be defined", pl.usagePattern());
assertNotNull("Command usage should be defined", pl.usageDetail());
}
}

View File

@@ -0,0 +1,86 @@
/**
*
*/
package net.bigeon.gclc.process;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import org.junit.Test;
import net.bigeon.gclc.command.ICommandProvider;
import net.bigeon.gclc.command.SubedCommand;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.exception.InvalidCommandName;
import net.bigeon.gclc.process.mocks.CommandMock;
/**
* @author Emmanuel Bigeon
*
*/
public class CommandForkTest {
/**
* Test method for {@link net.bigeon.gclc.process.CommandFork#CommandFork(java.lang.String, net.bigeon.gclc.command.ICommandProvider, net.bigeon.gclc.process.TaskPool)}.
*/
@Test
public void testCommandForkStringICommandProviderTaskPool() {
final TaskPool pool = new TaskPool();
final ICommandProvider provider = new SubedCommand("test");
final CommandFork pl = new CommandFork("fork", provider, pool);
assertEquals("Command name should be preserved", "fork", pl.getCommandName());
}
/** Test method for
* {@link net.bigeon.gclc.process.CommandFork#execute(net.bigeon.gclc.manager.ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[])}.
*
* @throws InvalidCommandName if the test init failed
* @throws CommandRunException if the command execution failed in the test */
@Test
public void testExecute() throws InvalidCommandName, CommandRunException {
final TaskPool pool = new TaskPool(false);
final ICommandProvider provider = new SubedCommand("test");
final CommandFork pl = new CommandFork("fork", provider, pool);
try {
pl.execute(null, null);
fail("No command specified should through an exception");
} catch (final CommandRunException e) {
assertEquals("No command specified is a usage error",
CommandRunExceptionType.USAGE, e.getType());
}
try {
pl.execute(null, null, "invalid");
fail("Invalid command specified should through an exception");
} catch (final CommandRunException e) {
assertEquals("Invalid command specified is a usage error",
CommandRunExceptionType.USAGE, e.getType());
}
final CommandMock mock = new CommandMock();
provider.add(mock);
pl.execute(null, null, mock.getCommandName());
final ForkTask task = (ForkTask) pool.get(pool.getPIDs().iterator().next());
task.join(null, null, 1000);
assertEquals("Command should be executed when forked", 1, mock.getExecuteCall());
}
/**
* Test method for {@link net.bigeon.gclc.process.CommandFork#tip()}.
*/
@Test
public void testTip() {
final TaskPool pool = new TaskPool();
final ICommandProvider provider = new SubedCommand("test");
final CommandFork pl = new CommandFork("fork", provider, pool);
assertNotNull("Command tip should be defined", pl.tip());
assertNotNull("Command usage should be defined", pl.usagePattern());
assertNotNull("Command usage should be defined", pl.usageDetail());
}
}

View File

@@ -0,0 +1,35 @@
/**
*
*/
package net.bigeon.gclc.process;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import net.bigeon.gclc.process.mocks.CommandMock;
/**
* @author Emmanuel Bigeon
*
*/
public class ForkCommandTaskTest {
/** Test method for
* {@link net.bigeon.gclc.process.ForkCommandTask#ForkCommandTask(net.bigeon.gclc.command.ICommand, java.lang.String[], int)}.
*
* @throws InterruptedException if there is an error in the thread join */
@Test
public void testForkCommandTask() throws InterruptedException {
final CommandMock cmd = new CommandMock();
final ForkCommandTask task = new ForkCommandTask(cmd, new String[0], 1);
final Thread th = new Thread(task);
th.start();
th.join();
assertEquals("Task should be executed once", 1, cmd.getExecuteCall());
assertEquals("Names should be consistent between task and command",
cmd.getCommandName(), task.getName());
}
}

View File

@@ -0,0 +1,156 @@
/**
*
*/
package net.bigeon.gclc.process;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.manager.PipedConsoleInput;
import net.bigeon.gclc.manager.PipedConsoleOutput;
/** @author Emmanuel Bigeon */
public class ForkTaskTest {
@Test
public void testGenericForkTask() throws IOException {
final ForkTask task = new ForkTask(5) {
@Override
public String getName() {
return "name";
}
@Override
protected void doRun() throws CommandRunException {
String msg;
try {
msg = in.prompt();
} catch (final IOException e) {
throw new CommandRunException(CommandRunExceptionType.INTERACTION,
"Unable to prompt user");
}
out.println(msg);
}
};
final Thread execThread = new Thread(task, "Task");
execThread.start();
try {
execThread.join(100);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try (PipedConsoleOutput pco = new PipedConsoleOutput();
PipedConsoleInput pci = new PipedConsoleInput(null)) {
while (!pco.available()) {
pci.type("ok");
task.join(pco, pci, 1000);
}
assertEquals("Positive execution", "ok", pco.readNextLine());
}
assertFalse("Running state should be updated by task on its completion",
task.isRunning());
}
@Test
public void testTaskInterruption() throws IOException, InterruptedException {
final ForkTask task = new ForkTask(5) {
@Override
public String getName() {
return "name";
}
@Override
protected void doRun() throws CommandRunException {
while (isRunning()) {
String msg;
try {
msg = in.prompt(1000);
} catch (final IOException e) {
throw new CommandRunException(CommandRunExceptionType.INTERACTION,
"Unable to prompt user");
}
if (msg != null) {
out.println(msg);
}
}
}
};
final Thread execThread = new Thread(task, "Task");
execThread.start();
execThread.join(100);
final AtomicBoolean interrupted = new AtomicBoolean(false);
final AtomicBoolean interrupted2 = new AtomicBoolean(false);
final InterruptionListener listener = new InterruptionListener() {
@Override
public void interrupted() {
interrupted.set(true);
}
};
final InterruptionListener listener2 = new InterruptionListener() {
@Override
public void interrupted() {
interrupted2.set(true);
}
};
task.addInterruptionListener(listener);
task.addInterruptionListener(listener2);
task.rmInterruptionListener(listener2);
assertFalse("Interruption should not be notified before actual interruption",
interrupted.get());
assertTrue("Task should be started", task.isStarted());
task.setRunning(false);
try (PipedConsoleOutput pco = new PipedConsoleOutput();
PipedConsoleInput pci = new PipedConsoleInput(null)) {
task.join(pco, pci, 2000);
}
execThread.join();
assertTrue("Interruption should be notified to listeners", interrupted.get());
assertFalse("Running state should be updated by task on its completion",
task.isRunning());
assertFalse("Interruption should not be notified to removed listeners",
interrupted2.get());
task.rmInterruptionListener(listener);
}
@Test
public void testFailingTask() throws InterruptedException {
final ForkTask task = new ForkTask(5) {
@Override
public String getName() {
return "name";
}
@Override
protected void doRun() throws CommandRunException {
throw new CommandRunException("Error");
}
};
final Thread execThread = new Thread(task, "Task");
execThread.start();
execThread.join();
assertNotNull("Exception should be forwarded", task.getException());
}
}

View File

@@ -0,0 +1,34 @@
/**
*
*/
package net.bigeon.gclc.process;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import net.bigeon.gclc.command.SubedCommand;
import net.bigeon.gclc.exception.InvalidCommandName;
/**
* @author Emmanuel Bigeon
*
*/
public class ProcessAttachementTest {
/** Test method for
* {@link net.bigeon.gclc.process.ProcessAttachement#attach(net.bigeon.gclc.command.ICommandProvider)}.
*
* @throws InvalidCommandName if the command has already some subcommand that is
* incompatible */
@Test
public void testAttach() throws InvalidCommandName {
final TaskPool pool = new TaskPool();
final ProcessAttachement attachement = new ProcessAttachement(pool);
final SubedCommand testPoint = new SubedCommand("test");
attachement.attach(testPoint);
assertNotNull("Commands should be added", testPoint.get("kill"));
assertNotNull("Commands should be added", testPoint.get("list"));
}
}

View File

@@ -0,0 +1,53 @@
/**
*
*/
package net.bigeon.gclc.process;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import net.bigeon.gclc.process.mocks.TaskMock;
/**
* @author Emmanuel Bigeon
*
*/
public class ProcessClearTest {
/**
* Test method for {@link net.bigeon.gclc.process.ProcessClear#ProcessClear(java.lang.String, net.bigeon.gclc.process.TaskPool)}.
*/
@Test
public void testProcessClear() {
final ProcessClear pl = new ProcessClear("clear", new TaskPool());
assertEquals("Command name should be kept as specified", "clear",
pl.getCommandName());
}
/**
* Test method for {@link net.bigeon.gclc.process.ProcessClear#execute(net.bigeon.gclc.manager.ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[])}.
*/
@Test
public void testExecute() {
final TaskPool pool = new TaskPool();
final TaskMock task = new TaskMock();
pool.add(task);
final ProcessClear pl = new ProcessClear("clear", pool);
pl.execute(null, null);
assertTrue("cleared pool should be empty", pool.getPIDs().isEmpty());
}
/**
* Test method for {@link net.bigeon.gclc.process.ProcessClear#tip()}.
*/
@Test
public void testTip() {
final ProcessClear pl = new ProcessClear("clear", new TaskPool());
assertNotNull("Command tip should be defined", pl.tip());
assertNotNull("Command usage should be defined", pl.usageDetail());
}
}

View File

@@ -0,0 +1,62 @@
/**
*
*/
package net.bigeon.gclc.process;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import org.junit.Test;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.process.mocks.TaskMock;
/** @author Emmanuel Bigeon */
public class ProcessKillTest {
/** Test method for
* {@link net.bigeon.gclc.process.ProcessKill#ProcessKill(java.lang.String, net.bigeon.gclc.process.TaskPool)}. */
@Test
public void testProcessKill() {
final ProcessList pl = new ProcessList("kill", new TaskPool());
assertEquals("Command name should be kept as specified", "kill",
pl.getCommandName());
}
/** Test method for
* {@link net.bigeon.gclc.process.ProcessKill#execute(net.bigeon.gclc.manager.ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[])}.
*
* @throws CommandRunException if an error occured in test */
@Test
public void testExecute() throws CommandRunException {
final TaskPool pool = new TaskPool();
final TaskMock task = new TaskMock();
final int id = pool.add(task);
final ProcessKill pl = new ProcessKill("kill", pool);
task.reset();
try {
pl.execute(null, null);
fail("Execution with no pid should raise exception");
} catch (final CommandRunException e) {
assertEquals("Usage exception expected", CommandRunExceptionType.USAGE,
e.getType());
}
pl.execute(null, null, Integer.toString(id));
assertEquals("Task should be shutdown", 1, task.getNumberSetRunning());
task.reset();
pl.execute(null, null, Integer.toString(id));
assertEquals("Task should be shutdown", 1, task.getNumberSetRunning());
}
/** Test method for {@link net.bigeon.gclc.process.ProcessKill#tip()}. */
@Test
public void testTip() {
final ProcessKill pl = new ProcessKill("kill", new TaskPool());
assertNotNull("Command tip should be defined", pl.tip());
assertNotNull("Command usage should be defined", pl.usagePattern());
assertNotNull("Command usage should be defined", pl.usageDetail());
}
}

View File

@@ -0,0 +1,67 @@
/**
*
*/
package net.bigeon.gclc.process;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import org.junit.Test;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.manager.PipedConsoleOutput;
import net.bigeon.gclc.process.mocks.TaskMock;
/** @author Emmanuel Bigeon */
public class ProcessListTest {
/** Test method for
* {@link net.bigeon.gclc.process.ProcessList#ProcessList(java.lang.String, net.bigeon.gclc.process.TaskPool)}. */
@Test
public void testProcessList() {
final ProcessList pl = new ProcessList("list", new TaskPool());
assertEquals("Command name should be kept as specified", "list",
pl.getCommandName());
}
/** Test method for
* {@link net.bigeon.gclc.process.ProcessList#execute(net.bigeon.gclc.manager.ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[])}.
*
* @throws CommandRunException if an error occured
* @throws IOException if an error in initialization of console occured */
@Test
public void testExecute() throws CommandRunException, IOException {
final TaskPool pool = new TaskPool();
final TaskMock task = new TaskMock();
pool.add(task);
final ProcessList pl = new ProcessList("list", pool);
final PipedConsoleOutput out = new PipedConsoleOutput();
pl.execute(out, null);
assertTrue("Task should be contained in list",
out.readNextLine().contains(task.getName()));
out.close();
try {
pl.execute(out, null);
fail("Printing element to closed console should raise interaction exception");
} catch (final CommandRunException e) {
assertEquals("Console printing error should be an interaction error",
CommandRunExceptionType.INTERACTION, e.getType());
}
}
/** Test method for {@link net.bigeon.gclc.process.ProcessList#tip()}. */
@Test
public void testTip() {
final ProcessList pl = new ProcessList("list", new TaskPool());
assertNotNull("Command tip should be defined", pl.tip());
assertNotNull("Command usage should be defined", pl.usageDetail());
}
}

View File

@@ -0,0 +1,33 @@
/**
*
*/
package net.bigeon.gclc.process;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import net.bigeon.gclc.command.SubedCommand;
import net.bigeon.gclc.exception.InvalidCommandName;
/** @author Emmanuel Bigeon */
public class ScreenAttachementTest {
/** Test method for
* {@link net.bigeon.gclc.process.ScreenAttachement#attach(net.bigeon.gclc.command.ICommandProvider)}.
*
* @throws InvalidCommandName if an error occured in attachement */
@Test
public void testAttach() throws InvalidCommandName {
final TaskPool pool = new TaskPool();
final ScreenAttachement attachement = new ScreenAttachement(pool, 15);
final SubedCommand testPoint = new SubedCommand("test");
attachement.attach(testPoint);
assertNotNull("Commands should be added", testPoint.get("terminate"));
assertNotNull("Commands should be added", testPoint.get("list"));
assertNotNull("Commands should be added", testPoint.get("clear"));
assertNotNull("Commands should be added", testPoint.get("fg"));
assertNotNull("Commands should be added", testPoint.get("fork"));
}
}

View File

@@ -0,0 +1,77 @@
package net.bigeon.gclc.process;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import net.bigeon.gclc.process.mocks.TaskMock;
public class TaskPoolTest {
public TaskMock task = new TaskMock();
public TaskPool pool = new TaskPool(false);
@Test
public void testTaskPool() {
assertFalse("task pool should not auto clear if specified",
pool.isAutoClearing());
pool = new TaskPool();
assertTrue("Default task pool should not auto clear", pool.isAutoClearing());
}
@Test
public void testAdd() {
final int id = pool.add(task);
assertTrue("Added task pid should be in pool", pool.getPIDs().contains(id));
assertEquals("Added task should be in pool", task, pool.get(id));
}
@Test
public void testGet() {
assertNull("Inexistent task id should provide null as task in pool",
pool.get(60));
final int id = pool.add(task);
assertEquals("Added task should be in pool at its id", task, pool.get(id));
assertNull("Added task should be in pool at its id", pool.get(id + 1));
}
@Test
public void testGetPIDs() {
assertTrue("new pool should be empty", pool.getPIDs().isEmpty());
final int id = pool.add(task);
assertNull("Added task should be in pool at its id", pool.get(id + 1));
assertEquals(
"Pool task addition should change the pid collection size accordingly", 1,
pool.getPIDs().size());
}
@Test
public void testRemove() {
assertTrue("new pool should be empty", pool.getPIDs().isEmpty());
pool.remove(60);
assertTrue("removal of inexistent task should be NOP", pool.getPIDs().isEmpty());
final int id = pool.add(task);
assertEquals(
"Pool task addition should change the pid collection size accordingly", 1,
pool.getPIDs().size());
pool.remove(id + 1);
assertEquals("removal of inexistent task should be NOP", 1,
pool.getPIDs().size());
pool.remove(id);
assertTrue("removal of task should reduce the number of tasks",
pool.getPIDs().isEmpty());
}
@Test
public void testShutdown() {
final int id = pool.add(task);
task.reset();
pool.shutdown();
assertEquals("Shutdown should request shut down of tasks, only once", 1,
task.getNumberSetRunning());
}
}

View File

@@ -0,0 +1,80 @@
/**
*
*/
package net.bigeon.gclc.process;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Test;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput;
/** @author Emmanuel Bigeon */
public class TaskSpawnerTest {
/** Test method for
* {@link net.bigeon.gclc.process.TaskSpawner#execute(net.bigeon.gclc.manager.ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[])}.
*
* @throws CommandRunException if an error occurred */
@Test
public void testExecute() throws CommandRunException {
final Task task = new Task() {
@Override
public void run() {
//
}
@Override
public void setRunning(final boolean running) {
//
}
@Override
public void rmInterruptionListener(final InterruptionListener listener) {
//
}
@Override
public boolean isRunning() {
return false;
}
@Override
public String getName() {
return "abc";
}
@Override
public void addInterruptionListener(final InterruptionListener listener) {
//
}
};
final TaskPool pool = new TaskPool();
assertTrue("Pool not empty", pool.getPIDs().isEmpty());
final ExecutorService service = Executors.newSingleThreadExecutor();
final TaskSpawner spawner = new TaskSpawner("name", pool, service) {
@Override
public String tip() {
return "tip";
}
@Override
protected String usageDetail() {
return "no details";
}
@Override
protected Task createTask(final ConsoleOutput out, final ConsoleInput in,
final String... args) throws CommandRunException {
return task;
}
};
spawner.execute(null, null);
assertEquals("Added the task", 1, pool.getPIDs().size());
final Integer id = pool.getPIDs().iterator().next();
assertEquals("Added task is not the one", task, pool.get(id));
}
}

View File

@@ -0,0 +1,148 @@
package net.bigeon.gclc.process.io;
import static org.junit.Assert.*;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import org.junit.Test;
import net.bigeon.gclc.manager.PipedConsoleInput;
import net.bigeon.gclc.manager.StreamConsoleInput;
import net.bigeon.test.junitmt.FunctionalTestRunnable;
import net.bigeon.test.junitmt.ThreadTest;
import net.bigeon.test.junitmt.api.ATestRunnable;
import net.bigeon.test.junitmt.api.TestFunction;
public class ConnectingConsoleInputTest {
@Test
public void testConnect() throws IOException {
final ConnectingConsoleInput in = new ConnectingConsoleInput();
final PipedConsoleInput pci = new PipedConsoleInput(null);
assertNull("Inputs should not transmit before connection", in.prompt(1000));
in.connect(pci);
pci.type("Some message");
assertEquals("Messages should be buffered", "Some message", in.prompt());
}
@Test
public void testDisconnect() throws IOException {
final ConnectingConsoleInput in = new ConnectingConsoleInput();
final PipedConsoleInput pci = new PipedConsoleInput(null);
assertNull("Inputs should not transmit before connection", in.prompt(1000));
in.connect(pci);
pci.type("Some message");
assertEquals("Messages should be buffered", "Some message", in.prompt());
in.disconnect();
assertNull("Disconnected inputs should not transmit anymore", in.prompt(1000));
}
@Test
public void testIsClosed() throws IOException {
final ConnectingConsoleInput in = new ConnectingConsoleInput();
assertFalse("New input should not be closed", in.isClosed());
final PipedConsoleInput pci = new PipedConsoleInput(null);
in.connect(pci);
pci.close();
assertFalse("Connected input closing should not close connecting", in.isClosed());
final PipedConsoleInput pci2 = new PipedConsoleInput(null);
in.connect(pci2);
in.close();
assertTrue("Input should close on request", in.isClosed());
assertFalse("Connected input should not be closed by connecting closure",
pci2.isClosed());
}
@Test
public void testCoveragePrompt() {
final ConnectingConsoleInput in = new ConnectingConsoleInput();
in.setPrompt("test");
assertEquals("Prompt should be set correctly", "test", in.getPrompt().get());
final Supplier<String> prompt = () -> "other";
in.setPrompt(prompt);
assertEquals("Prompt should be set correctly", prompt, in.getPrompt());
}
@Test
public void testPromptSequence() throws IOException, InterruptedException {
final ConnectingConsoleInput in = new ConnectingConsoleInput();
// Unconnected
final AtomicBoolean ended = new AtomicBoolean(false);
final TestFunction one = () -> {
try {
final String res1 = in.prompt("m1", -1);
fail("interruption of infinite waiting prompt should cause error, but was "
+ res1);
} catch (final InterruptedIOException e1) {
// ok
}
try {
final String res2 = in.prompt("m2", 25000);
fail("interruption of finite waiting prompt should cause error, but was "
+ res2);
} catch (final InterruptedIOException e2) {
// ok
}
synchronized (ended) {
ended.set(true);
try {
assertNull("Overtime should return null", in.prompt("m3", 200));
} catch (final InterruptedIOException e3) {
fail("Unexpected interruption error in overtime");
}
}
};
final ATestRunnable runnable = new FunctionalTestRunnable(one);
final Thread th = new Thread(runnable, "TestPromptSequence");
final Thread inter = new Thread(() -> {
while (!ended.get()) {
try {
th.join(100);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (ended) {
if (!ended.get()) {
in.interruptPrompt();
}
}
}
});
th.start();
inter.start();
ThreadTest.assertRuns(th, runnable);
inter.join();
ended.set(false);
final PipedOutputStream os = new PipedOutputStream();
final PipedInputStream pis = new PipedInputStream(os);
in.connect(new StreamConsoleInput(null, pis, StandardCharsets.UTF_8));
final ATestRunnable runnable2 = new FunctionalTestRunnable(one);
final Thread th2 = new Thread(runnable2);
final Thread inter2 = new Thread(() -> {
while (!ended.get()) {
try {
th2.join(100);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (ended) {
if (!ended.get()) {
in.interruptPrompt();
}
}
}
});
th2.start();
inter2.start();
ThreadTest.assertRuns(th2, runnable2);
}
}

View File

@@ -0,0 +1,88 @@
package net.bigeon.gclc.process.io;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import org.junit.Test;
import net.bigeon.gclc.manager.PipedConsoleOutput;
public class ConnectingConsoleOutputTest {
@Test
public void testConnect() throws IOException {
final ConnectingConsoleOutput out = new ConnectingConsoleOutput(
ConnectingConsoleOutput.QUEUE, 10);
final PipedConsoleOutput output = new PipedConsoleOutput();
out.println("Some message");
assertFalse("Message should not be transmitted", output.available());
// Connect
out.connect(output);
// Connected outputs should receive data
final String res = output.readNextLine();
assertEquals("Message should be correctly transmitted", "Some message", res);
}
@Test
public void testDisconnect() throws IOException {
final ConnectingConsoleOutput out = new ConnectingConsoleOutput(
ConnectingConsoleOutput.QUEUE, 10);
final PipedConsoleOutput output = new PipedConsoleOutput();
// NOP disconnection when no connection
out.disconnect();
// Connect
out.connect(output);
// Connected outputs should receive data
out.println("Some message");
final String res = output.readNextLine();
assertEquals("Message should be correctly transmitted", "Some message", res);
out.disconnect();
out.println("Some message");
assertFalse("Message should not be transmitted", output.available());
}
@Test
public void testIsClosed() throws IOException {
final ConnectingConsoleOutput out = new ConnectingConsoleOutput(
ConnectingConsoleOutput.QUEUE, 10);
final PipedConsoleOutput output = new PipedConsoleOutput();
assertFalse("New output should not be closed", out.isClosed());
// Connect
out.connect(output);
output.close();
assertFalse("Output should not close when connected outputs do", out.isClosed());
final PipedConsoleOutput output2 = new PipedConsoleOutput();
out.connect(output2);
out.close();
assertTrue("Output should close on request", out.isClosed());
assertFalse("Output should not close connected output when they do",
output2.isClosed());
}
@Test
public void testPrint() throws IOException {
final ConnectingConsoleOutput out = new ConnectingConsoleOutput(
ConnectingConsoleOutput.PERSIST, 10);
out.print("Some text");
out.println();
final PipedConsoleOutput output = new PipedConsoleOutput();
out.connect(output);
String res = output.readNextLine();
assertEquals("Message should be correctly transmitted", "Some text", res);
out.disconnect();
// Persist should be reprinted on any connect
out.connect(output);
res = output.readNextLine();
assertEquals("Message should be correctly transmitted", "Some text", res);
out.disconnect();
}
}

View File

@@ -0,0 +1,60 @@
/**
*
*/
package net.bigeon.gclc.process.mocks;
import java.io.IOException;
import net.bigeon.gclc.command.ICommand;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput;
/** @author Emmanuel Bigeon */
public class CommandMock implements ICommand {
private int executeCall = 0;
private int tipCall = 0;
private int helpCall = 0;
private int commandNameCall = 0;
/* (non-Javadoc)
* @see net.bigeon.gclc.command.ICommand#execute(net.bigeon.gclc.manager.
* ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[]) */
@Override
public void execute(final ConsoleOutput out, final ConsoleInput in,
final String... args) throws CommandRunException {
executeCall++;
}
/* (non-Javadoc)
* @see net.bigeon.gclc.command.ICommand#getCommandName() */
@Override
public String getCommandName() {
commandNameCall++;
return "name";
}
/* (non-Javadoc)
* @see
* net.bigeon.gclc.command.ICommand#help(net.bigeon.gclc.manager.ConsoleOutput,
* java.lang.String[]) */
@Override
public void help(final ConsoleOutput output, final String... args)
throws IOException {
helpCall++;
}
/* (non-Javadoc)
* @see net.bigeon.gclc.command.ICommand#tip() */
@Override
public String tip() {
tipCall++;
return "tip";
}
/** @return the executeCall */
public int getExecuteCall() {
return executeCall;
}
}

View File

@@ -0,0 +1,43 @@
/**
*
*/
package net.bigeon.gclc.process.mocks;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.process.ForkTask;
import net.bigeon.gclc.process.Task;
/**
* @author Emmanuel Bigeon
*
*/
public class ForkTaskMock extends ForkTask implements Task {
private int runCall = 0;
/** Default constructor. */
public ForkTaskMock() {
super(10);
}
/* (non-Javadoc)
* @see net.bigeon.gclc.process.Task#getName()
*/
@Override
public String getName() {
return "name";
}
/* (non-Javadoc)
* @see net.bigeon.gclc.process.ForkTask#doRun()
*/
@Override
protected void doRun() throws CommandRunException {
runCall++;
}
/** @return the runCall */
public int getRunCall() {
return runCall;
}
}

View File

@@ -0,0 +1,57 @@
package net.bigeon.gclc.process.mocks;
import net.bigeon.gclc.process.InterruptionListener;
import net.bigeon.gclc.process.Task;
public class TaskMock implements Task {
private int runCall = 0;
private int setRunningCall = 0;
@Override
public void run() {
runCall++;
}
@Override
public void addInterruptionListener(final InterruptionListener listener) {
// TODO Auto-generated method stub
}
@Override
public String getName() {
return "name";
}
@Override
public boolean isRunning() {
// TODO Auto-generated method stub
return false;
}
@Override
public void rmInterruptionListener(final InterruptionListener listener) {
// TODO Auto-generated method stub
}
@Override
public void setRunning(final boolean running) {
setRunningCall++;
}
/**
*
*/
public void reset() {
runCall = 0;
setRunningCall = 0;
}
/** @return the number of calls to set running method */
public int getNumberSetRunning() {
return setRunningCall;
}
}

View File

@@ -0,0 +1,4 @@
/** Mock definitions for testing.
*
* @author Emmanuel Bigeon */
package net.bigeon.gclc.process.mocks;

View File

@@ -1,64 +1,22 @@
<!-- GCLC Socket, Socket implementation of GCLC -->
<!-- Copyright (C) 2014-2017 E. Bigeon -->
<!-- mailto:emmanuel@bigeon.fr -->
<!-- -->
<!-- This software is governed by the CeCILL license under French law and -->
<!-- abiding by the rules of distribution of free software. You can use, -->
<!-- modify and/or redistribute the software under the terms of the CeCILL -->
<!-- license as circulated by CEA, CNRS and INRIA at the following URL -->
<!-- "http://www.cecill.info". -->
<!-- -->
<!-- As a counterpart to the access to the source code and rights to copy, -->
<!-- modify and redistribute granted by the license, users are provided only -->
<!-- with a limited warranty and the software's author, the holder of the -->
<!-- economic rights, and the successive licensors have only limited -->
<!-- liability. -->
<!-- -->
<!-- In this respect, the user's attention is drawn to the risks associated -->
<!-- with loading, using, modifying and/or developing or reproducing the -->
<!-- software by the user in light of its specific status of free software, -->
<!-- that may mean that it is complicated to manipulate, and that also -->
<!-- therefore means that it is reserved for developers and experienced -->
<!-- professionals having in-depth computer knowledge. Users are therefore -->
<!-- encouraged to load and test the software's suitability as regards their -->
<!-- requirements in conditions enabling the security of their systems and/or -->
<!-- data to be ensured and, more generally, to use and operate it in the -->
<!-- same conditions as regards security. -->
<!-- -->
<!-- The fact that you are presently reading this means that you have had -->
<!-- knowledge of the CeCILL license and that you accept its terms. -->
<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/maven-v4_0_0.xsd"> <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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>gclc-socket</artifactId>
<version>1.1.11-SNAPSHOT</version>
<packaging>jar</packaging>
<url>http://www.bigeon.net</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.scm.id>git.code.bigeon.net</project.scm.id>
<license.licenseName>cecill_2.1</license.licenseName>
<copyright.email>emmanuel@bigeon.fr</copyright.email>
</properties>
<dependencies>
<dependency>
<groupId>net.bigeon</groupId>
<artifactId>gclc</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>net.bigeon</groupId>
<artifactId>smu</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<parent> <parent>
<groupId>net.bigeon.config</groupId> <groupId>net.bigeon.config</groupId>
<artifactId>ebigeon-config</artifactId> <artifactId>ebigeon-public-conf</artifactId>
<version>1.8.9</version> <version>1.0.12</version>
</parent> </parent>
<groupId>net.bigeon.gclc</groupId>
<artifactId>socket</artifactId>
<version>1.1.19-SNAPSHOT</version>
<packaging>jar</packaging>
<name>GCLC Socket</name>
<description>Socket implementation of GCLC</description>
<url>https://bigeon.net/projects/gclc.html</url>
<inceptionYear>2016</inceptionYear>
<organization>
<name>Bigeon</name>
<url>https://bigeon.net/</url>
</organization>
<licenses> <licenses>
<license> <license>
<distribution>manual</distribution> <distribution>manual</distribution>
@@ -66,6 +24,42 @@
<url>https://cecill.info/licences/Licence_CeCILL_V2.1-en.html</url> <url>https://cecill.info/licences/Licence_CeCILL_V2.1-en.html</url>
</license> </license>
</licenses> </licenses>
<developers>
<developer>
<email>emmanuel@bigeon.fr</email>
<name>Emmanuel Bigeon</name>
<url>bigeon.net</url>
<roles>
<role>PM</role>
</roles>
</developer>
</developers>
<scm>
<developerConnection>scm:git:gitea@git.code.bigeon.net:emmanuel/gclc-core.git</developerConnection>
<tag>HEAD</tag>
</scm>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<license.licenseName>cecill_2.1</license.licenseName>
</properties>
<dependencies>
<dependency>
<groupId>net.bigeon</groupId>
<artifactId>gclc</artifactId>
<version>2.1.5</version>
</dependency>
<dependency>
<groupId>net.bigeon</groupId>
<artifactId>smu</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>net.bigeon.test</groupId>
<artifactId>junitmt</artifactId>
<version>1.0.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<reporting> <reporting>
<plugins> <plugins>
<plugin> <plugin>
@@ -80,22 +74,4 @@
</plugin> </plugin>
</plugins> </plugins>
</reporting> </reporting>
<developers>
<developer>
<email>emmanuel@bigeon.fr</email>
<name>Emmanuel Bigeon</name>
<url>bigeon.net</url>
<roles>
<role>PM</role>
</roles>
</developer>
</developers>
<name>GCLC Socket</name>
<description>Socket implementation of GCLC</description>
<scm>
<developerConnection>scm:git:gogs@git.code.bigeon.net:emmanuel/gclc.git</developerConnection>
<tag>HEAD</tag>
</scm>
<groupId>net.bigeon.gclc</groupId>
<inceptionYear>2016</inceptionYear>
</project> </project>

View File

@@ -61,12 +61,13 @@ public final class DConnexionManager<T> implements ConnexionManager<T> {
/** The lock for modification of {@link #counters}. */ /** The lock for modification of {@link #counters}. */
private final Object counterLock = new Object(); private final Object counterLock = new Object();
/** The count of connexions. */ /** The count of connexions. */
private int count = 0; private int count;
/** Default.constructor. */ /** Default.constructor. */
public DConnexionManager() { public DConnexionManager() {
// //
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see * @see
* net.bigeon.gclc.socket.ConnexionManager#addConnexion(java.lang.Object) */ * net.bigeon.gclc.socket.ConnexionManager#addConnexion(java.lang.Object) */
@@ -80,11 +81,12 @@ public final class DConnexionManager<T> implements ConnexionManager<T> {
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see * @see net.bigeon.gclc.socket.ConnexionManager#disconnect(java.lang.String) */
* net.bigeon.gclc.socket.ConnexionManager#disconnect(java.lang.String) */
@Override @Override
public T disconnect(final String id) { public T disconnect(final String id) {
if (connecteds.containsKey(id)) { if (!connecteds.containsKey(id)) {
return null;
}
final T disc = connecteds.remove(id); final T disc = connecteds.remove(id);
final Object lock = locks.get(id); final Object lock = locks.get(id);
synchronized (lock) { synchronized (lock) {
@@ -102,8 +104,6 @@ public final class DConnexionManager<T> implements ConnexionManager<T> {
} }
return disc; return disc;
} }
return null;
}
/* (non-Javadoc) /* (non-Javadoc)
* @see net.bigeon.gclc.socket.ConnexionManager#get(java.lang.String) */ * @see net.bigeon.gclc.socket.ConnexionManager#get(java.lang.String) */
@@ -120,8 +120,7 @@ public final class DConnexionManager<T> implements ConnexionManager<T> {
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see * @see net.bigeon.gclc.socket.ConnexionManager#isConnected(java.lang.String) */
* net.bigeon.gclc.socket.ConnexionManager#isConnected(java.lang.String) */
@Override @Override
public boolean isConnected(final String id) { public boolean isConnected(final String id) {
return connecteds.containsKey(id); return connecteds.containsKey(id);
@@ -144,7 +143,8 @@ public final class DConnexionManager<T> implements ConnexionManager<T> {
* *
* @return a new ID */ * @return a new ID */
private String newID() { private String newID() {
return "Client " + count++; //$NON-NLS-1$ final int c = count++;
return "Client " + c; //$NON-NLS-1$
} }
/* (non-Javadoc) /* (non-Javadoc)
@@ -154,8 +154,8 @@ public final class DConnexionManager<T> implements ConnexionManager<T> {
@Override @Override
public void releaseDisconnexionLock(final String id) { public void releaseDisconnexionLock(final String id) {
synchronized (counterLock) { synchronized (counterLock) {
counters.put(id, Integer counters.put(id,
.valueOf(Math.max(counters.get(id).intValue() - 1, 0))); Integer.valueOf(Math.max(counters.get(id).intValue() - 1, 0)));
counterLock.notifyAll(); counterLock.notifyAll();
} }
} }

View File

@@ -0,0 +1,14 @@
/**
*
*/
package net.bigeon.gclc.socket;
/** Interface for listener of brutal disconnection from a pluggable
* input/output.
*
* @author Emmanuel Bigeon */
@FunctionalInterface
public interface DisconnexionListener {
/** Indicate a brutal disconnection */
void disconnected();
}

View File

@@ -41,22 +41,27 @@ import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.tools.ConstantString;
import net.bigeon.gclc.tools.StringProvider;
import net.bigeon.gclc.utils.ReadingRunnable; import net.bigeon.gclc.utils.ReadingRunnable;
/** A console input where the stream can be plugged. /** A console input where the stream can be plugged.
* <p> * <p>
* This pluggable console input accepts an input and output to be connected to * This pluggable console input accepts an input and output to be connected to
* it. The connexion cannot be concurrent, which mean that any connected stream * it. The connection cannot be concurrent, which mean that any connected stream
* must be disconnected before a new call to * must be disconnected before a new call to
* {@link #connect(InputStream, PrintStream)} is done. * {@link #connect(InputStream, PrintStream)} is done.
* <p>
* On connection, if the prompt is in wait, this console input will provide the
* prompting message.
* *
* @author Emmanuel Bigeon */ * @author Emmanuel Bigeon */
public final class PluggableConsoleInput implements ConsoleInput { public final class PluggableConsoleInput implements ConsoleInput {
@@ -72,7 +77,7 @@ public final class PluggableConsoleInput implements ConsoleInput {
/** If the element is closed. */ /** If the element is closed. */
private boolean closed = false; private boolean closed = false;
/** The default prompt. */ /** The default prompt. */
private StringProvider prompt = new ConstantString("> "); //$NON-NLS-1$ private Supplier<String> prompt = () -> "> "; //$NON-NLS-1$
/** If the input is plugged or buffering. */ /** If the input is plugged or buffering. */
private boolean connected = false; private boolean connected = false;
/** The current connexion (if any). */ /** The current connexion (if any). */
@@ -84,6 +89,8 @@ public final class PluggableConsoleInput implements ConsoleInput {
/** The output for hints. */ /** The output for hints. */
private PrintStream output; private PrintStream output;
private final Set<DisconnexionListener> listeners = new HashSet<>();
// Locks // Locks
/** The lock for connexion and disconnexion of actual streams. */ /** The lock for connexion and disconnexion of actual streams. */
private final Object connexionLock = new Object(); private final Object connexionLock = new Object();
@@ -100,28 +107,27 @@ public final class PluggableConsoleInput implements ConsoleInput {
closed = true; closed = true;
} }
/** Connect the given input stream to the input and output to the hints /** Connect the given input stream to the input and output to the hints writing.
* writing.
* *
* @param stream the input stream * @param stream the input stream
* @param out the output for hints. * @param out the output for hints.
* @throws IOException if the input is already connected. */ * @throws IOException if the input is already connected. */
public void connect(final InputStream stream, public void connect(final InputStream stream, final PrintStream out)
final PrintStream out) throws IOException { throws IOException {
synchronized (connexionLock) { synchronized (connexionLock) {
if (connected) { if (connected) {
throw new IOException( // Cannot be connected to several sources
"Input already connected to an input stream"); //$NON-NLS-1$ throw new IOException("Input already connected to an input stream"); //$NON-NLS-1$
} }
output = out; output = out;
if (prompting) { if (prompting) {
// print the hint, to indicate we are waiting for a user input.
out.print(hint); out.print(hint);
out.println();
out.flush(); out.flush();
} }
final InputStreamReader streamReader = new InputStreamReader(stream,
final InputStreamReader streamReader = new InputStreamReader( StandardCharsets.UTF_8);
stream, StandardCharsets.UTF_8);
final BufferedReader reader = new BufferedReader(streamReader); final BufferedReader reader = new BufferedReader(streamReader);
connexion = new ReadingRunnable(reader); connexion = new ReadingRunnable(reader);
final Thread th = new Thread(connexion, "GCLC Socket - Read input"); //$NON-NLS-1$ final Thread th = new Thread(connexion, "GCLC Socket - Read input"); //$NON-NLS-1$
@@ -145,7 +151,7 @@ public final class PluggableConsoleInput implements ConsoleInput {
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#getPrompt() */ * @see fr.bigeon.gclc.manager.ConsoleInput#getPrompt() */
@Override @Override
public StringProvider getPrompt() { public Supplier<String> getPrompt() {
return prompt; return prompt;
} }
@@ -174,14 +180,14 @@ public final class PluggableConsoleInput implements ConsoleInput {
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt() */ * @see fr.bigeon.gclc.manager.ConsoleInput#prompt() */
@Override @Override
public String prompt() throws IOException { public String prompt() throws IOException {
return prompt(prompt.apply()); return prompt(prompt.get());
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(long) */ * @see fr.bigeon.gclc.manager.ConsoleInput#prompt(long) */
@Override @Override
public String prompt(final long timeout) throws IOException { public String prompt(final long timeout) throws IOException {
return prompt(prompt.apply(), timeout); return prompt(prompt.get(), timeout);
} }
/* (non-Javadoc) /* (non-Javadoc)
@@ -191,19 +197,11 @@ public final class PluggableConsoleInput implements ConsoleInput {
if (closed) { if (closed) {
throw new IOException(); throw new IOException();
} }
prompting = true; preparePrompt(message);
hint = message;
synchronized (connexionLock) {
hint = message;
if (connected) {
output.print(message);
output.flush();
}
}
String res = null; String res = null;
while (res == null && !interrupted) { while (res == null && !interrupted) {
try { try {
// Wait for a message or a connection with message
res = waitMessageOrConnexion(TIMEOUT, TIMEOUT / TENTH); res = waitMessageOrConnexion(TIMEOUT, TIMEOUT / TENTH);
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
LOGGER.log(Level.FINE, "Interruption of thread", e); //$NON-NLS-1$ LOGGER.log(Level.FINE, "Interruption of thread", e); //$NON-NLS-1$
@@ -217,31 +215,36 @@ public final class PluggableConsoleInput implements ConsoleInput {
return res; return res;
} }
/* (non-Javadoc) /** Prepare the new prompt.
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String, *
* long) */ * @param message the message to indicate request of prompt. */
@Override private void preparePrompt(final String message) {
public String prompt(final String message,
final long timeout) throws IOException {
if (closed) {
throw new IOException();
}
prompting = true; prompting = true;
// hold the message
synchronized (connexionLock) { synchronized (connexionLock) {
hint = message; hint = message;
if (connected) { if (connected) {
// print the message
output.print(message); output.print(message);
output.flush(); output.flush();
} }
} }
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String, long) */
@Override
public String prompt(final String message, final long timeout) throws IOException {
if (closed) {
throw new IOException();
}
preparePrompt(message);
String res = null; String res = null;
final long tic = System.currentTimeMillis(); final long tic = System.currentTimeMillis();
long time = System.currentTimeMillis() - tic; long time = System.currentTimeMillis() - tic;
while (res == null && !interrupted && time < timeout) { while (res == null && !interrupted && time < timeout) {
try { try {
res = waitMessageOrConnexion(timeout - time, res = waitMessageOrConnexion(timeout - time, (timeout - time) / TENTH);
(timeout - time) / TENTH);
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
LOGGER.log(Level.FINE, "Interruption of thread", e); //$NON-NLS-1$ LOGGER.log(Level.FINE, "Interruption of thread", e); //$NON-NLS-1$
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
@@ -255,32 +258,76 @@ public final class PluggableConsoleInput implements ConsoleInput {
return res; return res;
} }
/* (non-Javadoc)
* @see net.bigeon.gclc.manager.ConsoleInput#setPrompt(java.lang.String) */
@Override @Override
public void setPrompt(final StringProvider prompt) { public void setPrompt(final String prompt) {
setPrompt(() -> prompt);
}
/* (non-Javadoc)
* @see net.bigeon.gclc.manager.ConsoleInput#setPrompt(net.bigeon.gclc.tools.
* StringProvider) */
@Override
public void setPrompt(final Supplier<String> prompt) {
this.prompt = prompt; this.prompt = prompt;
} }
@Override /** Wait for a hint or connection.
public void setPrompt(String prompt) {
setPrompt(new ConstantString(prompt));
}
/** Wait for a hint or connexion.
* *
* @param messageTimeout the timeout on the current connexion hint waiting * @param messageTimeout the timeout on the current connection hint waiting
* @param connexionTimeout the timeout on the new connexion wait * @param connexionTimeout the timeout on the new connection wait
* @return the hint, or null if not connected or timed out. * @return the hint, or null if not connected or timed out.
* @throws IOException if the reading failed. * @throws IOException if the reading failed.
* @throws InterruptedException if the wait was interrupted */ * @throws InterruptedException if the wait was interrupted */
private String waitMessageOrConnexion(final long messageTimeout, private String waitMessageOrConnexion(final long messageTimeout,
final long connexionTimeout) throws IOException, final long connexionTimeout) throws IOException, InterruptedException {
InterruptedException {
synchronized (connexionLock) { synchronized (connexionLock) {
if (connected) { if (connected) {
try {
return connexion.getNextMessage(messageTimeout); return connexion.getNextMessage(messageTimeout);
} catch (final InterruptedIOException e) {
throw e;
} catch (final IOException e) {
LOGGER.log(Level.INFO, "Communication was abrubptly interrupted", e);
brutalDisconnection();
} }
}
while (!connected) {
connexionLock.wait(connexionTimeout); connexionLock.wait(connexionTimeout);
} }
}
return null; return null;
} }
/**
*
*/
private void brutalDisconnection() {
// clean up the disconnection
disconnect();
// notify listeners
for (final DisconnexionListener listener : listeners) {
listener.disconnected();
}
}
/** Add a listener to the list of listeners.
*
* @param e the listener
* @return if the listener was added
* @see java.util.Set#add(java.lang.Object) */
public boolean add(final DisconnexionListener e) {
return listeners.add(e);
}
/** Remove a listener from the list of listeners.
*
* @param o the listener
* @return if the listener was removed
* @see java.util.Set#remove(java.lang.Object) */
public boolean remove(final DisconnexionListener o) {
return listeners.remove(o);
}
} }

View File

@@ -60,6 +60,7 @@ public final class PluggableConsoleOutput implements ConsoleOutput {
public PluggableConsoleOutput() { public PluggableConsoleOutput() {
// //
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see java.lang.AutoCloseable#close() */ * @see java.lang.AutoCloseable#close() */
@Override @Override

View File

@@ -4,60 +4,21 @@
*/ */
package net.bigeon.gclc.socket; package net.bigeon.gclc.socket;
/*-
* #%L
* GCLC Socket
* %%
* Copyright (C) 2016 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Collection;
import net.bigeon.gclc.command.Command; import net.bigeon.gclc.command.Command;
import net.bigeon.gclc.exception.CommandRunException; import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput; import net.bigeon.gclc.manager.ConsoleOutput;
/** A {@link Command} to disconnect elements from a {@link ConnexionManager}. /** A {@link Command} to disconnect elements from a {@link ConnexionManager}.
* *
* @author Emmanuel Bigeon * @author Emmanuel Bigeon
* @param <T> the type of connected object */ * @param <T> the type of connected object
* @deprecated since 1.1.17, this has been moved to
* {@link RemoteDisconnectCommand}. */
@Deprecated
public final class RemoteDisconnectCommand<T> extends Command { public final class RemoteDisconnectCommand<T> extends Command {
/** The connexion manager. */ private final net.bigeon.gclc.socket.cmd.RemoteDisconnectCommand<T> real;
private final ConnexionManager<T> manager;
/** If all connexion should be disconnected when no argument have been
* specified. */
private final boolean all;
/** Create the disconnection command. /** Create the disconnection command.
* *
@@ -65,70 +26,33 @@ public final class RemoteDisconnectCommand<T> extends Command {
* @param manager the manager * @param manager the manager
* @param all if all elements should be disconnected when no argument is * @param all if all elements should be disconnected when no argument is
* provided */ * provided */
public RemoteDisconnectCommand(final String name, public RemoteDisconnectCommand(final String name, final ConnexionManager<T> manager,
final ConnexionManager<T> manager, final boolean all) { final boolean all) {
super(name); super(name);
this.manager = manager; real = new net.bigeon.gclc.socket.cmd.RemoteDisconnectCommand<>(name, manager,
this.all = all; all);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager. * @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager.
* ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, * ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, java.lang.String[]) */
* java.lang.String[]) */
@Override @Override
public void execute(final ConsoleOutput out, final ConsoleInput in, public void execute(final ConsoleOutput out, final ConsoleInput in,
final String... args) throws CommandRunException { final String... args) throws CommandRunException {
if (args.length == 0 && all) { real.execute(out, in, args);
final Collection<String> coll = manager.getConnected();
for (final String string : coll) {
manager.disconnect(string);
}
}
for (final String string : args) {
if (manager.isConnected(string)) {
manager.disconnect(string);
} else {
print(out, MessageFormat
.format("[WARNING] {0} is not connected", string)); //$NON-NLS-1$
}
}
}
/** Print a message if the output is defined.
*
* @param out the output
* @param string the message
* @throws CommandRunException if the output exists but cannot be printed
* to */
private void print(final ConsoleOutput out,
final String string) throws CommandRunException {
if (out != null) {
try {
out.println(string);
} catch (final IOException e) {
throw new CommandRunException(
CommandRunExceptionType.INTERACTION,
"Unable to print to existing output", e); //$NON-NLS-1$
}
}
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#tip() */ * @see fr.bigeon.gclc.command.ICommand#tip() */
@Override @Override
public String tip() { public String tip() {
return "Close a connexion."; //$NON-NLS-1$ return real.tip();
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.Command#usageDetail() */ * @see fr.bigeon.gclc.command.Command#usageDetail() */
@SuppressWarnings("nls")
@Override @Override
protected String usageDetail() { protected String usageDetail() {
return MessageFormat.format( return "";
" If arguments are provided the corresponding connexions are closed, " +
"otherwise{0}{1} are.",
System.lineSeparator(), all ? "all connexions" : "none");
} }
} }

View File

@@ -77,18 +77,19 @@ public final class SocketConsoleApplicationShell implements Runnable {
/** The application. */ /** The application. */
private ConsoleApplication app; private ConsoleApplication app;
private final Object initLock = new Object();
/** The server socket. */ /** The server socket. */
private ServerSocket serverSocket; private ServerSocket serverSocket;
/** THe server address. */ /** THe server address. */
private final InetAddress addr; private final InetAddress addr;
/** Create a socket application shell which will listen on the given port /** Create a socket application shell which will listen on the given port and
* and network interface. * network interface.
* *
* @param port the part * @param port the part
* @param addr the inet address */ * @param addr the inet address */
public SocketConsoleApplicationShell(final int port, public SocketConsoleApplicationShell(final int port, final InetAddress addr) {
final InetAddress addr) {
super(); super();
this.port = port; this.port = port;
this.addr = addr; this.addr = addr;
@@ -110,9 +111,9 @@ public final class SocketConsoleApplicationShell implements Runnable {
} }
/** If the port provided was 0, this allows to get the actual port. /** If the port provided was 0, this allows to get the actual port.
*
* @return the local port * @return the local port
* @see java.net.ServerSocket#getLocalPort() * @see java.net.ServerSocket#getLocalPort() */
*/
public int getLocalPort() { public int getLocalPort() {
return serverSocket.getLocalPort(); return serverSocket.getLocalPort();
} }
@@ -122,16 +123,17 @@ public final class SocketConsoleApplicationShell implements Runnable {
@Override @Override
public void run() { public void run() {
// Create the server // Create the server
try (ServerSocket actualServerSocket = new ServerSocket(port, 1, try (ServerSocket actualServerSocket = new ServerSocket(port, 1, addr)) {
addr)) {
serverSocket = actualServerSocket; serverSocket = actualServerSocket;
synchronized (initLock) {
running = true; running = true;
initLock.notifyAll();
}
// Create the streams // Create the streams
runSokectServer(); runSokectServer();
} catch (final IOException e) { } catch (final IOException e) {
LOGGER.severe("Communication error between client and server"); //$NON-NLS-1$ LOGGER.severe("Communication error between client and server"); //$NON-NLS-1$
LOGGER.log(Level.FINE, LOGGER.log(Level.FINE, "Communication error between client and server", e); //$NON-NLS-1$
"Communication error between client and server", e); //$NON-NLS-1$
} }
} }
@@ -141,7 +143,7 @@ public final class SocketConsoleApplicationShell implements Runnable {
private void runSokectServer() throws IOException { private void runSokectServer() throws IOException {
while (running && app.isRunning()) { while (running && app.isRunning()) {
LOGGER.info("Waiting client"); //$NON-NLS-1$ LOGGER.info("Waiting client"); //$NON-NLS-1$
try (Socket clientSocket = serverSocket.accept();) { try (Socket clientSocket = serverSocket.accept()) {
sci.connect(clientSocket); sci.connect(clientSocket);
@@ -197,4 +199,23 @@ public final class SocketConsoleApplicationShell implements Runnable {
LOGGER.log(Level.SEVERE, "Exception in closing socket server", e); //$NON-NLS-1$ LOGGER.log(Level.SEVERE, "Exception in closing socket server", e); //$NON-NLS-1$
} }
} }
/** A method to wait for the socket server initialization.
*
* @param timeout the timeout */
public void waitStartUp(final long timeout) {
synchronized (initLock) {
final long tic = System.currentTimeMillis() + timeout;
long tac;
while (!running && (tac = System.currentTimeMillis()) < tic) {
final long idle = tic - tac;
try {
initLock.wait(idle);
} catch (final InterruptedException e) {
LOGGER.log(Level.INFO, "Thread interruption", e);
Thread.currentThread().interrupt();
}
}
}
}
} }

View File

@@ -68,12 +68,10 @@ public final class SocketConsoleInterface {
* @param socket the socket * @param socket the socket
* @throws IOException if the connection failed */ * @throws IOException if the connection failed */
public void connect(final Socket socket) throws IOException { public void connect(final Socket socket) throws IOException {
final PrintStream printStream = new PrintStream( final PrintStream printStream = new PrintStream(socket.getOutputStream(), true,
socket.getOutputStream(), true,
StandardCharsets.UTF_8.name()); StandardCharsets.UTF_8.name());
output.connect(printStream); output.connect(printStream);
input.connect(socket.getInputStream(), input.connect(socket.getInputStream(), printStream);
printStream);
} }
/** Disconnect the input and output of the application from the socket's. */ /** Disconnect the input and output of the application from the socket's. */

View File

@@ -0,0 +1,98 @@
/**
* gclc-socket:net.bigeon.gclc.socket.RemoteDisconnectCommand.java
* Created on: Nov 18, 2017
*/
package net.bigeon.gclc.socket.cmd;
/*-
* #%L
* GCLC Socket
* %%
* Copyright (C) 2016 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
import java.io.IOException;
import java.util.Collection;
import net.bigeon.gclc.command.Command;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput;
import net.bigeon.gclc.socket.ConnexionManager;
/** A {@link Command} to disconnect elements from a {@link ConnexionManager}.
*
* @author Emmanuel Bigeon
* @param <T> the type of connected object */
public final class ConnexionListCommand<T> extends Command {
/** The connexion manager. */
private final ConnexionManager<T> manager;
/** Create the connexion listing command.
*
* @param name the command name
* @param manager the manager */
public ConnexionListCommand(final String name, final ConnexionManager<T> manager) {
super(name);
this.manager = manager;
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager.
* ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, java.lang.String[]) */
@Override
public void execute(final ConsoleOutput out, final ConsoleInput in,
final String... args) throws CommandRunException {
final Collection<String> coll = manager.getConnected();
try {
for (final String string : coll) {
out.println(string);
}
} catch (final IOException e) {
throw new CommandRunException(CommandRunExceptionType.INTERACTION,
"User cannot be notified", e);
}
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#tip() */
@Override
public String tip() {
return "List current connexions."; //$NON-NLS-1$
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.Command#usageDetail() */
@Override
protected String usageDetail() {
return "";
}
}

View File

@@ -0,0 +1,136 @@
/**
* gclc-socket:net.bigeon.gclc.socket.RemoteDisconnectCommand.java
* Created on: Nov 18, 2017
*/
package net.bigeon.gclc.socket.cmd;
/*-
* #%L
* GCLC Socket
* %%
* Copyright (C) 2016 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Collection;
import net.bigeon.gclc.command.Command;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput;
import net.bigeon.gclc.socket.ConnexionManager;
/** A {@link Command} to disconnect elements from a {@link ConnexionManager}.
*
* @author Emmanuel Bigeon
* @param <T> the type of connected object */
public final class RemoteDisconnectCommand<T> extends Command {
/** The connexion manager. */
private final ConnexionManager<T> manager;
/** If all connexion should be disconnected when no argument have been
* specified. */
private final boolean all;
/** Create the disconnection command.
*
* @param name the command name
* @param manager the manager
* @param all if all elements should be disconnected when no argument is
* provided */
public RemoteDisconnectCommand(final String name, final ConnexionManager<T> manager,
final boolean all) {
super(name);
this.manager = manager;
this.all = all;
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager.
* ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, java.lang.String[]) */
@Override
public void execute(final ConsoleOutput out, final ConsoleInput in,
final String... args) throws CommandRunException {
if (args.length == 0 && all) {
final Collection<String> coll = manager.getConnected();
for (final String string : coll) {
manager.disconnect(string);
}
}
for (final String string : args) {
if (manager.isConnected(string)) {
manager.disconnect(string);
} else {
print(out,
MessageFormat.format("[WARNING] {0} is not connected", string)); //$NON-NLS-1$
}
}
}
/** Print a message if the output is defined.
*
* @param out the output
* @param string the message
* @throws CommandRunException if the output exists but cannot be printed to */
private static void print(final ConsoleOutput out, final String string)
throws CommandRunException {
if (out != null) {
try {
out.println(string);
} catch (final IOException e) {
throw new CommandRunException(CommandRunExceptionType.INTERACTION,
"Unable to print to existing output", e); //$NON-NLS-1$
}
}
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#tip() */
@Override
public String tip() {
return "Close a connexion."; //$NON-NLS-1$
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.Command#usageDetail() */
@Override
protected String usageDetail() {
final StringBuilder builder = new StringBuilder(
" If arguments are provided the corresponding connexions are closed, otherwise\n");
if (all) {
builder.append("all connections");
} else {
builder.append("none");
}
builder.append(" are.");
return builder.toString();
}
}

View File

@@ -0,0 +1,8 @@
/**
*
*/
/**
* @author Emmanuel Bigeon
*
*/
package net.bigeon.gclc.socket.cmd;

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
#%L
Generic Command Ligne console
%%
Copyright (C) 2014 - 2018 Bigeon
%%
This software is governed by the CeCILL license under French law and
abiding by the rules of distribution of free software. You can use,
modify and/ or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
"http://www.cecill.info".
As a counterpart to the access to the source code and rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty and the software's author, the holder of the
economic rights, and the successive licensors have only limited
liability.
In this respect, the user's attention is drawn to the risks associated
with loading, using, modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean that it is complicated to manipulate, and that also
therefore means that it is reserved for developers and experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or
data to be ensured and, more generally, to use and operate it in the
same conditions as regards security.
The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.
#L%
-->
<project name="GCLC">
<bannerRight>
<src>https://bigeon.net/images/logo_48.png</src>
<href>https://bigeon.net/</href>
</bannerRight>
<body>
<menu ref="reports"/>
</body>
</project>

View File

@@ -39,14 +39,12 @@ import java.util.Collection;
import net.bigeon.gclc.ConsoleApplication; import net.bigeon.gclc.ConsoleApplication;
import net.bigeon.gclc.command.Command; import net.bigeon.gclc.command.Command;
import net.bigeon.gclc.command.HelpExecutor;
import net.bigeon.gclc.command.base.ExitCommand; import net.bigeon.gclc.command.base.ExitCommand;
import net.bigeon.gclc.command.base.HelpExecutor;
import net.bigeon.gclc.exception.CommandRunException; import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.InvalidCommandName; import net.bigeon.gclc.exception.InvalidCommandName;
import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput; import net.bigeon.gclc.manager.ConsoleOutput;
import net.bigeon.gclc.socket.ConnexionManager;
import net.bigeon.gclc.socket.RemoteDisconnectCommand;
/** A test-purpose application /** A test-purpose application
* *
@@ -64,13 +62,10 @@ public class ConsoleTestApplication {
* @return create the application */ * @return create the application */
@SuppressWarnings("nls") @SuppressWarnings("nls")
public static ConsoleApplication create(final ConsoleOutput output, public static ConsoleApplication create(final ConsoleOutput output,
final ConsoleInput input, final ConsoleInput input, final ConnexionManager<Socket> manager) {
final ConnexionManager<Socket> manager) {
try { try {
final ConsoleApplication application = new ConsoleApplication( final ConsoleApplication application = new ConsoleApplication(output, input,
output, input, "Welcome to the test application. Type help or test.", "See you");
"Welcome to the test application. Type help or test.",
"See you");
application.add(new ExitCommand(EXIT, application) { application.add(new ExitCommand(EXIT, application) {
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.ExitCommand#beforeExit() */ * @see fr.bigeon.gclc.command.ExitCommand#beforeExit() */
@@ -82,8 +77,7 @@ public class ConsoleTestApplication {
} }
} }
}); });
application application.add(new HelpExecutor("help", application.root));
.add(new HelpExecutor("help", application.root));
application.add(new Command("test") { application.add(new Command("test") {
/* (non-Javadoc) /* (non-Javadoc)
@@ -91,8 +85,7 @@ public class ConsoleTestApplication {
* manager.ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, * manager.ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput,
* java.lang.String[]) */ * java.lang.String[]) */
@Override @Override
public void execute(final ConsoleOutput out, public void execute(final ConsoleOutput out, final ConsoleInput in,
final ConsoleInput in,
final String... args) throws CommandRunException { final String... args) throws CommandRunException {
try { try {
output.println("Test command ran fine"); output.println("Test command ran fine");
@@ -107,8 +100,7 @@ public class ConsoleTestApplication {
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.Command#usageDetail() * @see fr.bigeon.gclc.command.Command#usageDetail() */
*/
@Override @Override
protected String usageDetail() { protected String usageDetail() {
// TODO Auto-generated method stub // TODO Auto-generated method stub
@@ -123,11 +115,13 @@ public class ConsoleTestApplication {
* manager.ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, * manager.ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput,
* java.lang.String[]) */ * java.lang.String[]) */
@Override @Override
public void execute(final ConsoleOutput out, public void execute(final ConsoleOutput out, final ConsoleInput in,
final ConsoleInput in,
final String... args) throws CommandRunException { final String... args) throws CommandRunException {
try { try {
Thread.sleep(2000); Object obj = new Object();
synchronized (obj) {
obj.wait(2000);
}
output.println("Test command ran fine"); output.println("Test command ran fine");
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
throw new CommandRunException("manager closed", e); throw new CommandRunException("manager closed", e);
@@ -140,8 +134,7 @@ public class ConsoleTestApplication {
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.Command#usageDetail() * @see fr.bigeon.gclc.command.Command#usageDetail() */
*/
@Override @Override
protected String usageDetail() { protected String usageDetail() {
// TODO Auto-generated method stub // TODO Auto-generated method stub
@@ -149,8 +142,7 @@ public class ConsoleTestApplication {
throw new RuntimeException("Not implemented yet"); throw new RuntimeException("Not implemented yet");
} }
}); });
application.add( application.add(new RemoteDisconnectCommand<>("out", manager, true));
new RemoteDisconnectCommand<>("out", manager, true));
return application; return application;
} catch (final InvalidCommandName e) { } catch (final InvalidCommandName e) {
e.printStackTrace(); e.printStackTrace();

View File

@@ -39,6 +39,7 @@ package net.bigeon.gclc.socket;
*/ */
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@@ -46,11 +47,10 @@ import java.io.IOException;
import java.io.PipedInputStream; import java.io.PipedInputStream;
import java.io.PipedOutputStream; import java.io.PipedOutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test; import org.junit.Test;
import net.bigeon.gclc.socket.PluggableConsoleInput;
/** /**
* <p> * <p>
* TODO * TODO
@@ -90,7 +90,10 @@ public class PluggableConsoleInputTest {
} }
try { try {
Thread.sleep(100); final Object obj = new Object();
synchronized (obj) {
obj.wait(100);
}
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
@@ -112,7 +115,10 @@ public class PluggableConsoleInputTest {
@Override @Override
public void run() { public void run() {
try { try {
Thread.sleep(200); final Object obj = new Object();
synchronized (obj) {
obj.wait(200);
}
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
@@ -162,32 +168,40 @@ public class PluggableConsoleInputTest {
}); });
th.start(); th.start();
final Object obj = new Object();
synchronized (obj) {
while (!input.isPrompting()) { while (!input.isPrompting()) {
Thread.sleep(10); obj.wait(10);
}
} }
input.connect(pis, out); input.connect(pis, out);
testIn.println("tac"); testIn.println("tac");
final AtomicReference<IOException> ref = new AtomicReference<>();
final Thread th2 = new Thread(new Runnable() { final Thread th2 = new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
input.prompt("Test", 5000); input.prompt("Test", 5000);
fail("Prompt should io"); // fail("Prompt should io");
} catch (final IOException e) { } catch (final IOException e) {
// ok // ok
ref.set(e);
} }
} }
}); });
th2.start(); th2.start();
synchronized (obj) {
while (!input.isPrompting()) { while (!input.isPrompting()) {
Thread.sleep(10); obj.wait(10);
}
} }
input.close(); input.close();
th2.join();
assertNotNull("Prompt should io", ref.get());
} }
} }
@@ -197,8 +211,8 @@ public class PluggableConsoleInputTest {
@Test @Test
public final void testGetPrompt() { public final void testGetPrompt() {
final PluggableConsoleInput input = new PluggableConsoleInput(); final PluggableConsoleInput input = new PluggableConsoleInput();
assertEquals("Default prompt invalid", "> ", input.getPrompt().apply()); assertEquals("Default prompt invalid", "> ", input.getPrompt().get());
input.setPrompt("test"); input.setPrompt("test");
assertEquals("Prompt setting failed", "test", input.getPrompt().apply()); assertEquals("Prompt setting failed", "test", input.getPrompt().get());
} }
} }

View File

@@ -43,13 +43,12 @@ import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.junit.Test; import org.junit.Test;
import net.bigeon.gclc.socket.SocketConsoleApplicationShell;
/** Test class for {@link SocketConsoleApplicationShell} /** Test class for {@link SocketConsoleApplicationShell}
* *
* @author Emmanuel Bigeon */ * @author Emmanuel Bigeon */
@@ -62,12 +61,12 @@ public class SocketConsoleApplicationTest {
/** @param in the input /** @param in the input
* @return the string * @return the string
* @throws IOException if the input reading failed */ * @throws IOException if the input reading failed */
private String consumeToPrompt(final String server, private String consumeToPrompt(final String server, final BufferedReader in)
final BufferedReader in) throws IOException { throws IOException {
String fromServer = server; String fromServer = server;
LOGGER.fine("Server: \n" + fromServer); LOGGER.fine("Server: \n" + fromServer);
while (fromServer != null && !fromServer.equals("Bye.") && while (fromServer != null && !fromServer.equals("Bye.")
!fromServer.equals("> ")) { && !fromServer.equals("> ")) {
fromServer = in.readLine(); fromServer = in.readLine();
LOGGER.fine("Server: \n" + fromServer); LOGGER.fine("Server: \n" + fromServer);
} }
@@ -78,13 +77,11 @@ public class SocketConsoleApplicationTest {
public void testIntegration() throws IOException, InterruptedException { public void testIntegration() throws IOException, InterruptedException {
Thread server; Thread server;
server = TestServer.getServer(); server = TestServer.getServer();
Thread.sleep(1000); final InetAddress hostName = InetAddress.getLocalHost();
final String hostName = "127.0.0.1"; final int portNumber = TestServer.PORT;
final int portNumber = 3300;
try (Socket kkSocket = new Socket(hostName, portNumber); try (Socket kkSocket = new Socket(hostName, portNumber);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), true);
true);
BufferedReader in = new BufferedReader( BufferedReader in = new BufferedReader(
new InputStreamReader(kkSocket.getInputStream()));) { new InputStreamReader(kkSocket.getInputStream()));) {
@@ -108,15 +105,13 @@ public class SocketConsoleApplicationTest {
} }
try (Socket kkSocket = new Socket(hostName, portNumber); try (Socket kkSocket = new Socket(hostName, portNumber);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), true);
true);
BufferedReader in = new BufferedReader( BufferedReader in = new BufferedReader(
new InputStreamReader(kkSocket.getInputStream()));) { new InputStreamReader(kkSocket.getInputStream()));) {
String fromServer; String fromServer;
int i = 0; int i = 0;
final String[] cmds = {"help", "toto", "test", final String[] cmds = { "help", "toto", "test", ConsoleTestApplication.EXIT };
ConsoleTestApplication.EXIT};
while ((fromServer = in.readLine()) != null) { while ((fromServer = in.readLine()) != null) {
fromServer = consumeToPrompt(fromServer, in); fromServer = consumeToPrompt(fromServer, in);
if (fromServer == null || fromServer.equals("Bye.")) { if (fromServer == null || fromServer.equals("Bye.")) {
@@ -131,26 +126,20 @@ public class SocketConsoleApplicationTest {
} }
i++; i++;
} }
assertEquals("Application exit command should close connection", 4, assertEquals("Application exit command should close connection", 4, i);
i);
} }
Thread.sleep(100);
TestServer.closeServer(); TestServer.closeServer();
server.join(); server.join();
server = TestServer.getServer(); server = TestServer.getServer();
Thread.sleep(1000);
try (Socket kkSocket = new Socket(hostName, portNumber); try (Socket kkSocket = new Socket(hostName, portNumber);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), true);
true);
BufferedReader in = new BufferedReader( BufferedReader in = new BufferedReader(
new InputStreamReader(kkSocket.getInputStream()));) { new InputStreamReader(kkSocket.getInputStream()));) {
String fromServer; String fromServer;
int i = 0; int i = 0;
final String[] cmds = {"help", "toto", "test", final String[] cmds = { "help", "toto", "test", ConsoleTestApplication.EXIT };
ConsoleTestApplication.EXIT};
while ((fromServer = in.readLine()) != null) { while ((fromServer = in.readLine()) != null) {
fromServer = consumeToPrompt(fromServer, in); fromServer = consumeToPrompt(fromServer, in);
if (fromServer == null || fromServer.equals("Bye.")) { if (fromServer == null || fromServer.equals("Bye.")) {
@@ -164,8 +153,7 @@ public class SocketConsoleApplicationTest {
} }
i++; i++;
} }
assertEquals("Application exit command should close connection", 4, assertEquals("Application exit command should close connection", 4, i);
i);
} }
TestServer.closeServer(); TestServer.closeServer();
server.join(); server.join();

View File

@@ -44,9 +44,8 @@ import java.util.Collection;
import net.bigeon.smu.StringEncoder; import net.bigeon.smu.StringEncoder;
/** TODO Describe TestConsoleClient.java /** TODO Describe TestConsoleClient.java
* @author Emmanuel Bigeon
* *
*/ * @author Emmanuel Bigeon */
@SuppressWarnings("nls") @SuppressWarnings("nls")
public class TestConsoleClient { public class TestConsoleClient {
@SuppressWarnings("javadoc") @SuppressWarnings("javadoc")
@@ -56,8 +55,7 @@ public class TestConsoleClient {
} }
@SuppressWarnings("javadoc") @SuppressWarnings("javadoc")
private static final StringEncoder ENCODER = new StringEncoder("%", private static final StringEncoder ENCODER = new StringEncoder("%", TO_ENCODE);
TO_ENCODE);
@SuppressWarnings("javadoc") @SuppressWarnings("javadoc")
public static void main(String[] args) { public static void main(String[] args) {
@@ -65,10 +63,9 @@ public class TestConsoleClient {
final int portNumber = 3300; final int portNumber = 3300;
try (Socket kkSocket = new Socket(hostName, portNumber); try (Socket kkSocket = new Socket(hostName, portNumber);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), true);
true); BufferedReader in = new BufferedReader(
BufferedReader in = new BufferedReader(new InputStreamReader( new InputStreamReader(kkSocket.getInputStream()));) {
kkSocket.getInputStream()));) {
String fromServer; String fromServer;
while ((fromServer = in.readLine()) != null) { while ((fromServer = in.readLine()) != null) {

View File

@@ -39,12 +39,6 @@ import java.net.Socket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import net.bigeon.gclc.ConsoleApplication; import net.bigeon.gclc.ConsoleApplication;
import net.bigeon.gclc.socket.ConnexionManager;
import net.bigeon.gclc.socket.DConnexionManager;
import net.bigeon.gclc.socket.PluggableConsoleInput;
import net.bigeon.gclc.socket.PluggableConsoleOutput;
import net.bigeon.gclc.socket.SocketConsoleApplicationShell;
import net.bigeon.gclc.socket.SocketConsoleInterface;
/** A test server /** A test server
* *
@@ -52,6 +46,7 @@ import net.bigeon.gclc.socket.SocketConsoleInterface;
@SuppressWarnings({ "javadoc", "nls" }) @SuppressWarnings({ "javadoc", "nls" })
public class TestServer { public class TestServer {
public static final int PORT = 12343;
private static SocketConsoleApplicationShell SHELL; private static SocketConsoleApplicationShell SHELL;
private static ConnexionManager<Socket> manager; private static ConnexionManager<Socket> manager;
@@ -71,6 +66,7 @@ public class TestServer {
if (server == null) { if (server == null) {
server = new Thread(getShell(), "gclcServer"); server = new Thread(getShell(), "gclcServer");
server.start(); server.start();
getShell().waitStartUp(500);
} }
return server; return server;
} }
@@ -81,21 +77,24 @@ public class TestServer {
input.setPrompt("> \n"); input.setPrompt("> \n");
output = new PluggableConsoleOutput(); output = new PluggableConsoleOutput();
manager = new DConnexionManager<>(); manager = new DConnexionManager<>();
SHELL = new SocketConsoleApplicationShell(3300, SHELL = new SocketConsoleApplicationShell(PORT, InetAddress.getLocalHost());
InetAddress.getByName("127.0.0.1")); final ConsoleApplication app = ConsoleTestApplication.create(output, input,
final ConsoleApplication app = ConsoleTestApplication manager);
.create(output, input, manager);
SHELL.setInterface(new SocketConsoleInterface(input, output)); SHELL.setInterface(new SocketConsoleInterface(input, output));
SHELL.setConnexionManager(manager); SHELL.setConnexionManager(manager);
SHELL.setApplication(app); SHELL.setApplication(app);
final Thread th = new Thread(new Runnable() { final Thread th = new Thread(() -> app.start());
@Override
public void run() {
app.start();
}
});
th.start(); th.start();
try {
final Object waiting = new Object();
synchronized (waiting) {
while (!input.isPrompting()) {
waiting.wait(50);
}
}
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
} }
return SHELL; return SHELL;
} }

View File

@@ -2,7 +2,7 @@
* gclc-socket:net.bigeon.gclc.socket.RemoteDisconnectCommandTest.java * gclc-socket:net.bigeon.gclc.socket.RemoteDisconnectCommandTest.java
* Created on: Nov 18, 2017 * Created on: Nov 18, 2017
*/ */
package net.bigeon.gclc.socket; package net.bigeon.gclc.socket.cmd;
/*- /*-
* #%L * #%L
@@ -46,9 +46,8 @@ import java.io.IOException;
import org.junit.Test; import org.junit.Test;
import net.bigeon.gclc.exception.CommandRunException; import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.manager.PipedConsoleOutput;
import net.bigeon.gclc.socket.DConnexionManager; import net.bigeon.gclc.socket.DConnexionManager;
import net.bigeon.gclc.socket.RemoteDisconnectCommand;
import net.bigeon.gclc.utils.PipedConsoleOutput;
/** /**
* <p> * <p>
@@ -58,17 +57,17 @@ import net.bigeon.gclc.utils.PipedConsoleOutput;
public class RemoteDisconnectCommandTest { public class RemoteDisconnectCommandTest {
/** Test method for /** Test method for
* {@link net.bigeon.gclc.socket.RemoteDisconnectCommand#execute(net.bigeon.gclc.manager.ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[])}. * {@link net.bigeon.gclc.socket.cmd.RemoteDisconnectCommand#execute(net.bigeon.gclc.manager.ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[])}.
* *
* @throws CommandRunException if the command unexpectedly failed. * @throws CommandRunException if the command unexpectedly failed.
* @throws IOException if the output could not be written to */ * @throws IOException if the output could not be written to */
@Test @Test
public final void testExecute() throws CommandRunException, IOException { public final void testExecute() throws CommandRunException, IOException {
final DConnexionManager<String> manager = new DConnexionManager<>(); final DConnexionManager<String> manager = new DConnexionManager<>();
final RemoteDisconnectCommand<String> cmd = new RemoteDisconnectCommand<>( final RemoteDisconnectCommand<String> cmd = new RemoteDisconnectCommand<>("quit",
"quit", manager, true); manager, true);
final RemoteDisconnectCommand<String> cmd2 = new RemoteDisconnectCommand<>( final RemoteDisconnectCommand<String> cmd2 = new RemoteDisconnectCommand<>("quit",
"quit", manager, false); manager, false);
manager.addConnexion("test"); manager.addConnexion("test");
cmd2.execute(null, null); cmd2.execute(null, null);

4
gclc-swt/.gitignore vendored
View File

@@ -1,4 +0,0 @@
/target/
/.settings/
/.classpath
/.project

View File

@@ -1,69 +1,22 @@
<!-- GCLC swt, provide a swt window for console applications -->
<!-- Copyright (C) 2015-2017 E. Bigeon -->
<!-- mailto:emmanuel@bigeon.fr -->
<!-- -->
<!-- This software is governed by the CeCILL license under French law and -->
<!-- abiding by the rules of distribution of free software. You can use, -->
<!-- modify and/or redistribute the software under the terms of the CeCILL -->
<!-- license as circulated by CEA, CNRS and INRIA at the following URL -->
<!-- "http://www.cecill.info". -->
<!-- -->
<!-- As a counterpart to the access to the source code and rights to copy, -->
<!-- modify and redistribute granted by the license, users are provided only -->
<!-- with a limited warranty and the software's author, the holder of the -->
<!-- economic rights, and the successive licensors have only limited -->
<!-- liability. -->
<!-- -->
<!-- In this respect, the user's attention is drawn to the risks associated -->
<!-- with loading, using, modifying and/or developing or reproducing the -->
<!-- software by the user in light of its specific status of free software, -->
<!-- that may mean that it is complicated to manipulate, and that also -->
<!-- therefore means that it is reserved for developers and experienced -->
<!-- professionals having in-depth computer knowledge. Users are therefore -->
<!-- encouraged to load and test the software's suitability as regards their -->
<!-- requirements in conditions enabling the security of their systems and/or -->
<!-- data to be ensured and, more generally, to use and operate it in the -->
<!-- same conditions as regards security. -->
<!-- -->
<!-- The fact that you are presently reading this means that you have had -->
<!-- knowledge of the CeCILL license and that you accept its terms. -->
<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/maven-v4_0_0.xsd"> <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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>net.bigeon.gclc</groupId>
<artifactId>gclc-swt</artifactId>
<version>1.1.6-SNAPSHOT</version>
<packaging>jar</packaging>
<inceptionYear>2015</inceptionYear>
<name>GCLC swt</name>
<description>A swt window for console applications</description>
<parent> <parent>
<groupId>net.bigeon.config</groupId> <groupId>net.bigeon.config</groupId>
<artifactId>swt-config</artifactId> <artifactId>swt-public-conf</artifactId>
<version>1.8.7</version> <version>1.0.2</version>
</parent> </parent>
<groupId>net.bigeon.gclc</groupId>
<properties> <artifactId>swt</artifactId>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <version>1.2.2-SNAPSHOT</version>
<copyright.email>emmanuel@bigeon.fr</copyright.email> <packaging>jar</packaging>
<license.licenseName>cecill_2.1</license.licenseName> <name>GCLC swt</name>
</properties> <description>A swt window for console applications</description>
<url>https://bigeon.net/projects/gclc.html</url>
<dependencies> <inceptionYear>2015</inceptionYear>
<dependency> <organization>
<groupId>net.bigeon</groupId> <name>Bigeon</name>
<artifactId>gclc</artifactId> <url>https://bigeon.net/</url>
<version>2.0.7</version> </organization>
</dependency>
<dependency>
<groupId>net.bigeon</groupId>
<artifactId>collections</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
<licenses> <licenses>
<license> <license>
<distribution>manual</distribution> <distribution>manual</distribution>
@@ -71,20 +24,6 @@
<url>https://cecill.info/licences/Licence_CeCILL_V2.1-en.html</url> <url>https://cecill.info/licences/Licence_CeCILL_V2.1-en.html</url>
</license> </license>
</licenses> </licenses>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
</plugin>
<plugin>
<groupId>com.github.sevntu-checkstyle</groupId>
<artifactId>dsm-maven-plugin</artifactId>
<version>2.2.0</version>
</plugin>
</plugins>
</reporting>
<developers> <developers>
<developer> <developer>
<email>emmanuel@bigeon.fr</email> <email>emmanuel@bigeon.fr</email>
@@ -95,9 +34,46 @@
</roles> </roles>
</developer> </developer>
</developers> </developers>
<scm> <scm>
<developerConnection>scm:git:gogs@git.code.bigeon.net:emmanuel/gclc.git</developerConnection> <developerConnection>scm:git:gitea@git.code.bigeon.net:emmanuel/gclc-core.git</developerConnection>
<tag>HEAD</tag> <tag>HEAD</tag>
</scm> </scm>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<copyright.email>emmanuel@bigeon.fr</copyright.email>
<license.licenseName>cecill_2.1</license.licenseName>
</properties>
<dependencies>
<dependency>
<groupId>net.bigeon</groupId>
<artifactId>gclc</artifactId>
<version>2.1.5</version>
</dependency>
<dependency>
<groupId>net.bigeon</groupId>
<artifactId>collections</artifactId>
<version>1.3.5</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.15.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.1</version>
</plugin>
<plugin>
<groupId>com.github.sevntu-checkstyle</groupId>
<artifactId>dsm-maven-plugin</artifactId>
<version>2.2.0</version>
</plugin>
</plugins>
</reporting>
</project> </project>

View File

@@ -30,11 +30,13 @@
* knowledge of the CeCILL license and that you accept its terms. * knowledge of the CeCILL license and that you accept its terms.
*/ */
/** /**
* gclc-swt:net.bigeon.gclc.swt.SWTConsole.java * gclc-swt:net.bigeon.gclc.swt.SWTConsoleShell.java
* Created on: Apr 18, 2015 * Created on: Apr 18, 2015
*/ */
package net.bigeon.gclc.swt; package net.bigeon.gclc.swt;
import java.io.BufferedReader;
/*- /*-
* #%L * #%L
* GCLC swt * GCLC swt
@@ -68,402 +70,80 @@ package net.bigeon.gclc.swt;
* knowledge of the CeCILL license and that you accept its terms. * knowledge of the CeCILL license and that you accept its terms.
* #L% * #L%
*/ */
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Text;
import net.bigeon.gclc.ConsoleApplication; import net.bigeon.gclc.manager.PipedConsoleInput;
import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.PipedConsoleOutput;
import net.bigeon.gclc.manager.ConsoleOutput; import net.bigeon.gclc.swt.api.ConsoleDelayIO;
import net.bigeon.gclc.tools.ConstantString; import net.bigeon.gclc.swt.api.ConsoleOutputDisplay;
import net.bigeon.gclc.tools.StringProvider; import net.bigeon.gclc.swt.io.ConsoleInputManager;
import net.bigeon.gclc.swt.io.ConsoleOutputManager;
import net.bigeon.gclc.swt.io.ConsolePromptManager;
/** A SWT component to connect to gclc {@link ConsoleApplication}. /** A shell containing a {@link SWTConsoleView}
* <p> * <p>
* *
* @author Emmanuel Bigeon */ * @author Emmanuel Bigeon */
public final class SWTConsole extends Composite public final class SWTConsole extends Composite {
implements ConsoleDelayIO, ConsoleInput, ConsoleOutput {
/** The number of columns of the layout. */
private static final int LAYOUT_NB_COLUMNS = 2;
/** The cmd prefix in the output console. */
private static final String CMD_PREFIX = "[CMD] "; //$NON-NLS-1$
/** The class logger. */
private static final Logger LOGGER = Logger
.getLogger(SWTConsole.class.getName());
/** The empty string constant. */
private static final String EMPTY = ""; //$NON-NLS-1$
/** The console output text field. */
private final Text consoleOutput;
/** The console input text field. */
private final Text consoleInput;
/** The prompt label. */
private final Label lblPromptlabel;
/** The prompt text. */
private StringProvider prompt = new ConstantString("> "); //$NON-NLS-1$
/** The command entered by the user. */
private String command = null;
/** If the prompt should be active. */
private boolean prompting = false;
/** The object for thread synchronization with the prompt. */
private final Object promptLock = new Object();
/** Create the composite. private ConsoleInputManager inputManager;
private ConsoleOutputManager outputManager;
private ConsolePromptManager promptManager;
/** Create the shell.
* *
* @param parent the prent composite * @param parent the containing composite
* @param style the composite style */ * @param style the shell style */
public SWTConsole(final Composite parent, final int style) { public SWTConsole(final Composite parent, final int style) {
super(parent, style); super(parent, style);
setLayout(new GridLayout(2, false));
setLayout(new GridLayout(LAYOUT_NB_COLUMNS, false)); createContents();
consoleOutput = new Text(this,
SWT.BORDER | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL | SWT.MULTI);
consoleOutput.setLayoutData(
new GridData(SWT.FILL, SWT.FILL, true, true, LAYOUT_NB_COLUMNS, 1));
consoleOutput.setRedraw(true);
lblPromptlabel = new Label(this, SWT.NONE);
lblPromptlabel.setText(prompt.apply());
consoleInput = new Text(this, SWT.BORDER);
consoleInput.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
consoleInput.addKeyListener(new HistoryTextKeyListener(this));
} }
@Override /** Create contents of the shell. */
protected void checkSubclass() { private void createContents() {
// Disable the check that prevents subclassing of SWT components final Text output = new Text(this,
SWT.WRAP | SWT.READ_ONLY | SWT.MULTI | SWT.V_SCROLL);
output.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
final Label prompt = new Label(this, SWT.NONE);
prompt.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
final Text input = new Text(this, SWT.NONE);
input.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
inputManager = new ConsoleInputManager(input);
outputManager = new ConsoleOutputManager(output);
promptManager = new ConsolePromptManager(prompt);
} }
/* (non-Javadoc) /** Connect the console parts to the shell.
* @see fr.bigeon.gclc.manager.ConsoleManager#close() */
@Override
public void close() {
synchronized (promptLock) {
prompting = false;
promptLock.notifyAll();
}
if (consoleInput.isDisposed()) {
return;
}
consoleInput.setEnabled(false);
consoleOutput.setEnabled(false);
}
/* (non-Javadoc)
* @see net.bigeon.gclc.swt.ConsoleDelayIO#getInput() */
@Override
public String getInput() {
return consoleInput.getText();
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#getPrompt() */
@Override
public StringProvider getPrompt() {
return prompt;
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */
@Override
public void interruptPrompt() {
synchronized (promptLock) {
promptLock.notifyAll();
}
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */
@Override
public boolean isClosed() {
return isDisposed();
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#print(java.lang.String) */
@Override
public void print(final String text) throws IOException {
if (isDisposed()) {
throw new IOException();
}
Display.getDefault().syncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
if (!consoleOutput.isDisposed()) {
consoleOutput.append(text);
}
}
});
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#println() */
@Override
public void println() throws IOException {
if (isDisposed()) {
throw new IOException();
}
Display.getDefault().syncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
if (!consoleOutput.isDisposed()) {
consoleOutput.append(System.lineSeparator());
}
}
});
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#println(java.lang.String) */
@Override
public void println(final String message) throws IOException {
if (isDisposed()) {
throw new IOException();
}
Display.getDefault().syncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
if (!consoleOutput.isDisposed()) {
consoleOutput.append(message + System.lineSeparator());
}
}
});
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#prompt() */
@Override
public String prompt() throws IOException {
synchronized (promptLock) {
if (isDisposed()) {
throw new IOException();
}
try {
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
if (!consoleInput.isDisposed()) {
consoleInput.setEnabled(true);
lblPromptlabel.setText(prompt.apply());
// relayout
SWTConsole.this.layout();
consoleInput.setFocus();
}
}
});
prompting = true;
promptLock.notifyAll();
while (prompting) {
promptLock.wait();
}
} catch (final InterruptedException e) {
LOGGER.log(Level.WARNING, "Error in synchronization of prompting", e); //$NON-NLS-1$
command = null;
Thread.currentThread().interrupt();
}
}
if (isDisposed()) {
throw new IOException("Input closed"); //$NON-NLS-1$
}
return command;
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(long) */
@Override
public String prompt(final long timeout) throws IOException {
synchronized (promptLock) {
if (isDisposed()) {
throw new IOException();
}
try {
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
if (!consoleInput.isDisposed()) {
consoleInput.setEnabled(true);
lblPromptlabel.setText(prompt.apply());
// relayout
SWTConsole.this.layout();
consoleInput.setFocus();
}
}
});
prompting = true;
command = null;
promptLock.notifyAll();
final long start = System.currentTimeMillis();
long cur = start;
while (prompting && start + timeout>cur) {
promptLock.wait((cur-start-timeout)/2);
cur = System.currentTimeMillis();
}
} catch (final InterruptedException e) {
LOGGER.log(Level.WARNING, "Error in synchronization of prompting", e); //$NON-NLS-1$
command = null;
Thread.currentThread().interrupt();
}
}
if (isDisposed()) {
throw new IOException("Input closed"); //$NON-NLS-1$
}
return command;
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#prompt(java.lang.String) */
@Override
public String prompt(final String message) throws IOException {
synchronized (promptLock) {
if (isDisposed()) {
throw new IOException();
}
try {
Display.getDefault().syncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
if (!consoleOutput.isDisposed()) {
lblPromptlabel.setText(message);
// relayout
SWTConsole.this.layout();
consoleInput.setEnabled(true);
consoleInput.setFocus();
}
}
});
prompting = true;
promptLock.wait();
if (isDisposed()) {
throw new IOException();
}
} catch (final InterruptedException e) {
LOGGER.log(Level.WARNING, "Error in synchronization of prompting", e); //$NON-NLS-1$
command = null;
Thread.currentThread().interrupt();
} finally {
Display.getDefault().syncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
if (!consoleOutput.isDisposed()) {
lblPromptlabel.setText(prompt.apply());
// relayout
SWTConsole.this.layout();
}
}
});
}
}
return command;
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String, long) */
@Override
public String prompt(final String message, final long timeout) throws IOException {
// TODO Auto-generated method stub
// return null;
throw new RuntimeException("Not implemented yet");
}
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Composite#setFocus() */
@Override
public boolean setFocus() {
return consoleInput.setFocus();
}
/* (non-Javadoc)
* @see net.bigeon.gclc.swt.ConsoleDelayIO#setInput(java.lang.String) */
@Override
public void setInput(final String input) {
consoleInput.setText(input);
consoleInput.setSelection(input.length());
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#setPrompt(java.lang.String) */
@Override
public void setPrompt(final StringProvider prompt) {
this.prompt = prompt;
Display.getDefault().syncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
if (!consoleOutput.isDisposed()) {
lblPromptlabel.setText(prompt.apply());
// relayout
SWTConsole.this.layout();
}
}
});
}
@Override
public void setPrompt(String prompt) {
setPrompt(new ConstantString(prompt));
}
/** @param string the text */
public void setText(final String string) {
consoleInput.setText(string);
}
/**
* *
*/ * @param input the input
public void validateCommand() { * @param output the output
validateInput(); * @param promptStream the stream where the prompts are forwarded. */
public void connect(final PipedConsoleInput input, final PipedConsoleOutput output,
final BufferedReader promptStream) {
inputManager.setManager(input);
outputManager.setManager(output);
promptManager.setStream(promptStream);
} }
/** /** @return the inputManager */
* public ConsoleDelayIO getInputManager() {
*/ return inputManager;
@Override
public void validateInput() {
Display.getDefault().syncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
consoleInput.setEnabled(false);
}
});
synchronized (promptLock) {
while (!prompting) {
try {
promptLock.wait();
} catch (final InterruptedException e) {
LOGGER.log(Level.SEVERE, "Interruption while waiting prompt", e); //$NON-NLS-1$
}
}
Display.getDefault().syncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
command = consoleInput.getText();
prompting = false;
consoleInput.setText(EMPTY);
consoleOutput.append(CMD_PREFIX + command + System.lineSeparator());
}
});
promptLock.notifyAll();
}
} }
/** @return the outputManager */
public ConsoleOutputDisplay getOutputManager() {
return outputManager;
}
/** @return the promptManager */
public ConsolePromptManager getPromptManager() {
return promptManager;
}
} }

View File

@@ -68,67 +68,30 @@ package net.bigeon.gclc.swt;
* knowledge of the CeCILL license and that you accept its terms. * knowledge of the CeCILL license and that you accept its terms.
* #L% * #L%
*/ */
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Text;
import net.bigeon.gclc.ConsoleApplication; import net.bigeon.gclc.ConsoleApplication;
import net.bigeon.gclc.utils.AOutputForwardRunnable; import net.bigeon.gclc.manager.PipedConsoleInput;
import net.bigeon.gclc.utils.PipedConsoleInput; import net.bigeon.gclc.manager.PipedConsoleOutput;
import net.bigeon.gclc.utils.PipedConsoleOutput; import net.bigeon.gclc.swt.api.ConsoleDelayIO;
import net.bigeon.gclc.swt.api.ConsoleOutputDisplay;
import net.bigeon.gclc.swt.io.ConsoleInputManager;
import net.bigeon.gclc.swt.io.ConsoleOutputManager;
import net.bigeon.gclc.swt.tools.HistoryTextKeyListener;
/** A SWT component to connect to gclc {@link ConsoleApplication} /** A SWT component to connect to gclc {@link ConsoleApplication}.
* <p>
* *
* @author Emmanuel Bigeon */ * @author Emmanuel Bigeon */
public final class SWTConsoleView extends Composite implements ConsoleDelayIO { public final class SWTConsoleView extends Composite {
/** The local implementation of the forwarding runnable /** The input manager. */
* private final ConsoleInputManager inManager;
* @author Emmanuel Bigeon */ /** The output manager. */
private final class ToSWTConsoleForwardRunnable private final ConsoleOutputManager outManager;
extends AOutputForwardRunnable {
/** The running status */
private boolean running = true;
/** @param manager the manager */
public ToSWTConsoleForwardRunnable(final PipedConsoleOutput manager) {
super(manager);
}
@Override
protected void forwardLine(final String m) {
appendConsoleOutput(m);
}
@Override
protected boolean isRunning() {
return running && !isDisposed();
}
/** @param running the running to set */
public void setRunning(final boolean running) {
this.running = running;
}
}
/** The class logger */
private static final Logger LOGGER = Logger
.getLogger(SWTConsoleView.class.getName());
/** The console output text field */
private final Text consoleOutput;
/** The console input text field */
private final Text consoleInput;
/** The input. */
private PipedConsoleInput input;
/** The forwarding runnable */
private ToSWTConsoleForwardRunnable forward;
/** Create the composite. /** Create the composite.
* *
@@ -139,54 +102,23 @@ public final class SWTConsoleView extends Composite implements ConsoleDelayIO {
setLayout(new GridLayout(1, false)); setLayout(new GridLayout(1, false));
consoleOutput = new Text(this, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP | final Text consoleOutput = new Text(this,
SWT.V_SCROLL | SWT.MULTI); SWT.BORDER | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL | SWT.MULTI);
consoleOutput.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, consoleOutput.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
1, 1));
consoleOutput.setRedraw(true); consoleOutput.setRedraw(true);
outManager = new ConsoleOutputManager(consoleOutput);
consoleInput = new Text(this, SWT.BORDER); final Text consoleInput = new Text(this, SWT.BORDER);
consoleInput.setLayoutData( consoleInput.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); inManager = new ConsoleInputManager(consoleInput);
consoleInput.addKeyListener(new HistoryTextKeyListener(this)); consoleInput.addKeyListener(new HistoryTextKeyListener(inManager));
}
/** @param next the next message */
protected void appendConsoleOutput(final String next) {
Display.getDefault().syncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
consoleOutput.append(System.lineSeparator() + next);
}
});
}
@Override
protected void checkSubclass() {
// Disable the check that prevents subclassing of SWT components
}
/* (non-Javadoc)
* @see net.bigeon.gclc.swt.ConsoleDelayIO#getInput() */
@Override
public String getInput() {
return consoleInput.getText();
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.eclipse.swt.widgets.Composite#setFocus() */ * @see org.eclipse.swt.widgets.Composite#setFocus() */
@Override @Override
public boolean setFocus() { public boolean setFocus() {
return consoleInput.setFocus(); return inManager.getText().setFocus();
}
/* (non-Javadoc)
* @see net.bigeon.gclc.swt.ConsoleDelayIO#setInput(java.lang.String) */
@Override
public void setInput(final String input) {
consoleInput.setText(input);
consoleInput.setSelection(input.length());
} }
/** Set the input and output. /** Set the input and output.
@@ -195,29 +127,21 @@ public final class SWTConsoleView extends Composite implements ConsoleDelayIO {
* @param input the input */ * @param input the input */
public void setManager(final PipedConsoleOutput manager, public void setManager(final PipedConsoleOutput manager,
final PipedConsoleInput input) { final PipedConsoleInput input) {
this.input = input; outManager.setManager(manager);
if (forward != null) { inManager.setManager(input);
forward.setRunning(false);
}
forward = new ToSWTConsoleForwardRunnable(manager);
final Thread th = new Thread(forward, "gclcToSWT"); //$NON-NLS-1$
th.start();
} }
/** @param string the text */ /** Get the manager of console input.
public void setText(final String string) {
consoleInput.setText(string);
}
/**
* *
*/ * @return the input manager */
@Override public ConsoleDelayIO getInputManager() {
public void validateInput() { return inManager;
try { }
input.type(getInput());
} catch (final IOException e) { /** Get the manager of console output.
LOGGER.log(Level.SEVERE, "Unable to input value to console", e); //$NON-NLS-1$ *
} * @return the output manager */
public ConsoleOutputDisplay getOutputManager() {
return outManager;
} }
} }

View File

@@ -33,7 +33,9 @@
* gclc-swt:net.bigeon.gclc.swt.ConsoleDelayIO.java * gclc-swt:net.bigeon.gclc.swt.ConsoleDelayIO.java
* Created on: Nov 19, 2016 * Created on: Nov 19, 2016
*/ */
package net.bigeon.gclc.swt; package net.bigeon.gclc.swt.api;
import java.io.IOException;
/*- /*-
* #%L * #%L
@@ -77,6 +79,7 @@ package net.bigeon.gclc.swt;
* @author Emmanuel Bigeon */ * @author Emmanuel Bigeon */
public interface ConsoleDelayIO { public interface ConsoleDelayIO {
/** Get the input text. /** Get the input text.
*
* @return the non validated input */ * @return the non validated input */
String getInput(); String getInput();
@@ -85,6 +88,8 @@ public interface ConsoleDelayIO {
* @param input the input to set */ * @param input the input to set */
void setInput(String input); void setInput(String input);
/** Actually send the input as the prompt next input. */ /** Actually send the input as the prompt next input.
void validateInput(); *
* @throws IOException if an error occurred */
void validateInput() throws IOException;
} }

View File

@@ -0,0 +1,50 @@
/**
*
*/
package net.bigeon.gclc.swt.api;
/*-
* #%L
* GCLC swt
* %%
* Copyright (C) 2015 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
/** The common interface for console display.
*
* @author Emmanuel Bigeon */
public interface ConsoleOutputDisplay {
/** Append a line to the display.
*
* @param m the line content */
void appendLine(String m);
}

View File

@@ -0,0 +1,8 @@
/**
*
*/
/**
* @author Emmanuel Bigeon
*
*/
package net.bigeon.gclc.swt.api;

View File

@@ -0,0 +1,98 @@
/**
*
*/
package net.bigeon.gclc.swt.io;
/*-
* #%L
* GCLC swt
* %%
* Copyright (C) 2015 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
import java.io.IOException;
import org.eclipse.swt.widgets.Text;
import net.bigeon.gclc.manager.PipedConsoleInput;
import net.bigeon.gclc.swt.api.ConsoleDelayIO;
/** The object managing the console input.
*
* @author Emmanuel Bigeon */
public final class ConsoleInputManager implements ConsoleDelayIO {
private final Text text;
private PipedConsoleInput input;
/** Create a managing object.
*
* @param text the text */
public ConsoleInputManager(final Text text) {
super();
this.text = text;
}
/* (non-Javadoc)
* @see net.bigeon.gclc.swt.ConsoleDelayIO#setInput(java.lang.String) */
@Override
public void setInput(final String string) {
text.setText(string);
text.setSelection(string.length());
}
/* (non-Javadoc)
* @see net.bigeon.gclc.swt.ConsoleDelayIO#getInput() */
@Override
public String getInput() {
return text.getText();
}
/* (non-Javadoc)
* @see net.bigeon.gclc.swt.ConsoleDelayIO#validateInput() */
@Override
public void validateInput() throws IOException {
input.type(text.getText());
}
/** Set the input to control.
*
* @param input the input */
public void setManager(final PipedConsoleInput input) {
this.input = input;
}
/** Get the text component containing the currently set input.
*
* @return the text */
public Text getText() {
return text;
}
}

View File

@@ -0,0 +1,107 @@
/**
*
*/
package net.bigeon.gclc.swt.io;
/*-
* #%L
* GCLC swt
* %%
* Copyright (C) 2015 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
import org.eclipse.swt.widgets.Text;
import net.bigeon.gclc.manager.PipedConsoleOutput;
import net.bigeon.gclc.swt.api.ConsoleOutputDisplay;
import net.bigeon.gclc.swt.tools.TextAppendingRunnable;
import net.bigeon.gclc.swt.tools.ToSWTConsoleForwardRunnable;
/** The manager for console output to insert in a text.
*
* @author Emmanuel Bigeon */
public final class ConsoleOutputManager implements ConsoleOutputDisplay {
/** The SWT component displaying the output content. */
private final Text text;
/** The forwarding runnable. */
private ToSWTConsoleForwardRunnable forward;
/** The forwarding thread. */
private Thread forwardThread;
/** Create the manager.
*
* @param text the text to display the output in. */
public ConsoleOutputManager(final Text text) {
super();
this.text = text;
}
/* (non-Javadoc)
* @see net.bigeon.gclc.swt.ConsoleOutputDisplay#appendLine(java.lang.String) */
@Override
public void appendLine(final String next) {
text.getDisplay().syncExec(new TextAppendingRunnable(text, next));
}
/** Set the output.
*
* @param output the output to set */
public void setManager(final PipedConsoleOutput output) {
if (forward != null) {
if (forward.getOuput() == output) {
return;
}
forward.setRunning(false);
}
if (output == null) {
forward = null;
forwardThread = null;
return;
}
forward = new ToSWTConsoleForwardRunnable(output, this, text);
forwardThread = new Thread(forward, "gclcToSWT"); //$NON-NLS-1$
forwardThread.start();
}
/** Get the current thread forwarding the console output to the SWT components.
*
* @return the forwardThread */
public Thread getForwardThread() {
return forwardThread;
}
/** @return the output consumer */
public PipedConsoleOutput getManager() {
if (forward == null) {
return null;
}
return forward.getOuput();
}
}

View File

@@ -0,0 +1,93 @@
/**
*
*/
package net.bigeon.gclc.swt.io;
/*-
* #%L
* GCLC swt
* %%
* Copyright (C) 2015 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
import java.io.BufferedReader;
import org.eclipse.swt.widgets.Label;
import net.bigeon.gclc.swt.tools.PromptReadingRunnable;
/** The manager for the console prompt updates.
*
* @author Emmanuel Bigeon */
public final class ConsolePromptManager {
/** The label to set the prompts at. */
private final Label label;
/** The current prompt reading runnable. */
private PromptReadingRunnable promptRead;
/** Create the manager.
*
* @param label the label to update with prompts */
public ConsolePromptManager(final Label label) {
super();
this.label = label;
}
/** Set the prompt.
* <p>
* This method sets the prompt on the label as requested, without changing the
* actual prompt of the console input.
*
* @param string the text */
public void setPrompt(final String string) {
label.setText(string);
}
/** Set the input to control.
*
* @param promptStream the input */
public void setStream(final BufferedReader promptStream) {
if (promptRead != null) {
if (promptRead.getReader() == promptStream) {
return;
}
promptRead.setRunning(false);
}
if (promptStream == null) {
promptRead = null;
return;
}
promptRead = new PromptReadingRunnable(promptStream, label);
final Thread th = new Thread(promptRead, "Prompt To Label");
th.start();
}
}

View File

@@ -0,0 +1,8 @@
/**
*
*/
/**
* @author Emmanuel Bigeon
*
*/
package net.bigeon.gclc.swt.io;

View File

@@ -33,7 +33,11 @@
* gclc-swt:net.bigeon.gclc.swt.HistoryTextKeyListener.java * gclc-swt:net.bigeon.gclc.swt.HistoryTextKeyListener.java
* Created on: Jun 9, 2016 * Created on: Jun 9, 2016
*/ */
package net.bigeon.gclc.swt; package net.bigeon.gclc.swt.tools;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/*- /*-
* #%L * #%L
@@ -72,8 +76,9 @@ import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyEvent;
import net.bigeon.collections.ArrayRibbon;
import net.bigeon.collections.Ribbon; import net.bigeon.collections.Ribbon;
import net.bigeon.collections.ribbon.ArrayRibbon;
import net.bigeon.gclc.swt.api.ConsoleDelayIO;
/** A key listener to validate commands and manage the history of commands. /** A key listener to validate commands and manage the history of commands.
* *
@@ -84,6 +89,8 @@ public final class HistoryTextKeyListener extends KeyAdapter {
private static final int DEFAULT_HISTORY_SIZE = 10; private static final int DEFAULT_HISTORY_SIZE = 10;
/** The empty string constant. */ /** The empty string constant. */
private static final String EMPTY = ""; //$NON-NLS-1$ private static final String EMPTY = ""; //$NON-NLS-1$
private static final Logger LOGGER = Logger
.getLogger(HistoryTextKeyListener.class.getName());
/** The history ribbon. */ /** The history ribbon. */
private final Ribbon<String> commands; private final Ribbon<String> commands;
/** The current index in history search. */ /** The current index in history search. */
@@ -118,13 +125,16 @@ public final class HistoryTextKeyListener extends KeyAdapter {
if (!input.isEmpty()) { if (!input.isEmpty()) {
commands.add(input); commands.add(input);
} }
try {
console.validateInput(); console.validateInput();
} catch (final IOException e) {
LOGGER.log(Level.SEVERE, "Unable to write to console", e);
}
currentIndex = -1; currentIndex = -1;
} }
// Upper arrow retrieves previous commands // Upper arrow retrieves previous commands
if (keyCode == SWT.ARROW_UP && if (keyCode == SWT.ARROW_UP && currentIndex < commands.size() - 1) {
currentIndex < commands.size() - 1) {
currentIndex++; currentIndex++;
final String cmd = commands.get(commands.size() - currentIndex - 1); final String cmd = commands.get(commands.size() - currentIndex - 1);
console.setInput(cmd); console.setInput(cmd);
@@ -132,12 +142,14 @@ public final class HistoryTextKeyListener extends KeyAdapter {
// Lower arrow retrieves next commands // Lower arrow retrieves next commands
if (keyCode == SWT.ARROW_DOWN) { if (keyCode == SWT.ARROW_DOWN) {
if (currentIndex <= 0) {
if (currentIndex == 0) { if (currentIndex == 0) {
currentIndex--;
console.setInput(EMPTY); console.setInput(EMPTY);
} else if (currentIndex > 0) { }
final String cmd = commands currentIndex = -1;
.get(commands.size() - (--currentIndex) - 1); } else {
currentIndex--;
final String cmd = commands.get(commands.size() - currentIndex - 1);
console.setInput(cmd); console.setInput(cmd);
} }
} }

View File

@@ -0,0 +1,134 @@
/**
*
*/
package net.bigeon.gclc.swt.tools;
/*-
* #%L
* GCLC swt
* %%
* Copyright (C) 2015 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.swt.widgets.Label;
/** The runnable forwarding prompts from a buffered input to a label.
*
* @author Emmanuel Bigeon */
public class PromptReadingRunnable implements Runnable {
/** The logger for this class. */
private static final Logger LOGGER = Logger
.getLogger(PromptReadingRunnable.class.getName());
/** The reader providing the succession of prompts */
private final BufferedReader reader;
/** The label. */
private final Label view;
/** The running status. */
private boolean running = true;
/** The updating runnable implementation.
*
* @author Emmanuel Bigeon */
private static class LabelUpdater implements Runnable {
/** The label to update. */
private final Label label;
/** The text to set. */
private final String content;
/** Create the updating runnable.
*
* @param label the label
* @param content the content */
public LabelUpdater(final Label label, final String content) {
super();
this.label = label;
this.content = content;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run() */
@Override
public void run() {
label.setText(content);
}
}
/** Create the prompt flow updating runnable.
*
* @param reader the reader
* @param view the view to update on lines. */
public PromptReadingRunnable(final BufferedReader reader, final Label view) {
super();
this.reader = reader;
this.view = view;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run() */
@Override
public void run() {
String prompt;
try {
while (isRunning() && (prompt = reader.readLine()) != null) {
view.getDisplay().syncExec(new LabelUpdater(view, prompt));
}
} catch (final IOException e) {
LOGGER.log(Level.INFO, "Prompt cannot be read, or was prematuraly closed", e);
}
}
/** Get the running state.
*
* @return the running */
public synchronized boolean isRunning() {
return running;
}
/** Set the running state.
*
* @param running the running to set */
public synchronized void setRunning(final boolean running) {
this.running = running;
}
/** Get the prompt flow.
*
* @return the reader */
public BufferedReader getReader() {
return reader;
}
}

View File

@@ -0,0 +1,36 @@
/**
*
*/
package net.bigeon.gclc.swt.tools;
import org.eclipse.swt.widgets.Text;
/** A runnable appending text to the content of a {@link Text} component.
*
* @author Emmanuel Bigeon */
public class TextAppendingRunnable implements Runnable {
/** The text to append on a line (possibly new). */
private final String next;
/** The {@link Text} component. */
private final Text text;
/** Create the appending runnable.
*
* @param text the component to update
* @param next the text to append */
public TextAppendingRunnable(final Text text, final String next) {
this.text = text;
this.next = next;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run() */
@Override
public void run() {
final String initialText = text.getText();
if (initialText != null && !initialText.isEmpty()) {
text.append(System.lineSeparator());
}
text.append(next);
}
}

View File

@@ -0,0 +1,67 @@
/**
*
*/
package net.bigeon.gclc.swt.tools;
import org.eclipse.swt.widgets.Widget;
import net.bigeon.gclc.manager.PipedConsoleOutput;
import net.bigeon.gclc.swt.api.ConsoleOutputDisplay;
import net.bigeon.gclc.tools.AOutputForwardRunnable;
/** The local implementation of the forwarding runnable.
*
* @author Emmanuel Bigeon */
public final class ToSWTConsoleForwardRunnable extends AOutputForwardRunnable {
/** The running status. */
private boolean running = true;
/** The console output. */
private final PipedConsoleOutput out;
/** The console output display. */
private final ConsoleOutputDisplay display;
/** The actual SWT component. */
private final Widget element;
/** Create the forwarding runnable.
*
* @param manager the manager
* @param display the display
* @param element the composite */
public ToSWTConsoleForwardRunnable(final PipedConsoleOutput manager,
final ConsoleOutputDisplay display,
final Widget element) {
super(manager);
out = manager;
this.display = display;
this.element = element;
}
/* (non-Javadoc)
* @see
* net.bigeon.gclc.utils.AOutputForwardRunnable#forwardLine(java.lang.String) */
@Override
protected void forwardLine(final String m) {
display.appendLine(m);
}
/* (non-Javadoc)
* @see net.bigeon.gclc.utils.AOutputForwardRunnable#isRunning() */
@Override
protected boolean isRunning() {
return running && !element.isDisposed();
}
/** Set the running status.
*
* @param running the running to set */
public void setRunning(final boolean running) {
this.running = running;
}
/** Get the output.
*
* @return the currently forwarded output */
public PipedConsoleOutput getOuput() {
return out;
}
}

View File

@@ -0,0 +1,9 @@
/**
*
*/
/** Tool classes for the library. External code should not rely on classes in
* there as they can be removed without notice.
*
* @author Emmanuel Bigeon */
package net.bigeon.gclc.swt.tools;

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
#%L
Generic Command Ligne console
%%
Copyright (C) 2014 - 2018 Bigeon
%%
This software is governed by the CeCILL license under French law and
abiding by the rules of distribution of free software. You can use,
modify and/ or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
"http://www.cecill.info".
As a counterpart to the access to the source code and rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty and the software's author, the holder of the
economic rights, and the successive licensors have only limited
liability.
In this respect, the user's attention is drawn to the risks associated
with loading, using, modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean that it is complicated to manipulate, and that also
therefore means that it is reserved for developers and experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or
data to be ensured and, more generally, to use and operate it in the
same conditions as regards security.
The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.
#L%
-->
<project name="GCLC">
<bannerRight>
<src>https://bigeon.net/images/logo_48.png</src>
<href>https://bigeon.net/</href>
</bannerRight>
<body>
<menu ref="reports"/>
</body>
</project>

View File

@@ -0,0 +1,117 @@
/**
*
*/
package net.bigeon.gclc.swt;
/*-
* #%L
* GCLC swt
* %%
* Copyright (C) 2015 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;
import java.io.IOException;
import org.eclipse.swt.widgets.Text;
import org.junit.Test;
import net.bigeon.gclc.manager.PipedConsoleInput;
import net.bigeon.gclc.swt.io.ConsoleInputManager;
/** @author Emmanuel Bigeon */
public class ConsoleInputManagerTest {
private final Text text = mock(Text.class);
private final ConsoleInputManager cim = new ConsoleInputManager(text);
/** Test method for
* {@link net.bigeon.gclc.swt.io.ConsoleInputManager#setInput(java.lang.String)}. */
@Test
public void testSetText() {
cim.setInput("Text");
verify(text).setText("Text");
}
/** Test method for
* {@link net.bigeon.gclc.swt.io.ConsoleInputManager#validateInput()}.
*
* @throws IOException if an error occurred */
@Test
public void testValidateInput() throws IOException {
// Expected calls
when(text.getText()).thenReturn("abc");
try {
cim.validateInput();
fail("No input should cause exception for input validation");
} catch (final Exception e) {
// ok
}
final PipedConsoleInput input = mock(PipedConsoleInput.class);
cim.setManager(input);
cim.validateInput();
verify(input).type("abc");
}
@Test
public void testValidateInputWithError() throws IOException {
final PipedConsoleInput input = mock(PipedConsoleInput.class);
cim.setManager(input);
// Expected calls
when(text.getText()).thenReturn("abc");
doThrow(new IOException()).when(input).type("abc");
try {
cim.validateInput();
fail("IO exception should be forwarded");
} catch (final IOException e) {
// ok
}
}
/** Test method for
* {@link net.bigeon.gclc.swt.io.ConsoleInputManager#getText()}. */
@Test
public void testGetText() {
assertEquals("Text component should be preserved", text, cim.getText());
}
@Test
public void testGetInput() {
when(text.getText()).thenReturn("abc");
assertEquals("Text component should be preserved", "abc", cim.getInput());
}
}

View File

@@ -0,0 +1,103 @@
/**
*
*/
package net.bigeon.gclc.swt;
/*-
* #%L
* GCLC swt
* %%
* Copyright (C) 2015 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import java.io.IOException;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import net.bigeon.gclc.manager.PipedConsoleOutput;
import net.bigeon.gclc.swt.io.ConsoleOutputManager;
/** @author Emmanuel Bigeon */
public class ConsoleOutputManagerTest {
private final Text text = mock(Text.class);
private final ConsoleOutputManager com = new ConsoleOutputManager(text);
private final Display display = mock(Display.class);
{
when(text.getDisplay()).thenReturn(display);
doAnswer(new Answer<Object>() {
/* (non-Javadoc)
* @see
* org.mockito.stubbing.Answer#answer(org.mockito.invocation.InvocationOnMock) */
@Override
public Object answer(final InvocationOnMock invocation) throws Throwable {
final Runnable runnable = invocation.getArgument(0);
runnable.run();
return null;
}
}).when(display).syncExec(any(Runnable.class));
}
/** Test method for
* {@link net.bigeon.gclc.swt.io.ConsoleOutputManager#appendLine(java.lang.String)}. */
@Test
public void testAppendConsoleOutput() {
when(text.getText()).thenReturn("", "abc",
"abc" + System.lineSeparator() + "def");
com.appendLine("abc");
verify(text).append("abc");
com.appendLine("def");
verify(text).append(System.lineSeparator());
verify(text).append("def");
}
@Test
public void testSetManager() throws IOException, InterruptedException {
final PipedConsoleOutput output = mock(PipedConsoleOutput.class);
com.setManager(output);
com.setManager(output);
try (PipedConsoleOutput output2 = new PipedConsoleOutput()) {
com.setManager(output2);
output2.println("line");
final Thread forwardThread = com.getForwardThread();
forwardThread.join(500);
com.setManager(null);
forwardThread.join();
verify(text).append("line");
}
}
}

View File

@@ -0,0 +1,164 @@
/**
*
*/
package net.bigeon.gclc.swt;
import static org.mockito.ArgumentMatchers.any;
/*-
* #%L
* GCLC swt
* %%
* Copyright (C) 2015 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
import static org.mockito.Mockito.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import net.bigeon.gclc.swt.io.ConsolePromptManager;
/** @author Emmanuel Bigeon */
public class ConsolePromptManagerTest {
private final Label label = mock(Label.class);
private final ConsolePromptManager manager = new ConsolePromptManager(label);
/** Test method for
* {@link net.bigeon.gclc.swt.io.ConsolePromptManager#setPrompt(java.lang.String)}. */
@Test
public void testSetPrompt() {
manager.setPrompt("abc");
verify(label).setText("abc");
}
/** Test method for
* {@link net.bigeon.gclc.swt.io.ConsolePromptManager#setStream(java.io.BufferedReader)}.
*
* @throws IOException if error */
@Test
public void testSetStream() throws IOException {
// Create the dispaly, in case...
final Display d = mock(Display.class);
when(label.getDisplay()).thenReturn(d);
Mockito.doAnswer(invocation -> {
((Runnable) invocation.getArgument(0)).run();
return null;
}).when(d).asyncExec(any(Runnable.class));
Mockito.doAnswer(invocation -> {
((Runnable) invocation.getArgument(0)).run();
return null;
}).when(d).syncExec(any(Runnable.class));
// Test.
BufferedReader output = mock(BufferedReader.class);
final Object lock = new Object();
final AtomicInteger calls = new AtomicInteger(0);
Mockito.when(output.readLine()).then(new Answer<String>() {
String[] ans = { "A line" };
@Override
public String answer(final InvocationOnMock invocation) throws Throwable {
synchronized (lock) {
if (calls.get() >= ans.length) {
calls.incrementAndGet();
lock.notify();
return null;
}
lock.notify();
return ans[calls.getAndIncrement()];
}
}
});
manager.setStream(output);
manager.setStream(output);
synchronized (lock) {
while (calls.get() < 2) {
try {
lock.wait(10);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
verify(output, times(2)).readLine();
output = mock(BufferedReader.class);
calls.set(0);
Mockito.when(output.readLine()).then(new Answer<String>() {
String[] ans = { "A line" };
@Override
public String answer(final InvocationOnMock invocation) throws Throwable {
synchronized (lock) {
if (calls.get() >= ans.length) {
calls.incrementAndGet();
lock.notify();
return null;
}
lock.notify();
return ans[calls.getAndIncrement()];
}
}
});
manager.setStream(output);
while (calls.get() < 2) {
synchronized (lock) {
try {
lock.wait(10);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
manager.setStream(null);
manager.setStream(output);
while (calls.get() < 3) {
synchronized (lock) {
try {
lock.wait(10);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
verify(output, times(3)).readLine();
verify(label, times(2)).setText("A line");
}
}

View File

@@ -69,39 +69,48 @@ package net.bigeon.gclc.swt;
* #L% * #L%
*/ */
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
import java.io.IOException;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
/** <p> import net.bigeon.gclc.swt.api.ConsoleDelayIO;
import net.bigeon.gclc.swt.tools.HistoryTextKeyListener;
/**
* <p>
* TODO * TODO
* *
* @author Emmanuel Bigeon */ * @author Emmanuel Bigeon */
public class HistoryTextKeyListenerTest { public class HistoryTextKeyListenerTest {
/** Test method for /** Test method for
* {@link net.bigeon.gclc.swt.HistoryTextKeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)}. */ * {@link net.bigeon.gclc.swt.tools.HistoryTextKeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)}. */
@Test @Test
public final void testKeyPressedKeyEvent() { public final void testKeyPressedKeyEvent() {
ConsoleDelayIO io = new ConsoleDelayIO() { final ConsoleDelayIO io = new ConsoleDelayIO() {
private String input = ""; private String input = "";
@Override
public void validateInput() {
input = "";
}
@Override
public void setInput(String input) {
this.input = input;
}
@Override @Override
public String getInput() { public String getInput() {
return input; return input;
} }
@Override
public void setInput(final String input) {
this.input = input;
}
@Override
public void validateInput() {
input = "";
}
}; };
HistoryTextKeyListener listener = new HistoryTextKeyListener(io); final HistoryTextKeyListener listener = new HistoryTextKeyListener(io);
// no effects // no effects
assertEquals("", io.getInput()); assertEquals("", io.getInput());
@@ -138,4 +147,24 @@ public class HistoryTextKeyListenerTest {
assertEquals("cmd arg2", io.getInput()); assertEquals("cmd arg2", io.getInput());
} }
@Test
public void testKeyPressedA() throws IOException {
final ConsoleDelayIO io = mock(ConsoleDelayIO.class);
final KeyEvent event = mock(KeyEvent.class);
event.keyCode = 'a';
final HistoryTextKeyListener listener = new HistoryTextKeyListener(io);
listener.keyPressed(event);
verifyNoInteractions(io);
}
@Test
public void testKeyPressedReturn() throws IOException {
final ConsoleDelayIO io = mock(ConsoleDelayIO.class);
Mockito.when(io.getInput()).thenReturn("Alpha");
final KeyEvent event = mock(KeyEvent.class);
event.keyCode = '\r';
final HistoryTextKeyListener listener = new HistoryTextKeyListener(io);
listener.keyPressed(event);
verify(io, times(1)).validateInput();
}
} }

View File

@@ -0,0 +1,102 @@
/**
*
*/
package net.bigeon.gclc.swt;
/*-
* #%L
* GCLC swt
* %%
* Copyright (C) 2015 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.junit.Test;
import org.mockito.Mockito;
import net.bigeon.gclc.swt.tools.PromptReadingRunnable;
/** @author Emmanuel Bigeon */
public class PromptReadingRunnableTest {
private final Label view = mock(Label.class);
private final Display display = mock(Display.class);
{
when(view.getDisplay()).thenReturn(display);
doAnswer(invocation -> {
final Runnable runnable = invocation.getArgument(0);
runnable.run();
return null;
}).when(display).syncExec(any(Runnable.class));
}
/** Test method for {@link net.bigeon.gclc.swt.tools.PromptReadingRunnable#run()}.
*
* @throws IOException if an error occurred */
@Test
public void testRun() throws IOException {
final BufferedReader reader = mock(BufferedReader.class);
when(reader.readLine()).thenReturn("abc", "def", null);
final PromptReadingRunnable runnable = new PromptReadingRunnable(reader, view);
runnable.run();
Mockito.verify(reader, Mockito.times(3)).readLine();
}
/** Test method for {@link net.bigeon.gclc.swt.tools.PromptReadingRunnable#run()}.
*
* @throws IOException if an error occurred */
@Test
public void testRunWithIO() throws IOException {
final BufferedReader reader = mock(BufferedReader.class);
when(reader.readLine()).thenThrow(new IOException());
final PromptReadingRunnable runnable = new PromptReadingRunnable(reader, view);
final Logger logger = Logger.getLogger(PromptReadingRunnable.class.getName());
final Level back = logger.getLevel();
logger.setLevel(Level.OFF);
runnable.run();
logger.setLevel(back);
Mockito.verify(reader, Mockito.times(1)).readLine();
}
}

View File

@@ -1,37 +1,5 @@
/*
* GCLC swt, provide a swt window for console applications
* Copyright (C) 2015-2017 E. Bigeon
* mailto:emmanuel@bigeon.fr
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
/** /**
* gclc-swt:net.bigeon.gclc.swt.SWTConsoleShellTest.java *
* Created on: Jun 8, 2016
*/ */
package net.bigeon.gclc.swt; package net.bigeon.gclc.swt;
@@ -68,299 +36,44 @@ package net.bigeon.gclc.swt;
* knowledge of the CeCILL license and that you accept its terms. * knowledge of the CeCILL license and that you accept its terms.
* #L% * #L%
*/ */
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import java.io.IOException; import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell;
import org.junit.Test; import org.junit.Test;
import net.bigeon.gclc.ConsoleApplication; import net.bigeon.gclc.manager.PipedConsoleInput;
import net.bigeon.gclc.command.Command; import net.bigeon.gclc.manager.PipedConsoleOutput;
import net.bigeon.gclc.command.base.ExitCommand; import net.bigeon.gclc.swt.io.ConsoleOutputManager;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.InvalidCommandName;
import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput;
/** /** @author Emmanuel Bigeon */
* <p>
* TODO
*
* @author Emmanuel Bigeon */
@SuppressWarnings({"javadoc", "static-method", "nls", "deprecation"})
public class SWTConsoleShellTest { public class SWTConsoleShellTest {
protected static final long TWO_SECONDS = 2000; /** Test method for
private static final Display DISPLAY = Display.getDefault(); * {@link net.bigeon.gclc.swt.SWTConsole#SWTConsole(Composite, int)}. */
@Test @Test
public void test() { public void testSWTConsoleShell() {
final SWTConsoleShell shell = new SWTConsoleShell(DISPLAY); final SWTConsole console = new SWTConsole(new Shell(), SWT.NONE);
final SWTConsole swtConsole = shell.getManager(); assertNotNull("View should be initialized with managers",
try { console.getInputManager());
final ConsoleApplication appl = new ConsoleApplication(swtConsole, assertNotNull("View should be initialized with managers",
swtConsole, "Hello", "See you"); console.getOutputManager());
appl.add(new ExitCommand("exit", appl)); assertNotNull("View should be initialized with managers",
appl.add(new Command("long") { console.getPromptManager());
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.
* manager.ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput,
* java.lang.String[]) */
@Override
public void execute(final ConsoleOutput out,
final ConsoleInput in,
final String... args) throws CommandRunException {
try {
Thread.sleep(TWO_SECONDS);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public String tip() {
return "a long running command";
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.Command#usageDetail() */
@Override
protected String usageDetail() {
// TODO Auto-generated method stub
// return null;
throw new RuntimeException("Not implemented yet");
}
});
// shell.pack();
shell.open();
final Thread applThread = new Thread(new Runnable() {
@Override
public void run() {
appl.start();
}
});
final Thread testThread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(TWO_SECONDS);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
swtConsole.setText("test"); //$NON-NLS-1$
swtConsole.validateCommand();
}
});
try {
Thread.sleep(TWO_SECONDS);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
shell.dispose();
}
});
}
});
applThread.start();
testThread.start();
while (!shell.isDisposed()) {
if (!DISPLAY.readAndDispatch()) {
DISPLAY.sleep();
}
}
// DISPLAY.dispose();
assertTrue(swtConsole.isClosed());
Thread.sleep(TWO_SECONDS);
assertFalse(appl.isRunning());
} catch (final InvalidCommandName e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
/** Test method for
* {@link net.bigeon.gclc.swt.SWTConsole#connect(PipedConsoleInput, PipedConsoleOutput, java.io.BufferedReader)}. */
@Test @Test
public void testConsoleClose() { public void testConnect() {
final SWTConsoleShell shell = new SWTConsoleShell(DISPLAY); final SWTConsole console = new SWTConsole(new Shell(), SWT.NONE);
final SWTConsole swtConsole = shell.getManager(); // Disconnection should work.
swtConsole.close(); console.connect(null, null, null);
swtConsole.setPrompt(":"); assertNull("Console should disconnect",
try { ((ConsoleOutputManager) console.getOutputManager()).getManager());
final ConsoleApplication appl = new ConsoleApplication(swtConsole,
swtConsole, "Hello", "See you");
appl.add(new ExitCommand("exit", appl));
appl.add(new Command("long") {
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.
* manager.ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput,
* java.lang.String[]) */
@Override
public void execute(final ConsoleOutput out,
final ConsoleInput in,
final String... args) throws CommandRunException {
try {
Thread.sleep(TWO_SECONDS);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
@Override
public String tip() {
return "a long running command";
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.Command#usageDetail() */
@Override
protected String usageDetail() {
// TODO Auto-generated method stub
// return null;
throw new RuntimeException("Not implemented yet");
}
});
appl.add(new Command("test") {
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.
* manager.ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput,
* java.lang.String[]) */
@Override
public void execute(final ConsoleOutput out,
final ConsoleInput in,
final String... args) throws CommandRunException {
try {
swtConsole.prompt("Test");
} catch (final IOException e) {
throw new CommandRunException("No input", e);
}
}
@Override
public String tip() {
return "a prompting running command";
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.Command#usageDetail() */
@Override
protected String usageDetail() {
// TODO Auto-generated method stub
// return null;
throw new RuntimeException("Not implemented yet");
}
});
// shell.pack();
shell.open();
final Thread applThread = new Thread(new Runnable() {
@Override
public void run() {
appl.start();
}
});
final Thread testThread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(TWO_SECONDS);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
swtConsole.setText("test"); //$NON-NLS-1$
swtConsole.validateCommand();
}
});
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
swtConsole.setText("ok"); //$NON-NLS-1$
}
});
swtConsole.validateCommand();
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
swtConsole.setText("long"); //$NON-NLS-1$
}
});
swtConsole.validateCommand();
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
swtConsole.setText("test"); //$NON-NLS-1$
}
});
swtConsole.validateCommand();
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
swtConsole.setText("test"); //$NON-NLS-1$
}
});
swtConsole.validateCommand();
try {
Thread.sleep(TWO_SECONDS);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
shell.dispose();
}
});
}
});
applThread.start();
testThread.start();
while (!shell.isDisposed()) {
if (!DISPLAY.readAndDispatch()) {
DISPLAY.sleep();
}
}
swtConsole.setPrompt(">");
try {
swtConsole.prompt();
fail("Prompting when closed should fail!");
} catch (final IOException e) {
assertNotNull(e);
}
// DISPLAY.dispose();
assertTrue(swtConsole.isClosed());
Thread.sleep(TWO_SECONDS);
assertFalse(appl.isRunning());
} catch (final InvalidCommandName e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} }

View File

@@ -1,37 +1,5 @@
/*
* GCLC swt, provide a swt window for console applications
* Copyright (C) 2015-2017 E. Bigeon
* mailto:emmanuel@bigeon.fr
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
/** /**
* gclc-swt:net.bigeon.gclc.swt.SWTConsoleShellTest.java *
* Created on: Jun 8, 2016
*/ */
package net.bigeon.gclc.swt; package net.bigeon.gclc.swt;
@@ -68,135 +36,47 @@ package net.bigeon.gclc.swt;
* knowledge of the CeCILL license and that you accept its terms. * knowledge of the CeCILL license and that you accept its terms.
* #L% * #L%
*/ */
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import java.io.IOException;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Shell;
import org.junit.Test; import org.junit.Test;
import net.bigeon.gclc.ConsoleApplication; import net.bigeon.gclc.manager.PipedConsoleInput;
import net.bigeon.gclc.command.Command; import net.bigeon.gclc.manager.PipedConsoleOutput;
import net.bigeon.gclc.command.base.ExitCommand; import net.bigeon.gclc.swt.io.ConsoleOutputManager;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.InvalidCommandName;
import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput;
import net.bigeon.gclc.utils.PipedConsoleInput;
import net.bigeon.gclc.utils.PipedConsoleOutput;
/** <p> /** @author Emmanuel Bigeon */
* TODO
*
* @author Emmanuel Bigeon */
@SuppressWarnings({"javadoc", "static-method", "nls", "deprecation"})
public class SWTConsoleViewTest { public class SWTConsoleViewTest {
protected static final long TWO_SECONDS = 2000; /** Test method for {@link net.bigeon.gclc.swt.SWTConsoleView#setFocus()}. */
private static final Display DISPLAY = Display.getDefault();
@Test @Test
public void test() { public void testSetFocus() {
final Shell shell = new Shell(DISPLAY); final SWTConsoleView view = new SWTConsoleView(new Shell(), SWT.NONE);
final SWTConsoleView swtConsole = new SWTConsoleView(shell, SWT.NONE); assertFalse("Non visible component set focus should fail", view.setFocus());
try (PipedConsoleOutput manager = new PipedConsoleOutput();
PipedConsoleInput input = new PipedConsoleInput(System.out)) {
swtConsole.setManager(manager, input);
} catch (final IOException e2) {
assertNull(e2);
}
try (PipedConsoleOutput manager = new PipedConsoleOutput();
PipedConsoleInput input = new PipedConsoleInput(System.out)) {
swtConsole.setManager(manager, input);
final ConsoleApplication appl = new ConsoleApplication(manager,
input,
"Hello", "See you");
appl.add(new ExitCommand("exit", appl));
appl.add(new Command("long") {
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager.ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, java.lang.String[])
*/
@Override
public void execute(final ConsoleOutput out, final ConsoleInput in,
final String... args) throws CommandRunException {
try {
Thread.sleep(TWO_SECONDS);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
@Override /** Test method for
public String tip() { * {@link net.bigeon.gclc.swt.SWTConsoleView#SWTConsoleView(org.eclipse.swt.widgets.Composite, int)}. */
return "a long running command"; @Test
public void testSWTConsoleView() {
final SWTConsoleView view = new SWTConsoleView(new Shell(), SWT.NONE);
assertNotNull("View should be initialized with managers", view.getInputManager());
assertNotNull("View should be initialized with managers",
view.getOutputManager());
} }
/* (non-Javadoc) /** Test method for
* @see fr.bigeon.gclc.command.Command#usageDetail() */ * {@link net.bigeon.gclc.swt.SWTConsoleView#setManager(PipedConsoleOutput, PipedConsoleInput)}. */
@Override @Test
protected String usageDetail() { public void testSetManager() {
// TODO Auto-generated method stub final SWTConsoleView view = new SWTConsoleView(new Shell(), SWT.NONE);
// return null; // Disconnection should work.
throw new RuntimeException("Not implemented yet"); view.setManager(null, null);
} assertNull("Disconnection should replace the input.",
}); ((ConsoleOutputManager) view.getOutputManager()).getManager());
// shell.pack();
shell.open();
final Thread applThread = new Thread(new Runnable() {
@Override
public void run() {
appl.start();
}
});
final Thread testThread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(TWO_SECONDS);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
swtConsole.setText("test"); //$NON-NLS-1$
swtConsole.validateInput();
}
});
try {
Thread.sleep(TWO_SECONDS);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
shell.dispose();
}
});
}
});
applThread.start();
testThread.start();
while (!shell.isDisposed()) {
if (!DISPLAY.readAndDispatch()) {
DISPLAY.sleep();
}
}
} catch (final InvalidCommandName e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} }
} }

View File

@@ -0,0 +1 @@
mock-maker-inline

79
gclc-test/pom.xml Normal file
View File

@@ -0,0 +1,79 @@
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.bigeon.config</groupId>
<artifactId>ebigeon-config</artifactId>
<version>1.8.33</version>
</parent>
<groupId>net.bigeon.gclc</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>GCLC Tests</name>
<description>Test utilities for GCLC applications and extensions</description>
<inceptionYear>2018</inceptionYear>
<organization>
<name>Bigeon</name>
<url>https://bigeon.net</url>
</organization>
<licenses>
<license>
<distribution>manual</distribution>
<name>CeCILL 2.1</name>
<url>https://cecill.info/licences/Licence_CeCILL_V2.1-en.html</url>
</license>
</licenses>
<developers>
<developer>
<email>emmanuel@bigeon.fr</email>
<name>Emmanuel Bigeon</name>
<url>bigeon.net</url>
<roles>
<role>PM</role>
</roles>
</developer>
</developers>
<scm>
<developerConnection>scm:git:gitea@git.code.bigeon.net:emmanuel/gclc-core.git</developerConnection>
<tag>HEAD</tag>
</scm>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<license.licenseName>cecill_2.1</license.licenseName>
</properties>
<dependencies>
<dependency>
<groupId>net.bigeon</groupId>
<artifactId>gclc</artifactId>
<version>2.1.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>net.bigeon.test</groupId>
<artifactId>junitmt</artifactId>
<version>1.0.4</version>
</dependency>
</dependencies>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.1</version>
</plugin>
<plugin>
<groupId>com.github.sevntu-checkstyle</groupId>
<artifactId>dsm-maven-plugin</artifactId>
<version>2.2.0</version>
</plugin>
</plugins>
</reporting>
</project>

View File

@@ -0,0 +1,54 @@
/**
*
*/
package net.bigeon.gclc.test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.function.Supplier;
import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.test.junitmt.FunctionalTestRunnable;
import net.bigeon.test.junitmt.ThreadTest;
/** @author Emmanuel Bigeon */
public final class InputContract {
public void testInputContract(final Supplier<ConsoleInput> inputs)
throws IOException, InterruptedException {
// Test close contract
final ConsoleInput input = inputs.get();
assertFalse("An input should not initially be closed", input.isClosed());
input.close();
assertTrue("An input should close", input.isClosed());
input.close();
assertTrue("A closed input should stay closed", input.isClosed());
try {
input.prompt();
fail("Closed input prompting should fail");
} catch (final IOException e) {
// ok
}
// Test interruption contract
final ConsoleInput input2 = inputs.get();
final FunctionalTestRunnable prompting = new FunctionalTestRunnable(
() -> {
try {
input2.prompt();
fail("Interrupted prompt should throw INterruptedIOException");
} catch (final InterruptedIOException e) {
// ok
}
});
final Thread th = new Thread(prompting);
th.start();
th.join(200);
input2.interruptPrompt();
ThreadTest.assertRuns(th, prompting);
}
}

View File

@@ -0,0 +1,52 @@
/** gclc-test: InputContractTest
* Created on Apr. 6, 2020
*/
package net.bigeon.gclc.test;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import org.junit.After;
import org.junit.Test;
import net.bigeon.gclc.manager.EmptyInput;
import net.bigeon.gclc.manager.StreamConsoleInput;
/** @author Emmanuel Bigeon */
public class InputContractTest {
/** Test method for
* {@link net.bigeon.gclc.test.InputContract#testInputContract(java.util.function.Supplier)}. */
@Test
public void testTestInputContract() throws IOException, InterruptedException {
final InputContract contract = new InputContract();
try (InputStream in = new PipedInputStream(new PipedOutputStream());
PrintStream out = new PrintStream("temp.txt")) {
contract.testInputContract(
() -> new StreamConsoleInput(out, in, StandardCharsets.UTF_8));
}
try {
contract.testInputContract(() -> EmptyInput.INSTANCE);
fail("Empty input does not fit the contract for inputs since it cannot be closed.");
} catch (final AssertionError e) {
// ok
}
}
@After
public void tearDown() {
final File f = new File("temp.txt");
if (f.exists()) {
f.delete();
}
}
}

View File

@@ -1,36 +1,22 @@
<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/maven-v4_0_0.xsd"> <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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.bigeon.config</groupId>
<artifactId>ebigeon-config</artifactId>
<version>1.8.33</version>
</parent>
<groupId>net.bigeon.gclc</groupId> <groupId>net.bigeon.gclc</groupId>
<artifactId>system</artifactId> <artifactId>system</artifactId>
<version>0.0.2-SNAPSHOT</version> <version>0.0.3-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<url>http://www.bigeon.net</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<copyright.email>emmanuel@bigeon.fr</copyright.email>
<license.licenseName>cecill_2.1</license.licenseName>
</properties>
<build>
<plugins>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.bigeon</groupId>
<artifactId>gclc</artifactId>
<version>2.0.7</version>
</dependency>
</dependencies>
<name>GCLC system command</name> <name>GCLC system command</name>
<description>Provide an exec command to execute system commands</description> <description>Provide an exec command to execute system commands</description>
<url>https://bigeon.net/projects/gclc.html</url>
<inceptionYear>2016</inceptionYear> <inceptionYear>2016</inceptionYear>
<organization>
<name>Bigeon</name>
<url>https://bigeon.net</url>
</organization>
<licenses> <licenses>
<license> <license>
<distribution>manual</distribution> <distribution>manual</distribution>
@@ -38,15 +24,6 @@
<url>https://cecill.info/licences/Licence_CeCILL_V2.1-en.html</url> <url>https://cecill.info/licences/Licence_CeCILL_V2.1-en.html</url>
</license> </license>
</licenses> </licenses>
<reporting>
<plugins>
<plugin>
<groupId>com.github.sevntu-checkstyle</groupId>
<artifactId>dsm-maven-plugin</artifactId>
<version>2.2.0</version>
</plugin>
</plugins>
</reporting>
<developers> <developers>
<developer> <developer>
<email>emmanuel@bigeon.fr</email> <email>emmanuel@bigeon.fr</email>
@@ -57,14 +34,46 @@
</roles> </roles>
</developer> </developer>
</developers> </developers>
<scm> <scm>
<developerConnection>scm:git:gogs@git.code.bigeon.net:emmanuel/gclc.git</developerConnection> <developerConnection>scm:git:gitea@git.code.bigeon.net:emmanuel/gclc-core.git</developerConnection>
<tag>HEAD</tag> <tag>HEAD</tag>
</scm> </scm>
<parent> <properties>
<groupId>net.bigeon.config</groupId> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<artifactId>ebigeon-config</artifactId> <copyright.email>emmanuel@bigeon.fr</copyright.email>
<version>1.8.9</version> <license.licenseName>cecill_2.1</license.licenseName>
</parent> </properties>
<dependencies>
<dependency>
<groupId>net.bigeon</groupId>
<artifactId>gclc</artifactId>
<version>2.1.5</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.15.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.1</version>
</plugin>
<plugin>
<groupId>com.github.sevntu-checkstyle</groupId>
<artifactId>dsm-maven-plugin</artifactId>
<version>2.2.0</version>
</plugin>
</plugins>
</reporting>
</project> </project>

View File

@@ -39,11 +39,8 @@ package net.bigeon.gclc.system;
*/ */
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.bigeon.gclc.command.Command; import net.bigeon.gclc.command.Command;
import net.bigeon.gclc.exception.CommandRunException; import net.bigeon.gclc.exception.CommandRunException;
@@ -51,34 +48,38 @@ import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput; import net.bigeon.gclc.manager.ConsoleOutput;
/** A command that will execute a system command. /** A command that will execute system commands.
* <p>
* Note that this class may induce security issues in the code as it can execute
* arbitrary system commands. It is recommended that it be used only in private
* applications or application used by aware parties.
* *
* @author Emmanuel Bigeon */ * @author Emmanuel Bigeon */
public class ExecSystemCommand extends Command { public class ExecSystemCommand extends Command {
/** Error message for the manager closed. */
private static final String MANAGER_WAS_CLOSED = "manager was closed";
/** The command default name */ /** The command default name */
private static final String COMMAND_DEFAULT_NAME = "exec"; //$NON-NLS-1$ private static final String COMMAND_DEFAULT_NAME = "exec"; //$NON-NLS-1$
/** The end of line separator */ /** The end of line separator */
private static final String EOL = System.lineSeparator(); private static final String EOL = System.lineSeparator();
/** The class logger */
private static final Logger LOGGER = Logger
.getLogger(ExecSystemCommand.class.getName());
/***/ /** Create the command with the name 'exec'. */
public ExecSystemCommand() { public ExecSystemCommand() {
super(COMMAND_DEFAULT_NAME); super(COMMAND_DEFAULT_NAME);
} }
/** @param name the name of the command (the input from the manager that /** Create the command with a custom name.
* should trigger this command) */ *
* @param name the name of the command (the input from the manager that should
* trigger this command) */
public ExecSystemCommand(final String name) { public ExecSystemCommand(final String name) {
super(name); super(name);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager. * @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager.
* ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, * ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, java.lang.String[]) */
* java.lang.String[]) */
@Override @Override
public void execute(final ConsoleOutput out, final ConsoleInput in, public void execute(final ConsoleOutput out, final ConsoleInput in,
final String... args) throws CommandRunException { final String... args) throws CommandRunException {
@@ -86,68 +87,28 @@ public class ExecSystemCommand extends Command {
try { try {
proc = Runtime.getRuntime().exec(args); proc = Runtime.getRuntime().exec(args);
} catch (final IOException e2) { } catch (final IOException e2) {
LOGGER.log(Level.SEVERE, "Unable to run process", e2); //$NON-NLS-1$ throw new CommandRunException("Unable to run process", e2);
return;
} }
// Stream forwarding to the application's console
final InputStream is = proc // This is started in a parallel thread.
.getInputStream(); final Thread th = ForwardingRunnable.connect(proc.getInputStream(), out,
final Thread th = new Thread(new Runnable() { "Command output");
// Set the empty prompt for the processes promptings.
@SuppressWarnings("synthetic-access")
@Override
public void run() {
try {
readToEnd(out, is);
is.close();
} catch (final CommandRunException e) {
LOGGER.log(Level.WARNING,
"Manager was closed in the meantime...", e); //$NON-NLS-1$
} catch (final IOException e) {
LOGGER.log(Level.WARNING, "Input stream was closed...", e); //$NON-NLS-1$
}
}
});
th.start();
in.setPrompt(""); //$NON-NLS-1$ in.setPrompt(""); //$NON-NLS-1$
// Forward console input to the process, whether the process is waiting or not
// for input.
final OutputStream os = proc.getOutputStream(); final OutputStream os = proc.getOutputStream();
try (BufferedWriter writer = new BufferedWriter( try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os))) {
new OutputStreamWriter(os))) {
while (th.isAlive()) { while (th.isAlive()) {
String user; final String user = in.prompt();
try { // Forward to process if not empty.
user = in.prompt(); if (!user.isEmpty()) {
} catch (final IOException e) {
throw new CommandRunException(
CommandRunExceptionType.INTERACTION,
"manager was closed", e); //$NON-NLS-1$
}
writer.write(user + EOL); writer.write(user + EOL);
} }
}
} catch (final IOException e1) { } catch (final IOException e1) {
throw new CommandRunException(CommandRunExceptionType.INTERACTION, throw new CommandRunException(CommandRunExceptionType.INTERACTION,
"manager was closed", e1); //$NON-NLS-1$ MANAGER_WAS_CLOSED, e1); // $NON-NLS-1$
}
}
/** @param is the input stream
* @throws CommandRunException if the manager was closed while writing the
* stream */
protected void readToEnd(final ConsoleOutput out,
final InputStream is) throws CommandRunException {
int c;
try {
while ((c = is.read()) != -1) {
try {
out.print(Character.valueOf((char) c).toString());
} catch (final IOException e) {
throw new CommandRunException(
CommandRunExceptionType.INTERACTION,
"manager was closed", e); //$NON-NLS-1$
}
}
} catch (final IOException e) {
LOGGER.log(Level.INFO, "input stream reading failed", e); //$NON-NLS-1$
} }
} }
@@ -164,10 +125,9 @@ public class ExecSystemCommand extends Command {
protected String usageDetail() { protected String usageDetail() {
return " The system command is a system dependend command like sh on linux or" + //$NON-NLS-1$ return " The system command is a system dependend command like sh on linux or" + //$NON-NLS-1$
System.lineSeparator() + "powershell on windows." + //$NON-NLS-1$ System.lineSeparator() + "powershell on windows." + //$NON-NLS-1$
System.lineSeparator() + System.lineSeparator() + System.lineSeparator() + System.lineSeparator()
" As an example if you give \"cat /etc/hostname\" as argument, on a linux" + //$NON-NLS-1$ + " As an example if you give \"cat\", \"/etc/hostname\" as arguments, on a linux" //$NON-NLS-1$
System.lineSeparator() + + System.lineSeparator() + "system, you would get the computer name." + //$NON-NLS-1$
"system, you would get the computer name." + //$NON-NLS-1$
System.lineSeparator(); System.lineSeparator();
} }
@@ -177,5 +137,4 @@ public class ExecSystemCommand extends Command {
protected String usagePattern() { protected String usagePattern() {
return " CMD <system command>"; //$NON-NLS-1$ return " CMD <system command>"; //$NON-NLS-1$
} }
} }

View File

@@ -0,0 +1,165 @@
package net.bigeon.gclc.system;
/*-
* #%L
* GCLC system command
* %%
* Copyright (C) 2016 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.manager.ConsoleOutput;
/** A class forwarding the content of a stream into a console output.
* <p>
* This class is useful for processes that run in a different environment than
* the application but return an output. Such processes include command
* processes and socket communications for example.
* <p>
* In the current library is is used to retrieve a system command output.
*
* @author Emmanuel Bigeon */
public final class ForwardingRunnable implements Runnable {
/** Error message for the manager closed. */
private static final String MANAGER_WAS_CLOSED = "manager was closed";
/** The class logger */
private static final Logger LOGGER = Logger
.getLogger(ForwardingRunnable.class.getName());
/** The console output. */
private final ConsoleOutput out;
/** The input stream. */
private final InputStream is;
private Exception error;
/** Create the runnable.
*
* @param out the output console
* @param is the input stream */
public ForwardingRunnable(final ConsoleOutput out, final InputStream is) {
this.out = out;
this.is = is;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run() */
@Override
public void run() {
try {
readToEnd();
} catch (final CommandRunException e) {
LOGGER.log(Level.WARNING, "Manager was closed in the meantime...", e); //$NON-NLS-1$
setError(e);
}
}
/** Read the input until its end.
* <p>
* This method actually perform a loop on the input stream to read the next
* character and then print int on the screen.
* <p>
* When the stream returns a negative value, it will exit its loop and return.
*
* @throws CommandRunException if the manager was closed while writing the
* stream */
private void readToEnd() throws CommandRunException {
int c;
try {
while ((c = is.read()) >= 0) {
// Print the character in the output
printCharacter(out, (char) c);
}
} catch (final IOException e) {
LOGGER.log(Level.INFO, "input stream reading failed", e); //$NON-NLS-1$
setError(e);
}
}
/** Print a character on the console.
*
* @param out the console
* @param c the character
* @throws CommandRunException if the console failed to print the character. */
private static void printCharacter(final ConsoleOutput out, final char c)
throws CommandRunException {
try {
out.print(Character.toString(c));
} catch (final IOException e) {
throw new CommandRunException(CommandRunExceptionType.INTERACTION,
MANAGER_WAS_CLOSED, e); // $NON-NLS-1$
}
}
/** Tool method to connect an input stream to a console output.
*
* @param stream the stream
* @param output the output
* @param name the identification of the thread
* @return the thread the forwarding runnable runs into. */
public static Thread connect(final InputStream stream, final ConsoleOutput output,
final String name) {
final Thread th = new Thread(new ForwardingRunnable(output, stream), name);
th.start();
return th;
}
/** Tool method to connect an input stream to a console output.
*
* @param stream the stream
* @param output the output
* @return the thread the forwarding runnable runs into. */
public static Thread connect(final InputStream stream, final ConsoleOutput output) {
return connect(stream, output, "Forwarding");
}
/** Get the error in this runnable execution if any.
* <p>
* In the current implementation, this error can be an {@link IOException} if
* the input stream failed or a {@link CommandRunException} if the output was
* closed.
*
* @return the error */
public synchronized Exception getError() {
return error;
}
/** Set the error that caused this runnable to stop executing.
*
* @param error the error to set */
private synchronized void setError(final Exception error) {
this.error = error;
}
}

View File

@@ -0,0 +1,42 @@
/** This package contains classes for command execution on the system.
* <p>
* The class {@link net.bigeon.gclc.system.ExecSystemCommand} is, in the current
* version (0.0.1), a source of vulnerability and should probably be used only
* in private projects where the user is aware of the dangers.
*
* @author Emmanuel Bigeon */
package net.bigeon.gclc.system;
/*-
* #%L
* GCLC system command
* %%
* Copyright (C) 2016 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
#%L
Generic Command Ligne console
%%
Copyright (C) 2014 - 2018 Bigeon
%%
This software is governed by the CeCILL license under French law and
abiding by the rules of distribution of free software. You can use,
modify and/ or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
"http://www.cecill.info".
As a counterpart to the access to the source code and rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty and the software's author, the holder of the
economic rights, and the successive licensors have only limited
liability.
In this respect, the user's attention is drawn to the risks associated
with loading, using, modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean that it is complicated to manipulate, and that also
therefore means that it is reserved for developers and experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or
data to be ensured and, more generally, to use and operate it in the
same conditions as regards security.
The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.
#L%
-->
<project name="GCLC">
<bannerRight>
<src>https://bigeon.net/images/logo_48.png</src>
<href>https://bigeon.net/</href>
</bannerRight>
<body>
<menu ref="reports"/>
</body>
</project>

View File

@@ -0,0 +1,104 @@
package net.bigeon.gclc.system;
/*-
* #%L
* GCLC system command
* %%
* Copyright (C) 2016 - 2018 Bigeon
* %%
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
* #L%
*/
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput;
import net.bigeon.gclc.manager.EmptyInput;
import net.bigeon.gclc.manager.SinkOutput;
public class ExecSystemCommandTest {
/** Test the help methods of the command. */
@Test
public void testExecSystemCommand() {
final ExecSystemCommand cmd = new ExecSystemCommand("test");
assertEquals("test", cmd.getCommandName(), "Name should be preserved");
assertNotNull(cmd.tip(), "tip should be defined");
assertNotNull(cmd.usagePattern(), "usage should be defined");
assertNotNull(cmd.usageDetail(), "usage should be detailed");
}
/** Test the execution of the command.
*
* @throws CommandRunException if the command fails */
@Test
public void testExecute() throws CommandRunException {
final ConsoleOutput out = SinkOutput.INSTANCE;
final ConsoleInput in = EmptyInput.INSTANCE;
final ExecSystemCommand cmd = new ExecSystemCommand();
if (System.getProperty("os.name").contains("indows")) {
cmd.execute(out, in, "cmd", "/C", "dir");
} else if (System.getProperty("os.name").contains("inux")) {
cmd.execute(out, in, "ls");
}
try {
cmd.execute(out, in, "inexistent");
fail("Able to execute inexistent command in system");
} catch (final CommandRunException e) {
// ok
}
}
/** Test the execution of the command.
*
* @throws CommandRunException if the command fails */
@Test
public void testLongExecute() throws CommandRunException {
final ConsoleOutput out = SinkOutput.INSTANCE;
final ConsoleInput in = EmptyInput.INSTANCE;
final ExecSystemCommand cmd = new ExecSystemCommand();
if (System.getProperty("os.name").contains("indows")) {
cmd.execute(out, in, "cmd", "/C", "dir", "/s");
} else if (System.getProperty("os.name").contains("inux")) {
cmd.execute(out, in, "ls", "-R");
}
try {
cmd.execute(out, in, "inexistent");
fail("Able to execute inexistent command in system");
} catch (final CommandRunException e) {
// ok
}
}
}

View File

@@ -0,0 +1,44 @@
/**
*
*/
package net.bigeon.gclc.system;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.manager.SinkOutput;
import net.bigeon.gclc.manager.StreamConsoleOutput;
/** @author Emmanuel Bigeon */
public class ForwardingRunnableTest {
public void testClosedOutput() {
final StreamConsoleOutput out = new StreamConsoleOutput();
out.close();
final InputStream is = new ByteArrayInputStream(new byte[] { 1, 2, 3 });
final ForwardingRunnable runnable = new ForwardingRunnable(out, is);
// Runnable should close immediatly.
runnable.run();
assertInstanceOf(CommandRunException.class, runnable.getError(),
"Error should be a CommandRunException");
}
@Test
public void testIOInInput() throws IOException {
final InputStream is = Mockito.mock(InputStream.class);
Mockito.when(is.read()).thenThrow(new IOException());
final ForwardingRunnable runnable = new ForwardingRunnable(SinkOutput.INSTANCE,
is);
// Runnable should close immediatly.
runnable.run();
assertInstanceOf(IOException.class, runnable.getError(), "Error should be an IO");
}
}

View File

@@ -1,10 +1,44 @@
<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/maven-v4_0_0.xsd"> <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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.bigeon.config</groupId>
<artifactId>ebigeon-public-conf</artifactId>
<version>1.0.12</version>
</parent>
<groupId>net.bigeon</groupId>
<artifactId>gclc</artifactId> <artifactId>gclc</artifactId>
<version>2.0.8</version> <version>2.1.6-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<url>http://bigeon.net</url> <name>Generic Command Ligne console</name>
<description>A generic framework for console applications, with customized command input and output streams.</description>
<url>https://bigeon.net/projects/gclc.html</url>
<inceptionYear>2014</inceptionYear>
<organization>
<name>Bigeon</name>
<url>https://bigeon.net/</url>
</organization>
<licenses>
<license>
<distribution>manual</distribution>
<name>CeCILL 2.1</name>
<url>https://cecill.info/licences/Licence_CeCILL_V2.1-en.html</url>
</license>
</licenses>
<developers>
<developer>
<email>emmanuel@bigeon.fr</email>
<name>Emmanuel Bigeon</name>
<url>bigeon.net</url>
<roles>
<role>PM</role>
</roles>
</developer>
</developers>
<scm>
<developerConnection>scm:git:gitea@git.code.bigeon.net:emmanuel/gclc-core.git</developerConnection>
<tag>HEAD</tag>
</scm>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.scm.id>git.code.bigeon.net</project.scm.id> <project.scm.id>git.code.bigeon.net</project.scm.id>
@@ -13,17 +47,12 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>net.bigeon.test</groupId>
<artifactId>junit</artifactId> <artifactId>junitmt</artifactId>
<version>4.11</version> <version>1.0.4</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<parent>
<groupId>net.bigeon.config</groupId>
<artifactId>ebigeon-config</artifactId>
<version>1.8.9</version>
</parent>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
@@ -38,43 +67,18 @@
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<licenses>
<license>
<distribution>manual</distribution>
<name>CeCILL 2.1</name>
<url>https://cecill.info/licences/Licence_CeCILL_V2.1-en.html</url>
</license>
</licenses>
<reporting> <reporting>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
</plugin>
<plugin> <plugin>
<groupId>com.github.sevntu-checkstyle</groupId> <groupId>com.github.sevntu-checkstyle</groupId>
<artifactId>dsm-maven-plugin</artifactId> <artifactId>dsm-maven-plugin</artifactId>
<version>2.2.0</version> <version>2.2.0</version>
</plugin> </plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jdepend-maven-plugin</artifactId>
<version>2.0</version>
</plugin>
</plugins> </plugins>
</reporting> </reporting>
<developers>
<developer>
<email>emmanuel@bigeon.fr</email>
<name>Emmanuel Bigeon</name>
<url>bigeon.net</url>
<roles>
<role>PM</role>
</roles>
</developer>
</developers>
<inceptionYear>2014</inceptionYear>
<name>Generic Command Ligne console</name>
<description>A generic framework for console applications, with customized command input and output streams.</description>
<scm>
<developerConnection>scm:git:gogs@git.code.bigeon.net:emmanuel/gclc.git</developerConnection>
<tag>gclc-2.0.8</tag>
</scm>
<groupId>net.bigeon</groupId>
</project> </project>

View File

@@ -71,9 +71,10 @@ package net.bigeon.gclc;
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.InterruptedIOException; import java.io.InterruptedIOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -88,15 +89,14 @@ import net.bigeon.gclc.i18n.Messages;
import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.ConsoleOutput; import net.bigeon.gclc.manager.ConsoleOutput;
/** /** A {@link ConsoleApplication} is an application that require the user to
* A {@link ConsoleApplication} is an application that require the user to input * input commands.
* commands.
* <p> * <p>
* A typical use case is the following: * A typical use case is the following:
* *
* <pre> * <pre>
* {@link ConsoleOutput} out = new {@link net.bigeon.gclc.utils.StreamConsoleOutput StreamConsoleOutput}(); * {@link ConsoleOutput} out = new {@link net.bigeon.gclc.manager.StreamConsoleOutput StreamConsoleOutput}();
* {@link ConsoleInput} in = new {@link net.bigeon.gclc.utils.StreamConsoleInput StreamConsoleInput}(); * {@link ConsoleInput} in = new {@link net.bigeon.gclc.manager.StreamConsoleInput StreamConsoleInput}();
* {@link ConsoleApplication} app = new {@link ConsoleApplication}(out, in, "welcome", "see you latter")}; * {@link ConsoleApplication} app = new {@link ConsoleApplication}(out, in, "welcome", "see you latter")};
* app.{@link ConsoleApplication#add(ICommand) add}("my_command", new {@link ICommand MyCommand()}); * app.{@link ConsoleApplication#add(ICommand) add}("my_command", new {@link ICommand MyCommand()});
* app.{@link ConsoleApplication#start() start()}; * app.{@link ConsoleApplication#start() start()};
@@ -126,7 +126,7 @@ public final class ConsoleApplication implements ICommandProvider {
/** The state of this application. */ /** The state of this application. */
private boolean running; private boolean running;
/** The listeners. */ /** The listeners. */
private final List<CommandRequestListener> listeners = new ArrayList<>(); private final Set<CommandRequestListener> listeners = new LinkedHashSet<>();
/** Create a console application. /** Create a console application.
* *
@@ -135,8 +135,7 @@ public final class ConsoleApplication implements ICommandProvider {
* @param welcome the welcoming message * @param welcome the welcoming message
* @param goodbye the goodbye message */ * @param goodbye the goodbye message */
public ConsoleApplication(final ConsoleOutput out, final ConsoleInput in, public ConsoleApplication(final ConsoleOutput out, final ConsoleInput in,
final String welcome, final String welcome, final String goodbye) {
final String goodbye) {
header = welcome; header = welcome;
footer = goodbye; footer = goodbye;
this.in = in; this.in = in;
@@ -161,8 +160,7 @@ public final class ConsoleApplication implements ICommandProvider {
* java.lang.String[]) */ * java.lang.String[]) */
@Override @Override
public void executeSub(final ConsoleOutput output, final ConsoleInput input, public void executeSub(final ConsoleOutput output, final ConsoleInput input,
final String command, final String command, final String... args) throws CommandRunException {
final String... args) throws CommandRunException {
root.executeSub(output, input, command, args); root.executeSub(output, input, command, args);
} }
@@ -182,8 +180,8 @@ public final class ConsoleApplication implements ICommandProvider {
/** Interpret a command line. /** Interpret a command line.
* <p> * <p>
* This method will split the command in its part and execute the command * This method will split the command in its part and execute the command with
* with {@link #executeSub(ConsoleOutput, ConsoleInput, String, String...)}. * {@link #executeSub(ConsoleOutput, ConsoleInput, String, String...)}.
* *
* @param cmd the command * @param cmd the command
* @throws IOException if the command could not be parsed */ * @throws IOException if the command could not be parsed */
@@ -191,19 +189,18 @@ public final class ConsoleApplication implements ICommandProvider {
List<String> args; List<String> args;
try { try {
args = GCLCConstants.splitCommand(cmd); args = GCLCConstants.splitCommand(cmd);
} catch (final CommandParsingException e1) { } catch (final CommandParsingException e) {
out.println("Command line cannot be parsed"); //$NON-NLS-1$ out.println("Command line cannot be parsed"); //$NON-NLS-1$
LOGGER.log(Level.FINE, "Invalid user command " + cmd, e1); //$NON-NLS-1$ LOGGER.log(Level.FINE, e, () -> "Invalid user command " + cmd); //$NON-NLS-1$
return; return;
} }
if (!args.isEmpty()) { if (!args.isEmpty()) {
try { try {
executeSub(out, in, args.get(0), Arrays.copyOfRange( executeSub(out, in, args.get(0),
args.toArray(new String[0]), 1, args.size())); Arrays.copyOfRange(args.toArray(new String[0]), 1, args.size()));
} catch (final CommandRunException e) { } catch (final CommandRunException e) {
LOGGER.log(Level.FINE, "Command failed: " + cmd, e); //$NON-NLS-1$ LOGGER.log(Level.FINE, e, () -> "Command failed: " + cmd); //$NON-NLS-1$
out.println(Messages out.println(Messages.getString("ConsoleApplication.cmd.failed", cmd)); //$NON-NLS-1$
.getString("ConsoleApplication.cmd.failed", cmd)); //$NON-NLS-1$
out.println(e.getLocalizedMessage()); out.println(e.getLocalizedMessage());
if (e.getType() == CommandRunExceptionType.USAGE) { if (e.getType() == CommandRunExceptionType.USAGE) {
get(args.get(0)).help(out); get(args.get(0)).help(out);
@@ -228,8 +225,8 @@ public final class ConsoleApplication implements ICommandProvider {
/** The running loop content. /** The running loop content.
* <p> * <p>
* This consisting in getting the command, executing it and exiting * This consisting in getting the command, executing it and exiting (restarting
* (restarting the loop). */ * the loop). */
private void runLoop() { private void runLoop() {
try { try {
final String cmd = in.prompt(); final String cmd = in.prompt();
@@ -241,8 +238,7 @@ public final class ConsoleApplication implements ICommandProvider {
} }
interpretCommand(cmd); interpretCommand(cmd);
} catch (final InterruptedIOException e) { } catch (final InterruptedIOException e) {
LOGGER.info( LOGGER.info("Prompt interrupted. It is likely the application is closing."); //$NON-NLS-1$
"Prompt interrupted. It is likely the application is closing."); //$NON-NLS-1$
LOGGER.log(Level.FINER, "Interruption of the prompt.", //$NON-NLS-1$ LOGGER.log(Level.FINER, "Interruption of the prompt.", //$NON-NLS-1$
e); e);
} catch (final IOException e) { } catch (final IOException e) {
@@ -250,8 +246,7 @@ public final class ConsoleApplication implements ICommandProvider {
running = false; running = false;
LOGGER.warning( LOGGER.warning(
"The console manager was closed. Closing the application as no one can reach it."); //$NON-NLS-1$ "The console manager was closed. Closing the application as no one can reach it."); //$NON-NLS-1$
LOGGER.log(Level.FINE, LOGGER.log(Level.FINE, "An exception caused the closing of the application", //$NON-NLS-1$
"An exception caused the closing of the application", //$NON-NLS-1$
e); e);
} }
} }
@@ -268,8 +263,7 @@ public final class ConsoleApplication implements ICommandProvider {
running = false; running = false;
LOGGER.warning( LOGGER.warning(
"The console manager was closed. Closing the application as no one can reach it."); //$NON-NLS-1$ "The console manager was closed. Closing the application as no one can reach it."); //$NON-NLS-1$
LOGGER.log(Level.FINE, LOGGER.log(Level.FINE, "An exception caused the closing of the application", //$NON-NLS-1$
"An exception caused the closing of the application", //$NON-NLS-1$
e); e);
return; return;
} }

View File

@@ -85,7 +85,7 @@ import net.bigeon.gclc.exception.CommandParsingException;
public final class GCLCConstants { public final class GCLCConstants {
/** The escaping character. */ /** The escaping character. */
private static final char ESCAPING_CHAR = getSystemEscapingChar(); private static final char ESCAPING_CHAR = '\\';
/** Hide utility class constructor. */ /** Hide utility class constructor. */
private GCLCConstants() { private GCLCConstants() {
@@ -108,13 +108,6 @@ public final class GCLCConstants {
return cmd.substring(startIndex + 1, index - 1); return cmd.substring(startIndex + 1, index - 1);
} }
/** Get the excaping character.
*
* @return the escaping character */
private static char getSystemEscapingChar() {
return '\\';
}
/** Remove escaping characters from the string. /** Remove escaping characters from the string.
* *
* @param arg the string to remove excaping character from * @param arg the string to remove excaping character from
@@ -137,7 +130,8 @@ public final class GCLCConstants {
* @param cmd the command to split in its parts * @param cmd the command to split in its parts
* @return the list of argument preceded by the command name * @return the list of argument preceded by the command name
* @throws CommandParsingException if the parsing of the command failed */ * @throws CommandParsingException if the parsing of the command failed */
public static List<String> splitCommand(final String cmd) throws CommandParsingException { public static List<String> splitCommand(final String cmd)
throws CommandParsingException {
final List<String> args = new ArrayList<>(); final List<String> args = new ArrayList<>();
// parse the string to separate arguments // parse the string to separate arguments
int index = 0; int index = 0;
@@ -152,10 +146,7 @@ public final class GCLCConstants {
continue; continue;
} }
if (c == ' ' && !inString) { if (c == ' ' && !inString) {
final String arg = cmd.substring(startIndex, index - 1); addArgument(args, cmd.substring(startIndex, index - 1));
if (!arg.isEmpty()) {
args.add(removeEscaped(arg));
}
startIndex = index; startIndex = index;
} else if (c == '"') { } else if (c == '"') {
if (inString) { if (inString) {
@@ -164,13 +155,24 @@ public final class GCLCConstants {
startIndex = index; startIndex = index;
} }
inString = startIndex == index - 1; inString = startIndex == index - 1;
} else {
// No special processing
} }
} }
if (startIndex < cmd.length()) { if (startIndex < cmd.length()) {
final String arg = cmd.substring(startIndex, cmd.length()); final String arg = cmd.substring(startIndex, cmd.length());
args.add(arg); args.add(removeEscaped(arg));
} }
return args; return args;
} }
/** Add an argument to the list of arguments if it is not an empty string.
*
* @param args the list of argument
* @param arg the candidate argument */
private static void addArgument(final List<String> args, final String arg) {
if (!arg.isEmpty()) {
args.add(removeEscaped(arg));
}
}
} }

View File

@@ -73,15 +73,18 @@ public abstract class Command implements ICommand {
/** Create the command. /** Create the command.
* *
* @param name the command name */ * @param name the command name */
public Command(final String name) { protected Command(final String name) {
super(); super();
if (name == null) {
throw new IllegalArgumentException("The command name is mandatory");
}
this.name = name; this.name = name;
} }
/** Get the brief part of the command help. /** Get the brief part of the command help.
* <p> * <p>
* This method may be overriden by implementations to improve the help * This method may be overriden by implementations to improve the help content.
* content. The default behavior is to print the tip. * The default behavior is to print the tip.
* *
* @return a brief description of the command * @return a brief description of the command
* @see Command#help(ConsoleOutput, String...) */ * @see Command#help(ConsoleOutput, String...) */
@@ -112,8 +115,8 @@ public abstract class Command implements ICommand {
* *
* @see net.bigeon.gclc.command.ICommand#help(ConsoleOutput, String...) */ * @see net.bigeon.gclc.command.ICommand#help(ConsoleOutput, String...) */
@Override @Override
public final void help(final ConsoleOutput manager, public final void help(final ConsoleOutput manager, final String... args)
final String... args) throws IOException { throws IOException {
manager.println(getCommandName()); manager.println(getCommandName());
manager.println(brief()); manager.println(brief());
manager.println(); manager.println();
@@ -123,8 +126,8 @@ public abstract class Command implements ICommand {
final String details = usageDetail(); final String details = usageDetail();
if (details != null && !details.isEmpty()) { if (details != null && !details.isEmpty()) {
manager.print(details); manager.print(details);
if (!(details.endsWith(EOL_LINUX) || if (!(details.endsWith(EOL_LINUX)
details.endsWith(System.lineSeparator()))) { || details.endsWith(System.lineSeparator()))) {
manager.println(); manager.println();
} }
} }

View File

@@ -43,6 +43,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.bigeon.gclc.exception.CommandParsingException; import net.bigeon.gclc.exception.CommandParsingException;
@@ -55,9 +56,9 @@ public final class CommandParameters {
/** Number of element for a string argument. */ /** Number of element for a string argument. */
private static final int STRINGARG_NUMBER_OF_ELEMENTS = 2; private static final int STRINGARG_NUMBER_OF_ELEMENTS = 2;
/** Boolean arguments. */ /** Boolean arguments. */
private final Map<String, Boolean> booleanArguments = new HashMap<>(); private final Map<String, Boolean> booleanArguments;
/** String arguments. */ /** String arguments. */
private final Map<String, String> stringArguments = new HashMap<>(); private final Map<String, String> stringArguments;
/** Arguments restriction on the named ones. */ /** Arguments restriction on the named ones. */
private final boolean strict; private final boolean strict;
/** additional (unnamed) parameters. */ /** additional (unnamed) parameters. */
@@ -70,9 +71,12 @@ public final class CommandParameters {
* @param strict if the argument are restricted to the declared ones */ * @param strict if the argument are restricted to the declared ones */
public CommandParameters(final Set<String> bools, final Set<String> strings, public CommandParameters(final Set<String> bools, final Set<String> strings,
final boolean strict) { final boolean strict) {
booleanArguments = new ConcurrentHashMap<>(bools.size());
for (final String string : bools) { for (final String string : bools) {
booleanArguments.put(string, Boolean.FALSE); booleanArguments.put(string, Boolean.FALSE);
} }
// Cannot use concurrent because of the null values.
stringArguments = new HashMap<>(strings.size());
for (final String string : strings) { for (final String string : strings) {
stringArguments.put(string, null); stringArguments.put(string, null);
} }
@@ -98,9 +102,9 @@ public final class CommandParameters {
* *
* @param key the key * @param key the key
* @return if the key was specified */ * @return if the key was specified */
public boolean getBool(final String key) { public boolean isActive(final String key) {
return booleanArguments.containsKey(key) && final Boolean val = booleanArguments.get(key);
booleanArguments.get(key).booleanValue(); return val != null && val.booleanValue();
} }
/** Get the boolean arguments. /** Get the boolean arguments.
@@ -122,14 +126,13 @@ public final class CommandParameters {
* @param key the key * @param key the key
* @return if the key is present in string arguments or boolean ones. */ * @return if the key is present in string arguments or boolean ones. */
public boolean hasArgument(final String key) { public boolean hasArgument(final String key) {
return stringArguments.containsKey(key) || return stringArguments.containsKey(key) || booleanArguments.containsKey(key);
booleanArguments.containsKey(key);
} }
/** Attempt to parse an argument. /** Attempt to parse an argument.
* <p> * <p>
* This method return 0 if the parsing was incorrect, or the number of * This method return 0 if the parsing was incorrect, or the number of parsed
* parsed elements. * elements.
* *
* @param arg the argument * @param arg the argument
* @param next the next element * @param next the next element
@@ -170,8 +173,7 @@ public final class CommandParameters {
} }
final int p = parseArg(args[i], next); final int p = parseArg(args[i], next);
if (p == 0) { if (p == 0) {
throw new CommandParsingException( throw new CommandParsingException("Invalid parameter " + args[i]); //$NON-NLS-1$
"Invalid parameter " + args[i]); //$NON-NLS-1$
} }
i += p; i += p;
} }
@@ -196,9 +198,8 @@ public final class CommandParameters {
* @param string the key * @param string the key
* @param value the value */ * @param value the value */
public void set(final String string, final boolean value) { public void set(final String string, final boolean value) {
if (booleanArguments.containsKey(string)) { booleanArguments.computeIfPresent(string,
booleanArguments.put(string, Boolean.valueOf(value)); (final String k, final Boolean v) -> Boolean.valueOf(value));
}
} }
/** Set a string parameter value. /** Set a string parameter value.
@@ -206,6 +207,9 @@ public final class CommandParameters {
* @param string the key * @param string the key
* @param value the value */ * @param value the value */
public void set(final String string, final String value) { public void set(final String string, final String value) {
// DO NOT USE computeIfPresent. This is a HashMap, not a ConcurrentHashMap and
// keys can be associated to null, but computeIfPresent will consider them as
// absent!
if (stringArguments.containsKey(string)) { if (stringArguments.containsKey(string)) {
stringArguments.put(string, value); stringArguments.put(string, value);
} }

View File

@@ -35,8 +35,8 @@ package net.bigeon.gclc.command;
* knowledge of the CeCILL license and that you accept its terms. * knowledge of the CeCILL license and that you accept its terms.
* #L% * #L%
*/ */
import java.util.ArrayList; import java.util.LinkedHashSet;
import java.util.List; import java.util.Set;
import net.bigeon.gclc.exception.CommandRunException; import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.InvalidCommandName; import net.bigeon.gclc.exception.InvalidCommandName;
@@ -52,24 +52,17 @@ public class CommandProvider implements ICommandProvider {
private static final String MINUS = "-"; //$NON-NLS-1$ private static final String MINUS = "-"; //$NON-NLS-1$
/** The space character. */ /** The space character. */
private static final String SPACE = " "; //$NON-NLS-1$ private static final String SPACE = " "; //$NON-NLS-1$
/** The commands map. */
protected final List<ICommand> commands; /** The commands set.
* <p>
* The insertion order is conserved through the use of a
* {@link LinkedHashSet}. */
protected final Set<ICommand> commands;
/** Create a command provider. */ /** Create a command provider. */
public CommandProvider() { public CommandProvider() {
super(); super();
commands = new ArrayList<>(); commands = new LinkedHashSet<>();
}
/** Test the command name validity.
*
* @param name the command name
* @throws InvalidCommandName if the name is invalid */
private static void testCommandName(final String name) throws InvalidCommandName {
if (name == null || name.isEmpty() || name.startsWith(MINUS)
|| name.contains(SPACE)) {
throw new InvalidCommandName();
}
} }
/* (non-Javadoc) /* (non-Javadoc)
@@ -118,4 +111,15 @@ public class CommandProvider implements ICommandProvider {
} }
return null; return null;
} }
/** Test the command name validity.
*
* @param name the command name
* @throws InvalidCommandName if the name is invalid */
private static void testCommandName(final String name) throws InvalidCommandName {
if (name == null || name.isEmpty() || name.startsWith(MINUS)
|| name.contains(SPACE)) {
throw new InvalidCommandName();
}
}
} }

View File

@@ -90,8 +90,8 @@ public interface ICommand {
* @param in the input * @param in the input
* @param args the arguments * @param args the arguments
* @throws CommandRunException if the command failed */ * @throws CommandRunException if the command failed */
void execute(ConsoleOutput out, ConsoleInput in, void execute(ConsoleOutput out, ConsoleInput in, String... args)
String... args) throws CommandRunException; throws CommandRunException;
/** Get teh command name. /** Get teh command name.
* *

View File

@@ -81,8 +81,8 @@ import net.bigeon.gclc.manager.ConsoleOutput;
* @author Emmanuel BIGEON */ * @author Emmanuel BIGEON */
public interface ICommandProvider { public interface ICommandProvider {
/** Adds a command to this provider, if no command was associated with the /** Adds a command to this provider, if no command was associated with the given
* given key. * key.
* *
* @param value the command to execute * @param value the command to execute
* @return if the command was added * @return if the command was added
@@ -91,25 +91,25 @@ public interface ICommandProvider {
/** Execute the command with the given name. /** Execute the command with the given name.
* <p> * <p>
* If no command with this name is found, an error command is usually * If no command with this name is found, an error command is usually executed.
* executed. If there are several commands with the same name, the behavior * If there are several commands with the same name, the behavior is
* is unspecified. Depending on the implementation, it may run an error * unspecified. Depending on the implementation, it may run an error command or
* command or prompt the user for a choice. * prompt the user for a choice.
* *
* @param out the output * @param out the output
* @param in the input * @param in the input
* @param command the name of the command the user wishes to execute * @param command the name of the command the user wishes to execute
* @param args the arguments for the command * @param args the arguments for the command
* @throws CommandRunException if the command failed to run */ * @throws CommandRunException if the command failed to run */
void executeSub(ConsoleOutput out, ConsoleInput in, String command, void executeSub(ConsoleOutput out, ConsoleInput in, String command, String... args)
String... args) throws CommandRunException; throws CommandRunException;
/** Get the command with the given name. /** Get the command with the given name.
* <p> * <p>
* If no command with this name is found, an error command is usually * If no command with this name is found, an error command is usually returned.
* returned. If there are several commands with the same name, the behavior * If there are several commands with the same name, the behavior is
* is unspecified. Depending on the implementation, it may return an error * unspecified. Depending on the implementation, it may return an error command
* command or the first command with this name found. * or the first command with this name found.
* *
* @param command the name of the command the user wishes to execute * @param command the name of the command the user wishes to execute
* @return the command to execute */ * @return the command to execute */

View File

@@ -89,26 +89,24 @@ public abstract class ParametrizedCommand extends Command {
/** Create a parametrized command. /** Create a parametrized command.
* <p> * <p>
* Implementation are supposed to call the * Implementation are supposed to call the {@link #addBooleanParameter(String)}
* {@link #addBooleanParameter(String)} and * and {@link #addStringParameter(String, boolean)} method to set the
* {@link #addStringParameter(String, boolean)} method to set the
* parameters. * parameters.
* *
* @param name the name */ * @param name the name */
public ParametrizedCommand(final String name) { protected ParametrizedCommand(final String name) {
this(name, true); this(name, true);
} }
/** Create a parametrized command. /** Create a parametrized command.
* <p> * <p>
* Implementation are supposed to call the * Implementation are supposed to call the {@link #addBooleanParameter(String)}
* {@link #addBooleanParameter(String)} and * and {@link #addStringParameter(String, boolean)} method to set the
* {@link #addStringParameter(String, boolean)} method to set the
* parameters. * parameters.
* *
* @param name the name * @param name the name
* @param strict if the arguments are restricted to the declared ones */ * @param strict if the arguments are restricted to the declared ones */
public ParametrizedCommand(final String name, final boolean strict) { protected ParametrizedCommand(final String name, final boolean strict) {
super(name); super(name);
data = new ParametrizedCommandData(strict); data = new ParametrizedCommandData(strict);
} }
@@ -116,9 +114,9 @@ public abstract class ParametrizedCommand extends Command {
/** Add a boolean parameter to defined parmaters. /** Add a boolean parameter to defined parmaters.
* *
* @param flag the boolean flag * @param flag the boolean flag
* @throws InvalidParameterException if the parameter is already defined as * @throws InvalidParameterException if the parameter is already defined as a
* a string parameter */ * string parameter */
protected final void addBooleanParameter(final String flag) throws InvalidParameterException { protected final void addBooleanParameter(final String flag) {
data.addBooleanParameter(flag); data.addBooleanParameter(flag);
} }
@@ -126,10 +124,9 @@ public abstract class ParametrizedCommand extends Command {
* *
* @param flag the parameter flag * @param flag the parameter flag
* @param needed if the parameter's absence should cause an exception * @param needed if the parameter's absence should cause an exception
* @throws InvalidParameterException if the parameter is already defined as * @throws InvalidParameterException if the parameter is already defined as a
* a boolean parameter */ * boolean parameter */
protected final void addStringParameter(final String flag, protected final void addStringParameter(final String flag, final boolean needed) {
final boolean needed) throws InvalidParameterException {
data.addStringParameter(flag, needed); data.addStringParameter(flag, needed);
} }
@@ -145,8 +142,7 @@ public abstract class ParametrizedCommand extends Command {
/* (non-Javadoc) /* (non-Javadoc)
* @see net.bigeon.gclc.command.Command#execute(java.lang.String[]) */ * @see net.bigeon.gclc.command.Command#execute(java.lang.String[]) */
@Override @Override
public final void execute(final ConsoleOutput output, public final void execute(final ConsoleOutput output, final ConsoleInput input,
final ConsoleInput input,
final String... args) throws CommandRunException { final String... args) throws CommandRunException {
try { try {
doExecute(output, input, data.getParameters(input, args)); doExecute(output, input, data.getParameters(input, args));

View File

@@ -75,19 +75,18 @@ import java.io.IOException;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.bigeon.gclc.exception.CommandParsingException; import net.bigeon.gclc.exception.CommandParsingException;
import net.bigeon.gclc.exception.InvalidParameterException; import net.bigeon.gclc.exception.InvalidParameterException;
import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.ConsoleInput;
import net.bigeon.gclc.manager.EmptyInput; import net.bigeon.gclc.manager.EmptyInput;
import java.util.Set;
/** An object to handle standardized command parameters. /** An object to handle standardized command parameters.
* *
* @author Emmanuel BIGEON */ * @author Emmanuel BIGEON */
@@ -95,10 +94,11 @@ public final class ParametrizedCommandData {
/** The boolean parameters mandatory status. */ /** The boolean parameters mandatory status. */
private final Set<String> boolParams = new HashSet<>(); private final Set<String> boolParams = new HashSet<>();
/** The string parameters mandatory status. */ /** The string parameters mandatory status. */
private final Map<String, Boolean> stringParams = new HashMap<>(); private final Map<String, Boolean> stringParams = new ConcurrentHashMap<>();
/** The parameters mandatory status. */ /** The parameters mandatory status. */
private final Map<String, Boolean> params = new HashMap<>(); private final Map<String, Boolean> params = new ConcurrentHashMap<>();
/** The restriction of provided parameters on execution to declared paramters in /** The restriction of provided parameters on execution to declared paramters in
* the status maps. */ * the status maps. */
private final boolean strict; private final boolean strict;
@@ -120,8 +120,7 @@ public final class ParametrizedCommandData {
* @param flag the boolean flag * @param flag the boolean flag
* @throws InvalidParameterException if the parameter is already defined as a * @throws InvalidParameterException if the parameter is already defined as a
* string parameter */ * string parameter */
public final void addBooleanParameter(final String flag) public void addBooleanParameter(final String flag) {
throws InvalidParameterException {
if (params.containsKey(flag) && stringParams.containsKey(flag)) { if (params.containsKey(flag) && stringParams.containsKey(flag)) {
throw new InvalidParameterException("Parameter is already defined as string"); //$NON-NLS-1$ throw new InvalidParameterException("Parameter is already defined as string"); //$NON-NLS-1$
} }
@@ -135,8 +134,7 @@ public final class ParametrizedCommandData {
* @param needed if the parameter's absence should cause an exception * @param needed if the parameter's absence should cause an exception
* @throws InvalidParameterException if the parameter is already defined as a * @throws InvalidParameterException if the parameter is already defined as a
* boolean parameter */ * boolean parameter */
public final void addStringParameter(final String flag, final boolean needed) public void addStringParameter(final String flag, final boolean needed) {
throws InvalidParameterException {
if (params.containsKey(flag)) { if (params.containsKey(flag)) {
checkParam(flag, needed); checkParam(flag, needed);
return; return;
@@ -150,16 +148,31 @@ public final class ParametrizedCommandData {
* @param param the string parameter * @param param the string parameter
* @param needed if the parameter is needed * @param needed if the parameter is needed
* @throws InvalidParameterException if the new definition is invalid */ * @throws InvalidParameterException if the new definition is invalid */
private void checkParam(final String param, final boolean needed) private void checkParam(final String param, final boolean needed) {
throws InvalidParameterException { final Boolean val = stringParams.computeIfPresent(param,
if (stringParams.containsKey(param)) { (final String k, final Boolean v) -> {
final Boolean need = Boolean final Boolean need = Boolean.valueOf(needed || v.booleanValue());
.valueOf(needed || stringParams.get(param).booleanValue());
stringParams.put(param, need);
params.put(param, need); params.put(param, need);
return; return need;
});
if (val == null) {
throw new InvalidParameterException(
"Parameter is already defined as boolean"); //$NON-NLS-1$
} }
throw new InvalidParameterException("Parameter is already defined as boolean"); //$NON-NLS-1$ }
/** Retrieve the boolean parameters (aka flags).
*
* @return the set of boolean parameters */
public Set<String> getBooleanParameters() {
return Collections.unmodifiableSet(boolParams);
}
/** Retrieve the parameter names.
*
* @return the stringParams */
public Set<String> getParameters() {
return params.keySet();
} }
/** Get the parameters from an input. /** Get the parameters from an input.
@@ -168,8 +181,8 @@ public final class ParametrizedCommandData {
* @param args the command arguments * @param args the command arguments
* @return the command object * @return the command object
* @throws IOException if the command could not be filled. */ * @throws IOException if the command could not be filled. */
public final CommandParameters getParameters(final ConsoleInput input, public CommandParameters getParameters(final ConsoleInput input, final String... args)
final String... args) throws IOException { throws IOException {
final CommandParameters parameters = new CommandParameters(boolParams, final CommandParameters parameters = new CommandParameters(boolParams,
stringParams.keySet(), strict); stringParams.keySet(), strict);
try { try {
@@ -192,6 +205,29 @@ public final class ParametrizedCommandData {
return parameters; return parameters;
} }
/** Get the string parameters names.
*
* @return the stringParams */
public Set<String> getStringParameters() {
return stringParams.keySet();
}
/** Test if a parameter is needed.
*
* @param param the parameter name
* @return if the parameter is needed */
public boolean isNeeded(final String param) {
final Boolean val = params.get(param);
return val != null && val.booleanValue();
}
/** If the command refuse unrecognized parameters.
*
* @return the strict */
public boolean isStrict() {
return strict;
}
/** Fill the undefined parameters. /** Fill the undefined parameters.
* <p> * <p>
* This method prompts the user to fill the needed parameters. * This method prompts the user to fill the needed parameters.
@@ -200,7 +236,7 @@ public final class ParametrizedCommandData {
* @param parameters the parameter list to complete * @param parameters the parameter list to complete
* @param toProvide the parameters to ask for * @param toProvide the parameters to ask for
* @throws IOException if the manager was closed */ * @throws IOException if the manager was closed */
private final static void fillParameters(final ConsoleInput input, private static void fillParameters(final ConsoleInput input,
final List<String> toProvide, final CommandParameters parameters) final List<String> toProvide, final CommandParameters parameters)
throws IOException { throws IOException {
for (final String string : toProvide) { for (final String string : toProvide) {
@@ -214,40 +250,4 @@ public final class ParametrizedCommandData {
parameters.set(string, value); parameters.set(string, value);
} }
} }
/** Retrieve the boolean parameters (aka flags).
*
* @return the set of boolean parameters */
public final Set<String> getBooleanParameters() {
return Collections.unmodifiableSet(boolParams);
}
/** Retrieve the parameter names.
*
* @return the stringParams */
public final Set<String> getParameters() {
return params.keySet();
}
/** Get the string parameters names.
*
* @return the stringParams */
public final Set<String> getStringParameters() {
return stringParams.keySet();
}
/** Test if a parameter is needed.
*
* @param param the parameter name
* @return if the parameter is needed */
public final boolean isNeeded(final String param) {
return params.containsKey(param) && params.get(param).booleanValue();
}
/** If the command refuse unrecognized parameters.
*
* @return the strict */
public final boolean isStrict() {
return strict;
}
} }

Some files were not shown because too many files have changed in this diff Show More