Skip to content

Commit c69af8c

Browse files
Merge pull request #425 from zowe/examples
Refactor IssueUss class
2 parents 148c561 + b3d10d2 commit c69af8c

File tree

10 files changed

+222
-47
lines changed

10 files changed

+222
-47
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ zowe.client.sdk.zostso.methods
9292

9393
zowe.client.sdk.zosuss.method
9494

95-
IssueUss
95+
UssCmd
9696
9797
## TeamConfig Package
9898

src/main/java/zowe/client/sdk/zostso/methods/TsoCmd.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ private void stopTso(final String sessionId) throws ZosmfRequestException {
209209
}
210210

211211
/**
212-
* Transform the JSON response payload for its tso message types
212+
* Transform the JSON response payload for its TSO message types
213213
*
214214
* @param tsoData JsonNode object
215215
* @author Frank Giordano

src/main/java/zowe/client/sdk/zosuss/README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,33 @@ API located in the method package.
1010
package zowe.client.sdk.examples.zosuss;
1111

1212
import zowe.client.sdk.core.SshConnection;
13-
import zowe.client.sdk.zosuss.exception.IssueUssException;
14-
import zowe.client.sdk.zosuss.method.IssueUss;
13+
import zowe.client.sdk.zosuss.exception.UssCmdException;
14+
import zowe.client.sdk.zosuss.method.UssCmd;
15+
import zowe.client.sdk.zosuss.method.UssSshCmd;
1516

1617
/**
17-
* Class example to test uss command functionality via IssueUss class.
18+
* Class example to test USS command functionality via UssCmd class.
1819
*
1920
* @author Frank Giordano
2021
* @version 5.0
2122
*/
22-
public class IssueUssExp {
23+
public class UssCmdExp {
2324

2425
/**
25-
* The main method defines SSH connection and showcases executing a USS command via IssueUss class.
26+
* The main method defines ssh connection and showcases executing a USS command via UssCmd class.
2627
*
2728
* @param args for main not used
2829
* @author Frank Giordano
2930
*/
3031
public static void main(String[] args) {
3132
int portNum = 0; // replace with valid value
3233
SshConnection conn = new SshConnection("xxx", portNum, "xxx", "xxx");
33-
IssueUss issueUss = new IssueUss(conn);
34+
UssCmd ussCmd = new UssCmd(conn);
3435
// 10000 is the timeout value in milliseconds
3536
try {
3637
// value "frank" should display
37-
System.out.println(issueUss.issueCommand("mkdir test;cd test;touch frank;ls", 10000));
38-
} catch (IssueUssException e) {
38+
System.out.println(ussCmd.issueCommand("mkdir test;cd test;touch frank;ls", 10000));
39+
} catch (UssCmdException e) {
3940
throw new RuntimeException(e);
4041
}
4142
}

src/main/java/zowe/client/sdk/zosuss/exception/IssueUssException.java renamed to src/main/java/zowe/client/sdk/zosuss/exception/UssCmdException.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@
1515
* @author Frank Giordano
1616
* @version 5.0
1717
*/
18-
public class IssueUssException extends Exception {
18+
public class UssCmdException extends Exception {
1919

2020
/**
21-
* IssueCommandException constructor for message value
21+
* UssCmdException constructor for message value
2222
*
2323
* @param message error message
2424
* @param err original throwable exception
2525
* @author Frank Giordano
2626
*/
27-
public IssueUssException(final String message, Throwable err) {
27+
public UssCmdException(final String message, Throwable err) {
2828
super(message, err);
2929
}
3030

src/main/java/zowe/client/sdk/zosuss/method/IssueUss.java renamed to src/main/java/zowe/client/sdk/zosuss/method/UssCmd.java

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,32 @@
1313
import com.jcraft.jsch.JSch;
1414
import com.jcraft.jsch.JSchException;
1515
import com.jcraft.jsch.Session;
16-
import org.slf4j.Logger;
17-
import org.slf4j.LoggerFactory;
1816
import zowe.client.sdk.core.SshConnection;
1917
import zowe.client.sdk.utility.ValidateUtils;
2018
import zowe.client.sdk.utility.timer.WaitUtil;
21-
import zowe.client.sdk.zosuss.exception.IssueUssException;
19+
import zowe.client.sdk.zosuss.exception.UssCmdException;
2220

2321
import java.io.ByteArrayOutputStream;
2422
import java.io.IOException;
23+
import java.io.OutputStream;
2524
import java.util.Properties;
2625

2726
/**
28-
* IssueUss Class provides a way to execute USS commands via SSH connection
27+
* UssCmd Class provides a way to execute USS commands via ssh connection
2928
*
3029
* @author Frank Giordano
3130
* @version 5.0
3231
*/
33-
public class IssueUss {
34-
35-
private static final Logger LOG = LoggerFactory.getLogger(IssueUss.class);
32+
public class UssCmd {
3633

3734
private final SshConnection connection;
3835

3936
/**
40-
* Shell constructor
37+
* UssCmd constructor
4138
*
4239
* @param connection SshConnection object
4340
*/
44-
public IssueUss(final SshConnection connection) {
41+
public UssCmd(final SshConnection connection) {
4542
ValidateUtils.checkSshConnection(connection);
4643
this.connection = connection;
4744
}
@@ -52,43 +49,73 @@ public IssueUss(final SshConnection connection) {
5249
* @param command string value contains one or more USS commands
5350
* @param timeout int value in milliseconds for timeout duration on session connection
5451
* @return string output value
55-
* @throws IssueUssException SSH Unix System Services error request
52+
* @throws UssCmdException ssh Unix System Services error request
5653
* @author Frank Giordano
5754
*/
58-
public String issueCommand(final String command, final int timeout) throws IssueUssException {
59-
Session session = null;
60-
ChannelExec channel = null;
55+
public String issueCommand(final String command, final int timeout) throws UssCmdException {
56+
try (final ByteArrayOutputStream responseStream = new ByteArrayOutputStream();
57+
final ManagedSession session = new ManagedSession(connection, timeout);
58+
final ManagedChannel channel = new ManagedChannel(session.get(), command, responseStream)) {
59+
60+
// Wait for channel execution to complete
61+
while (channel.get().isConnected()) {
62+
WaitUtil.wait(1000);
63+
}
64+
65+
return responseStream.toString();
66+
} catch (IOException | JSchException e) {
67+
throw new UssCmdException(e.getMessage(), e);
68+
}
69+
}
6170

62-
try (final ByteArrayOutputStream responseStream = new ByteArrayOutputStream()) {
63-
session = new JSch().getSession(connection.getUser(), connection.getHost(), connection.getPort());
71+
/**
72+
* AutoCloseable wrapper for JSch Session
73+
*/
74+
static class ManagedSession implements AutoCloseable {
75+
private final Session session;
76+
77+
ManagedSession(SshConnection connection, int timeout) throws JSchException {
78+
this.session = new JSch().getSession(connection.getUser(), connection.getHost(), connection.getPort());
6479
session.setPassword(connection.getPassword());
6580
final Properties config = new Properties();
6681
config.put("StrictHostKeyChecking", "no");
6782
config.put("PreferredAuthentications", "password");
6883
session.setConfig(config);
6984
session.connect(timeout);
85+
}
7086

71-
channel = (ChannelExec) session.openChannel("exec");
87+
Session get() {
88+
return session;
89+
}
90+
91+
@Override
92+
public void close() {
93+
if (session != null && session.isConnected()) {
94+
session.disconnect();
95+
}
96+
}
97+
}
98+
99+
/**
100+
* AutoCloseable wrapper for JSch ChannelExec
101+
*/
102+
static class ManagedChannel implements AutoCloseable {
103+
private final ChannelExec channel;
104+
105+
ManagedChannel(Session session, String command, OutputStream responseStream) throws JSchException {
106+
this.channel = (ChannelExec) session.openChannel("exec");
72107
channel.setCommand(command);
73108
channel.setOutputStream(responseStream);
74109
channel.connect();
110+
}
75111

76-
while (channel.isConnected()) {
77-
WaitUtil.wait(1000);
78-
}
112+
ChannelExec get() {
113+
return channel;
114+
}
79115

80-
return responseStream.toString();
81-
} catch (IOException e) {
82-
LOG.debug("IOException error {}", String.valueOf(e));
83-
throw new IssueUssException(e.getMessage(), e);
84-
} catch (JSchException e) {
85-
LOG.debug("JSchException error {}", String.valueOf(e));
86-
throw new IssueUssException(e.getMessage(), e);
87-
} finally {
88-
if (session != null) {
89-
session.disconnect();
90-
}
91-
if (channel != null) {
116+
@Override
117+
public void close() {
118+
if (channel != null && channel.isConnected()) {
92119
channel.disconnect();
93120
}
94121
}

src/main/javadoc/serialized-form.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ <h2 title="Package">Package&nbsp;<a href="zowe/client/sdk/zosuss/exception/packa
9797
<ul class="block-list">
9898
<li>
9999
<section class="serialized-class-details" id="zowe.client.sdk.zosuss.exception.IssueUssException">
100-
<h3>Exception&nbsp;<a href="zowe/client/sdk/zosuss/exception/IssueUssException.html" title="class in zowe.client.sdk.zosuss.exception">zowe.client.sdk.zosuss.exception.IssueUssException</a></h3>
100+
<h3>Exception&nbsp;<a href="zowe/client/sdk/zosuss/exception/IssueUssException.html" title="class in zowe.client.sdk.zosuss.exception">zowe.client.sdk.zosuss.exception.UssCmdException</a></h3>
101101
<div class="type-signature">class IssueUssException extends <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a> implements <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/Serializable.html" title="class or interface in java.io" class="external-link">Serializable</a></div>
102102
</section>
103103
</li>

src/main/javadoc/zowe/client/sdk/zosuss/exception/IssueUssException.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ <h1 title="Class IssueUssException" class="title">Class IssueUssException</h1>
7070
<div class="inheritance" title="Inheritance Tree"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Object.html" title="class or interface in java.lang" class="external-link">java.lang.Object</a>
7171
<div class="inheritance"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Throwable.html" title="class or interface in java.lang" class="external-link">java.lang.Throwable</a>
7272
<div class="inheritance"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">java.lang.Exception</a>
73-
<div class="inheritance">zowe.client.sdk.zosuss.exception.IssueUssException</div>
73+
<div class="inheritance">zowe.client.sdk.zosuss.exception.UssCmdException</div>
7474
</div>
7575
</div>
7676
</div>

src/main/javadoc/zowe/client/sdk/zosuss/method/IssueUss.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
<h1 title="Class IssueUss" class="title">Class IssueUss</h1>
7373
</div>
7474
<div class="inheritance" title="Inheritance Tree"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Object.html" title="class or interface in java.lang" class="external-link">java.lang.Object</a>
75-
<div class="inheritance">zowe.client.sdk.zosuss.method.IssueUss</div>
75+
<div class="inheritance">zowe.client.sdk.zosuss.method.UssCmd</div>
7676
</div>
7777
<section class="class-description" id="class-description">
7878
<hr>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* This program and the accompanying materials are made available under the terms of the
3+
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
4+
* https://www.eclipse.org/legal/epl-v20.html
5+
*
6+
* SPDX-License-Identifier: EPL-2.0
7+
*
8+
* Copyright Contributors to the Zowe Project.
9+
*/
10+
package zowe.client.sdk.zosuss;
11+
12+
import org.junit.jupiter.api.Test;
13+
import zowe.client.sdk.zosuss.exception.UssCmdException;
14+
15+
import static org.junit.jupiter.api.Assertions.*;
16+
17+
/**
18+
* Class containing unit tests for UssCmdException.
19+
*
20+
* @author Frank Giordano
21+
* @version 5.0
22+
*/
23+
public class UssCmdExceptionTest {
24+
25+
@Test
26+
public void tstMessageIsStoredCorrectly() {
27+
UssCmdException ex = new UssCmdException("Test error", null);
28+
assertEquals("Test error", ex.getMessage());
29+
}
30+
31+
@Test
32+
public void tstCauseIsStoredCorrectly() {
33+
Throwable cause = new RuntimeException("Root cause");
34+
UssCmdException ex = new UssCmdException("Wrapper message", cause);
35+
36+
assertEquals("Wrapper message", ex.getMessage());
37+
assertSame(cause, ex.getCause());
38+
}
39+
40+
@Test
41+
public void tstToStringContainsClassNameAndMessage() {
42+
UssCmdException ex = new UssCmdException("Something went wrong", null);
43+
String toStringValue = ex.toString();
44+
45+
assertTrue(toStringValue.contains("UssCmdException"));
46+
assertTrue(toStringValue.contains("Something went wrong"));
47+
}
48+
49+
@Test
50+
public void tstExceptionWithNullMessage() {
51+
Throwable cause = new IllegalArgumentException("Bad arg");
52+
UssCmdException ex = new UssCmdException(null, cause);
53+
54+
assertNull(ex.getMessage());
55+
assertSame(cause, ex.getCause());
56+
}
57+
58+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* This program and the accompanying materials are made available under the terms of the
3+
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
4+
* https://www.eclipse.org/legal/epl-v20.html
5+
*
6+
* SPDX-License-Identifier: EPL-2.0
7+
*
8+
* Copyright Contributors to the Zowe Project.
9+
*/
10+
package zowe.client.sdk.zosuss.method;
11+
12+
import com.jcraft.jsch.ChannelExec;
13+
import com.jcraft.jsch.JSchException;
14+
import com.jcraft.jsch.Session;
15+
import org.junit.jupiter.api.BeforeEach;
16+
import org.junit.jupiter.api.Test;
17+
import org.mockito.MockedConstruction;
18+
import org.mockito.Mockito;
19+
import zowe.client.sdk.core.SshConnection;
20+
import zowe.client.sdk.zosuss.exception.UssCmdException;
21+
22+
import java.io.OutputStream;
23+
24+
import static org.junit.jupiter.api.Assertions.assertEquals;
25+
import static org.junit.jupiter.api.Assertions.assertThrows;
26+
import static org.mockito.Mockito.mock;
27+
import static org.mockito.Mockito.when;
28+
29+
/**
30+
* Class containing unit tests for UssCmd.
31+
*
32+
* @author Frank Giordano
33+
* @version 5.0
34+
*/
35+
public class UssCmdTest {
36+
37+
private SshConnection mockConnection;
38+
39+
@BeforeEach
40+
public void setup() {
41+
mockConnection = mock(SshConnection.class);
42+
when(mockConnection.getUser()).thenReturn("testuser");
43+
when(mockConnection.getHost()).thenReturn("localhost");
44+
when(mockConnection.getPort()).thenReturn(22);
45+
when(mockConnection.getPassword()).thenReturn("testpass");
46+
}
47+
48+
@Test
49+
public void tstIssueCommandReturnsOutputSuccess() throws Exception {
50+
// Mock JSch Session and ChannelExec
51+
Session mockSession = mock(Session.class);
52+
ChannelExec mockChannel = mock(ChannelExec.class);
53+
54+
// Simulate channel disconnect after one loop
55+
when(mockChannel.isConnected()).thenReturn(true, false);
56+
when(mockSession.openChannel("exec")).thenReturn(mockChannel);
57+
58+
try (MockedConstruction<UssCmd.ManagedSession> ignored1 = Mockito.mockConstruction(UssCmd.ManagedSession.class,
59+
(mock, context) -> when(mock.get()).thenReturn(mockSession));
60+
MockedConstruction<UssCmd.ManagedChannel> ignored2 = Mockito.mockConstruction(UssCmd.ManagedChannel.class,
61+
(mock, context) -> {
62+
// Grab the real OutputStream passed in constructor
63+
OutputStream os = (OutputStream) context.arguments().get(2);
64+
os.write("mock output".getBytes());
65+
when(mock.get()).thenReturn(mockChannel);
66+
})) {
67+
68+
UssCmd cmd = new UssCmd(mockConnection);
69+
String result = cmd.issueCommand("echo test", 1000);
70+
71+
assertEquals("mock output", result);
72+
}
73+
}
74+
75+
@Test
76+
public void tstIssueCommandThrowsExceptionOnJSchError() throws Exception {
77+
Session mockSession = mock(Session.class);
78+
when(mockSession.openChannel("exec")).thenThrow(new JSchException("SSH error"));
79+
80+
try (MockedConstruction<UssCmd.ManagedSession> ignored1 = Mockito.mockConstruction(UssCmd.ManagedSession.class,
81+
(mock, context) -> when(mock.get()).thenReturn(mockSession))) {
82+
83+
UssCmd cmd = new UssCmd(mockConnection);
84+
85+
assertThrows(UssCmdException.class, () -> cmd.issueCommand("bad cmd", 1000));
86+
}
87+
}
88+
89+
}

0 commit comments

Comments
 (0)