Skip to content

Commit c7247eb

Browse files
author
Peter Hill
committed
Added more tests
1 parent c621a87 commit c7247eb

9 files changed

Lines changed: 410 additions & 108 deletions

File tree

src/main/java/net/juniper/netconf/Device.java

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -612,9 +612,9 @@ public BufferedReader runShellCommandRunning(String command)
612612
}
613613

614614
/**
615-
* Send an RPC(as String object) over the default Netconf session and get
616-
* the response as an XML object.
617-
* <p>
615+
* Send an RPC(as String object) over the default Netconf session and get the
616+
* response as an XML object.
617+
* <p>Convenience overload for raw‑string payloads.</p>
618618
*
619619
* @param rpcContent RPC content to be sent. For example, to send an rpc
620620
* &lt;rpc&gt;&lt;get-chassis-inventory/&gt;&lt;/rpc&gt;, the
@@ -637,7 +637,7 @@ public XML executeRPC(String rpcContent) throws SAXException, IOException {
637637
/**
638638
* Send an RPC(as XML object) over the Netconf session and get the response
639639
* as an XML object.
640-
* <p>
640+
* <p>Use when the payload is already assembled as an {@link XML} helper object.</p>
641641
*
642642
* @param rpc RPC to be sent. Use the XMLBuilder to create RPC as an
643643
* XML object.
@@ -656,7 +656,8 @@ public XML executeRPC(XML rpc) throws SAXException, IOException {
656656
/**
657657
* Send an RPC(as Document object) over the Netconf session and get the
658658
* response as an XML object.
659-
* <p>
659+
* <p>Accepts a DOM {@link org.w3c.dom.Document} that represents the full
660+
* &lt;rpc&gt; element.</p>
660661
*
661662
* @param rpcDoc RPC content to be sent, as a org.w3c.dom.Document object.
662663
* @return RPC reply sent by Netconf server
@@ -691,7 +692,7 @@ public BufferedReader executeRPCRunning(String rpcContent) throws IOException {
691692
/**
692693
* Send an RPC(as XML object) over the Netconf session and get the response
693694
* as a BufferedReader.
694-
* <p>
695+
* <p>Streams the reply incrementally, suitable for large responses.</p>
695696
*
696697
* @param rpc RPC to be sent. Use the XMLBuilder to create RPC as an
697698
* XML object.
@@ -709,15 +710,23 @@ public BufferedReader executeRPCRunning(XML rpc) throws IOException {
709710
}
710711

711712
/**
712-
* Send an RPC(as Document object) over the Netconf session and get the
713-
* response as a BufferedReader.
713+
* Sends an RPC (as a DOM {@link Document}) over the active NETCONF session
714+
* and returns a {@link BufferedReader} for streaming the reply.
714715
* <p>
716+
* Use this variant when you need to consume the server response
717+
* <em>incrementally</em>&nbsp;&mdash; for example, when the RPC produces a
718+
* large dataset or when you want to start processing output before the
719+
* device finishes sending the final <code>]]&gt;]]&gt;</code> prompt.
720+
* </p>
715721
*
716-
* @param rpcDoc RPC content to be sent, as a org.w3c.dom.Document object.
717-
* @return RPC reply sent by Netconf server as a BufferedReader. This is
718-
* useful if we want continuous stream of output, rather than wait
719-
* for whole output till command execution completes.
720-
* @throws java.io.IOException If there are errors communicating with the Netconf server.
722+
* @param rpcDoc the complete &lt;rpc&gt; element encoded as a DOM
723+
* {@link Document}; must not be {@code null}
724+
*
725+
* @return a {@link BufferedReader} connected to the server’s reply stream
726+
*
727+
* @throws IOException if an I/O error occurs while sending the
728+
* request or reading the reply
729+
* @throws IllegalStateException if no NETCONF connection is established
721730
*/
722731
public BufferedReader executeRPCRunning(Document rpcDoc) throws IOException {
723732
if (netconfSession == null) {
@@ -1257,9 +1266,15 @@ public boolean isStrictHostKeyChecking() {
12571266
// Getters for fields
12581267

12591268
/**
1260-
* Returns the JSch SSH client used by this device.
1269+
* Returns the {@link JSch} instance that backs this {@code Device}.
12611270
* <p>
1262-
* Defensive copy not possible; caller must not modify the returned instance.
1271+
* <strong>Note&nbsp;–</strong> the returned object is the live instance
1272+
* used for all SSH operations; creating a defensive copy is not feasible,
1273+
* so callers <em>must not</em> modify its global state (e.g.&nbsp;changing
1274+
* the identity repository or host‑key repository) once the {@code Device}
1275+
* has been built.
1276+
*
1277+
* @return the underlying {@link JSch} SSH client
12631278
*/
12641279
public JSch getSshClient() {
12651280
// Defensive copy not possible; document that caller must not modify

src/main/java/net/juniper/netconf/NetconfConstants.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
package net.juniper.netconf;
22

33
/**
4+
* Centralised collection of string literals and protocol constants used
5+
* throughout the NETCONF client library.
6+
* <p>
7+
* The class is {@code final} and has a private constructor – it cannot be
8+
* instantiated or extended. All members are {@code public static final}
9+
* to encourage direct use without additional indirection.
10+
* </p>
11+
*
412
* @author Jonas Glass
513
*/
614
public class NetconfConstants {
@@ -55,6 +63,8 @@ public class NetconfConstants {
5563
/** UTF‑8 charset literal used throughout the library. */
5664
public static final String CHARSET_UTF8 = "utf-8";
5765

58-
private NetconfConstants() {
59-
}
66+
/**
67+
* Not instantiable – utility holder only.
68+
*/
69+
private NetconfConstants() { /* no‑op */ }
6070
}

src/main/java/net/juniper/netconf/NetconfSession.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,7 @@ public void commit() throws IOException, SAXException {
803803
*
804804
* @param seconds the &lt;confirm-timeout&gt; in seconds; if {@code seconds
805805
* &lt;= 0} the device’s default (600 s) is used
806+
* @throws IOException if communication with the device fails
806807
*
807808
* @deprecated Prefer {@link #commitConfirm(long, String)} which adds
808809
* the <code>&lt;persist&gt;</code> / <code>&lt;persist-id&gt;</code>

src/main/java/net/juniper/netconf/XML.java

Lines changed: 89 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -380,47 +380,49 @@ public String findValue(List<String> list) {
380380
for (int k=0; k<list.size(); k++) {
381381
nextElementFound = false;
382382
String nextElementName = list.get(k);
383-
if (!nextElementName.contains("~")){
384-
try {
385-
NodeList nextElementList = nextElement.
386-
getElementsByTagName(nextElementName);
387-
/* If the next to next(n2n) element is a filter based on
388-
* text value, then do the required filtering.
383+
if (!nextElementName.contains("~")) {
384+
NodeList nextElementList =
385+
nextElement != null
386+
? nextElement.getElementsByTagName(nextElementName)
387+
: null;
388+
if (nextElementList == null || nextElementList.getLength() == 0) {
389+
logger.fine("Element '" + nextElementName + "' not found in findValue()");
390+
return null;
391+
}
392+
393+
/* If the next‑to‑next (n2n) element is a filter based on
394+
* text value, then do the required filtering.
395+
*/
396+
String n2nString = null;
397+
if (k<list.size()-1)
398+
n2nString = list.get(k+1);
399+
if (n2nString != null && n2nString.contains("~")) {
400+
/* Since the n2n element is a filter based on text
401+
* value( decided by '~')
402+
* we now traverse the entire NodeList to find the
403+
* correct nextElement
404+
* based on the text value of the filter.
389405
*/
390-
String n2nString = null;
391-
if (k<list.size()-1)
392-
n2nString = list.get(k+1);
393-
if (n2nString != null && n2nString.contains("~")) {
394-
/* Since the n2n element is a filter based on text
395-
* value( decided by '~')
396-
* we now traverse the entire NodeList to find the
397-
* correct nextElement
398-
* based on the text value of the filter.
399-
*/
400-
String n2nText = n2nString.substring(n2nString.
401-
indexOf("~") + 1);
402-
String n2nElementName = n2nString.substring(0,
403-
n2nString.indexOf("~"));
404-
for (int i=0; i<nextElementList.getLength(); i++) {
405-
nextElement = (Element)nextElementList.item(i);
406-
NodeList nodes = nextElement.getElementsByTagName(n2nElementName);
407-
if (nodes.getLength() == 0) continue;
408-
Element n2nElement = (Element) nodes.item(0);
409-
if (n2nElement == null || n2nElement.getFirstChild() == null) continue;
410-
String text = trim(n2nElement.getFirstChild().getNodeValue());
411-
if (text.equals(n2nText)) {
412-
nextElementFound = true;
413-
break;
414-
}
406+
String n2nText = n2nString.substring(n2nString.
407+
indexOf("~") + 1);
408+
String n2nElementName = n2nString.substring(0,
409+
n2nString.indexOf("~"));
410+
for (int i=0; i<nextElementList.getLength(); i++) {
411+
nextElement = (Element)nextElementList.item(i);
412+
NodeList nodes = nextElement.getElementsByTagName(n2nElementName);
413+
if (nodes.getLength() == 0) continue;
414+
Element n2nElement = (Element) nodes.item(0);
415+
if (n2nElement == null || n2nElement.getFirstChild() == null) continue;
416+
String text = trim(n2nElement.getFirstChild().getNodeValue());
417+
if (text.equals(n2nText)) {
418+
nextElementFound = true;
419+
break;
415420
}
416-
if (!nextElementFound)
417-
return null;
418-
} else {
419-
nextElement = (Element)nextElementList.item(0);
420421
}
421-
} catch (NullPointerException e) {
422-
logger.warning("NullPointerException in findValue at element: " + nextElementName);
423-
return null;
422+
if (!nextElementFound)
423+
return null;
424+
} else {
425+
nextElement = (Element)nextElementList.item(0);
424426
}
425427
}
426428
}
@@ -458,57 +460,58 @@ public List<Node> findNodes(List<String> list) {
458460
nextElementFound = false;
459461
String nextElementName = list.get(k);
460462
if (!nextElementName.contains("~")) {
461-
try {
462-
NodeList nextElementList = nextElement.
463-
getElementsByTagName(nextElementName);
464-
/* If the next to next(n2n) element is a filter based on
465-
* text value,
466-
* then do the required filtering.
467-
* For example,
468-
* ....
469-
* <physical-interface>
470-
* <name>ge-1/0/0</name>
471-
* <logical-interface>
472-
* ....
473-
* In this case, the list passed to findValue function
474-
* should contain (..,"physical-interface","name~ge-1/0/0",
475-
* "logical-interface",..)
476-
* This will fetch me the required element.
463+
NodeList nextElementList =
464+
nextElement != null
465+
? nextElement.getElementsByTagName(nextElementName)
466+
: null;
467+
if (nextElementList == null || nextElementList.getLength() == 0) {
468+
logger.fine("Element '" + nextElementName + "' not found in findNodes()");
469+
return null;
470+
}
471+
/* If the next to next(n2n) element is a filter based on
472+
* text value,
473+
* then do the required filtering.
474+
* For example,
475+
* ....
476+
* <physical-interface>
477+
* <name>ge-1/0/0</name>
478+
* <logical-interface>
479+
* ....
480+
* In this case, the list passed to findValue function
481+
* should contain (..,"physical-interface","name~ge-1/0/0",
482+
* "logical-interface",..)
483+
* This will fetch me the required element.
484+
*/
485+
String n2nString = null;
486+
if (k<list.size()-1)
487+
n2nString = list.get(k+1);
488+
if (n2nString != null && n2nString.contains("~")) {
489+
/* Since the n2n element is a filter based on text value
490+
* ( decided by '~')
491+
* we now traverse the entire NodeList to find the
492+
* correct nextElement
493+
* based on the text value of the filter.
477494
*/
478-
String n2nString = null;
479-
if (k<list.size()-1)
480-
n2nString = list.get(k+1);
481-
if (n2nString != null && n2nString.contains("~")) {
482-
/* Since the n2n element is a filter based on text value
483-
* ( decided by '~')
484-
* we now traverse the entire NodeList to find the
485-
* correct nextElement
486-
* based on the text value of the filter.
487-
*/
488-
String n2nText = n2nString.substring(n2nString.
489-
indexOf("~") + 1);
490-
String n2nElementName = n2nString.substring(0,
491-
n2nString.indexOf("~"));
492-
for (int i=0; i<nextElementList.getLength(); i++) {
493-
nextElement = (Element)nextElementList.item(i);
494-
Element n2nElement = (Element)nextElement.
495-
getElementsByTagName(n2nElementName).item(0);
496-
String text = n2nElement.getFirstChild().
497-
getNodeValue();
498-
text = trim(text);
499-
if (text.equals(n2nText)) {
500-
nextElementFound = true;
501-
break;
502-
}
495+
String n2nText = n2nString.substring(n2nString.
496+
indexOf("~") + 1);
497+
String n2nElementName = n2nString.substring(0,
498+
n2nString.indexOf("~"));
499+
for (int i=0; i<nextElementList.getLength(); i++) {
500+
nextElement = (Element)nextElementList.item(i);
501+
Element n2nElement = (Element)nextElement.
502+
getElementsByTagName(n2nElementName).item(0);
503+
String text = n2nElement.getFirstChild().
504+
getNodeValue();
505+
text = trim(text);
506+
if (text.equals(n2nText)) {
507+
nextElementFound = true;
508+
break;
503509
}
504-
if (!nextElementFound)
505-
return null;
506-
} else {
507-
nextElement = (Element)nextElementList.item(0);
508510
}
509-
} catch (NullPointerException e) {
510-
logger.warning("NullPointerException in findNodes at element: " + nextElementName);
511-
return null;
511+
if (!nextElementFound)
512+
return null;
513+
} else {
514+
nextElement = (Element)nextElementList.item(0);
512515
}
513516
}
514517
}

0 commit comments

Comments
 (0)