Skip to content

Commit 5d48241

Browse files
Decoupling core built-in listeners
1 parent 933970a commit 5d48241

File tree

10 files changed

+286
-130
lines changed

10 files changed

+286
-130
lines changed

README.md

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ new Cmd().command("echo", "Hello").execute();
4242
````java
4343
new Cmd()
4444
.interpreter("sh") // specify command interpreter
45-
.command("-c", "s='Hello'; echo $s;").execute();
45+
.command("-c", "s='Hello'; echo $s;")
46+
.execute();
4647
````
4748
or even shorter
4849
````java
@@ -52,29 +53,33 @@ new Cmd().command("sh", "-c", "s='Hello'; echo $s;").execute();
5253
````java
5354
String output = new Cmd()
5455
.configuring(c -> c.readOutput(true)) // configure zt-exec's executor
55-
.interpreter("sh")
56-
.command("-c", "s='Hello'; echo $s;").execute()
56+
.command("sh", "-c", "s='Hello'; echo $s;")
57+
.execute()
5758
.outputUTF8();
5859
System.out.println(output);
5960

6061
// output> Hello
6162
````
62-
> Save output stream into a file, even if the process stopped unexpectedly
63+
> Save an output stream into a file, even if the process stopped unexpectedly
6364
```java
6465
new Cmd()
65-
.outputFileName("output.txt")
66-
.command("echo", "Hello").execute();
66+
.configuring(new RedirectToFile("./output.txt"))
67+
.command("echo", "Hello")
68+
.execute();
6769
````
68-
> Execute command inside a separate work directory. It creates work directory before start and delete after finish
70+
> Execute command within custom work directory
6971
````java
7072
new Cmd()
71-
.configuring(c -> c.directory(new File("./", "foo"))) // specify work directory ./foo
72-
.cleanUp(true) // delete work directory after process stopped, only if the directory will be created during the execution
73+
.configuring(
74+
new WorkDir("./foo"), // specify work directory ./foo (will be created automatically)
75+
new CleanUp() // delete work directory after process stop, only if the Cmd has created the directory
76+
)
7377
.listening().afterStop(process -> {
74-
//work directory ./foo will be exist here
78+
System.out.println(new File("./foo").exists()); //true
7579
}).back()
76-
.command("echo", "hello world").execute(); // work directory ./foo will be created automatically
77-
//work directory ./foo was deleted after execution
80+
.command("echo", "hello world").execute();
81+
82+
System.out.println(new File("./foo").exists()); // false
7883
````
7984
> Run command in a background
8085
````java

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<groupId>com.enot</groupId>
55
<artifactId>CmdTool</artifactId>
66
<packaging>jar</packaging>
7-
<version>0.3.2</version>
7+
<version>0.4.0</version>
88
<name>CmdTool</name>
99
<url>http://maven.apache.org</url>
1010

Lines changed: 47 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,41 @@
11
package com.enot.cmd.core;
22

3-
import org.apache.commons.io.FileUtils;
3+
import com.enot.cmd.core.LambdaListenerAdapter.AfterFinish;
4+
import com.enot.cmd.core.LambdaListenerAdapter.AfterStart;
5+
import com.enot.cmd.core.LambdaListenerAdapter.AfterStop;
6+
import com.enot.cmd.core.LambdaListenerAdapter.BeforeStart;
47
import org.cactoos.iterable.IterableOf;
58
import org.cactoos.iterable.Joined;
69
import org.cactoos.iterable.Mapped;
710
import org.zeroturnaround.exec.ProcessExecutor;
811

9-
import java.io.File;
10-
import java.io.IOException;
11-
import java.io.OutputStream;
12-
import java.io.UncheckedIOException;
13-
import java.nio.file.Files;
14-
import java.nio.file.Path;
15-
import java.nio.file.Paths;
16-
import java.nio.file.StandardOpenOption;
12+
import java.util.Arrays;
13+
import java.util.Collections;
14+
import java.util.List;
15+
import java.util.Map;
16+
import java.util.stream.Collectors;
1717

1818
/**
1919
* Command line representation with the additional features around a process execution
2020
*/
2121
public final class Cmd implements ICmd {
22-
private final boolean cleanUp;
23-
private final String outputFileName;
2422
private final Listening listening;
25-
private final LambdaListenerAdapter.BeforeStart configuring;
23+
private final BeforeStart[] beforeStart;
2624
private final String interpreter;
2725

2826
public Cmd() {
29-
this(false,
30-
"",
31-
new Listening(null, new IterableOf<>()),
32-
e -> {
33-
},
34-
"");
27+
this(new Listening(null, new IterableOf<>()), new BeforeStart[0], "");
3528
}
3629

37-
public Cmd(boolean cleanUp, String outputFileName, Listening listening, LambdaListenerAdapter.BeforeStart configuring, String interpreter) {
38-
this.cleanUp = cleanUp;
39-
this.outputFileName = outputFileName;
30+
public Cmd(Listening listening, BeforeStart[] beforeStart, String interpreter) {
4031
this.listening = listening;
41-
this.configuring = configuring;
32+
this.beforeStart = beforeStart;
4233
this.interpreter = interpreter;
4334
}
4435

45-
/**
46-
* Delete work directory after process stopped, only if the directory will be created during the execution
47-
*
48-
* @param cleanUp
49-
* @return
50-
*/
5136
@Override
52-
public Cmd cleanUp(boolean cleanUp) {
53-
return new Cmd(cleanUp, outputFileName, listening, configuring, interpreter);
54-
}
55-
56-
@Override
57-
public Cmd outputFileName(String outputFileName) {
58-
return new Cmd(cleanUp, outputFileName, listening, configuring, interpreter);
59-
}
60-
61-
@Override
62-
public Cmd configuring(LambdaListenerAdapter.BeforeStart configuring) {
63-
return new Cmd(cleanUp, outputFileName, listening, configuring, interpreter);
37+
public Cmd configuring(BeforeStart... configuring) {
38+
return new Cmd(listening, configuring, interpreter);
6439
}
6540

6641
@Override
@@ -70,7 +45,7 @@ public CmdListening listening() {
7045

7146
@Override
7247
public Cmd interpreter(String interpreter) {
73-
return new Cmd(cleanUp, outputFileName, listening, configuring, interpreter);
48+
return new Cmd(listening, beforeStart, interpreter);
7449
}
7550

7651
public Command command(String... command) {
@@ -79,77 +54,51 @@ public Command command(String... command) {
7954

8055
private ProcessExecutor processExecutor(String... command) {
8156
ProcessExecutor executor = new ProcessExecutor();
82-
configuring.run(executor);
83-
for (LambdaListenerAdapter listener : listening.listeners) {
84-
executor.addListener(listener);
85-
}
57+
58+
Map<Boolean, List<BeforeStart>> configuring = Arrays.stream(beforeStart).collect(
59+
Collectors.groupingBy(c -> c instanceof AfterStop));
60+
List<BeforeStart> configuringBefore = configuring.getOrDefault(false, Collections.emptyList());
61+
List<BeforeStart> configuringAfter = configuring.getOrDefault(true, Collections.emptyList());
62+
63+
configuringBefore.forEach(c -> c.run(executor));
64+
listening.listeners.forEach(executor::addListener);
65+
configuringAfter.forEach(c -> c.run(executor));
8666

8767
Iterable<String> commands = new IterableOf<>(command);
8868
if (interpreter != null && !interpreter.trim().isEmpty()) {
8969
commands = new Joined<>(new IterableOf<>(interpreter), commands);
9070
}
91-
92-
LambdaListenerAdapter beforeStart = new LambdaListenerAdapter((LambdaListenerAdapter.BeforeStart) processExecutor -> {
93-
File dir = processExecutor.getDirectory();
94-
if (dir != null && !dir.exists()) {
95-
final boolean workDirCreated = dir.mkdirs();
96-
if (!workDirCreated)
97-
throw new UncheckedIOException(
98-
new IOException(String.format("Work directory %s can not be created", dir.toPath())));
99-
if (cleanUp) {
100-
processExecutor.addListener(new LambdaListenerAdapter((LambdaListenerAdapter.AfterStop) p -> {
101-
try {
102-
FileUtils.deleteDirectory(dir);
103-
} catch (IOException e) {
104-
throw new UncheckedIOException(
105-
String.format("Work directory %s can not be deleted", dir.toPath()),
106-
e);
107-
}
108-
}));
109-
}
110-
}
111-
if (outputFileName != null && outputFileName.length() > 0) {
112-
OutputStream outputStream = createFileOS(dir);
113-
processExecutor.redirectOutputAlsoTo(outputStream);
114-
processExecutor.addListener(new LambdaListenerAdapter((LambdaListenerAdapter.AfterStop) process -> {
115-
try {
116-
outputStream.close();
117-
} catch (IOException e) {
118-
throw new UncheckedIOException(e);
119-
}
120-
}));
121-
}
122-
});
123-
return executor.command(commands).addListener(beforeStart);
71+
return executor.command(commands);
12472
}
12573

126-
private OutputStream createFileOS(File workDir) {
127-
Path outputFile;
128-
if (workDir != null) {
129-
outputFile = Paths.get(workDir.getPath(), outputFileName);
130-
} else {
131-
outputFile = Paths.get(outputFileName);
132-
}
133-
try {
134-
return Files.newOutputStream(outputFile, StandardOpenOption.CREATE);
135-
} catch (IOException e) {
136-
throw new UncheckedIOException(
137-
String.format("Output file %s can not be created", outputFile),
138-
e);
139-
}
140-
}
14174

14275
private static final class Listening implements CmdListening {
14376
private final Cmd owner;
14477
private final Iterable<LambdaListenerAdapter> listeners;
14578

79+
public Listening(Cmd owner, BeforeStart... lambdas) {
80+
this(owner, new Mapped<>(LambdaListenerAdapter::new, new IterableOf<>(lambdas)));
81+
}
82+
83+
public Listening(Cmd owner, AfterStart... lambdas) {
84+
this(owner, new Mapped<>(LambdaListenerAdapter::new, new IterableOf<>(lambdas)));
85+
}
86+
87+
public Listening(Cmd owner, AfterFinish... lambdas) {
88+
this(owner, new Mapped<>(LambdaListenerAdapter::new, new IterableOf<>(lambdas)));
89+
}
90+
91+
public Listening(Cmd owner, AfterStop... lambdas) {
92+
this(owner, new Mapped<>(LambdaListenerAdapter::new, new IterableOf<>(lambdas)));
93+
}
94+
14695
public Listening(Cmd owner, Iterable<LambdaListenerAdapter> listeners) {
14796
this.owner = owner;
14897
this.listeners = listeners;
14998
}
15099

151100
@Override
152-
public CmdListening beforeStart(LambdaListenerAdapter.BeforeStart... lambdas) {
101+
public CmdListening beforeStart(BeforeStart... lambdas) {
153102
return new Listening(
154103
owner,
155104
new Joined<>(
@@ -159,7 +108,7 @@ public CmdListening beforeStart(LambdaListenerAdapter.BeforeStart... lambdas) {
159108
}
160109

161110
@Override
162-
public CmdListening afterStart(LambdaListenerAdapter.AfterStart... lambdas) {
111+
public CmdListening afterStart(AfterStart... lambdas) {
163112
return new Listening(
164113
owner,
165114
new Joined<>(
@@ -169,7 +118,7 @@ public CmdListening afterStart(LambdaListenerAdapter.AfterStart... lambdas) {
169118
}
170119

171120
@Override
172-
public CmdListening afterFinish(LambdaListenerAdapter.AfterFinish... lambdas) {
121+
public CmdListening afterFinish(AfterFinish... lambdas) {
173122
return new Listening(
174123
owner,
175124
new Joined<>(
@@ -179,7 +128,7 @@ public CmdListening afterFinish(LambdaListenerAdapter.AfterFinish... lambdas) {
179128
}
180129

181130
@Override
182-
public CmdListening afterStop(LambdaListenerAdapter.AfterStop... lambdas) {
131+
public CmdListening afterStop(AfterStop... lambdas) {
183132
return new Listening(
184133
owner,
185134
new Joined<>(
@@ -190,7 +139,7 @@ public CmdListening afterStop(LambdaListenerAdapter.AfterStop... lambdas) {
190139

191140
@Override
192141
public Cmd back() {
193-
return new Cmd(owner.cleanUp, owner.outputFileName, this, owner.configuring, owner.interpreter);
142+
return new Cmd(this, owner.beforeStart, owner.interpreter);
194143
}
195144
}
196145
}
Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package com.enot.cmd.core;
22

3+
import com.enot.cmd.core.LambdaListenerAdapter.AfterFinish;
4+
import com.enot.cmd.core.LambdaListenerAdapter.AfterStart;
5+
import com.enot.cmd.core.LambdaListenerAdapter.AfterStop;
6+
import com.enot.cmd.core.LambdaListenerAdapter.BeforeStart;
7+
38
public interface CmdListening {
4-
CmdListening beforeStart(LambdaListenerAdapter.BeforeStart... lambdas);
9+
CmdListening beforeStart(BeforeStart... lambdas);
510

6-
CmdListening afterStart(LambdaListenerAdapter.AfterStart... lambdas);
11+
CmdListening afterStart(AfterStart... lambdas);
712

8-
CmdListening afterFinish(LambdaListenerAdapter.AfterFinish... lambdas);
13+
CmdListening afterFinish(AfterFinish... lambdas);
914

10-
CmdListening afterStop(LambdaListenerAdapter.AfterStop... lambdas);
15+
CmdListening afterStop(AfterStop... lambdas);
1116

1217
Cmd back();
1318
}

src/main/java/com/enot/cmd/core/ICmd.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package com.enot.cmd.core;
22

3-
public interface ICmd {
4-
ICmd cleanUp(boolean cleanUp);
5-
6-
ICmd outputFileName(String outputFileName);
3+
import com.enot.cmd.core.LambdaListenerAdapter.BeforeStart;
74

8-
ICmd configuring(LambdaListenerAdapter.BeforeStart configuring);
5+
public interface ICmd {
6+
ICmd configuring(BeforeStart... configuring);
97

108
CmdListening listening();
119

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.enot.cmd.listeners;
2+
3+
import com.enot.cmd.core.LambdaListenerAdapter;
4+
import com.enot.cmd.core.LambdaListenerAdapter.AfterStop;
5+
import com.enot.cmd.core.LambdaListenerAdapter.BeforeStart;
6+
import org.apache.commons.io.FileUtils;
7+
import org.zeroturnaround.exec.ProcessExecutor;
8+
9+
import java.io.File;
10+
import java.io.IOException;
11+
import java.io.UncheckedIOException;
12+
13+
/**
14+
* Deletes work directory after process stopped
15+
*/
16+
public final class CleanUp implements BeforeStart, AfterStop {
17+
private File dir;
18+
19+
@Override
20+
public void run(ProcessExecutor processExecutor) {
21+
dir = processExecutor.getDirectory();
22+
processExecutor.addListener(new LambdaListenerAdapter((AfterStop) this));
23+
}
24+
25+
@Override
26+
public void run(Process process) {
27+
try {
28+
FileUtils.deleteDirectory(dir);
29+
} catch (IOException e) {
30+
throw new UncheckedIOException("Work directory " + dir.toPath() + " can not be deleted", e);
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)