Moved around OSAL and implemented /proc/net/arp parser
This commit is contained in:
parent
ab8239a809
commit
1e5e114300
13 changed files with 1160 additions and 941 deletions
5
src/zutil/osal/OSAbstractionLayer.java
Normal file → Executable file
5
src/zutil/osal/OSAbstractionLayer.java
Normal file → Executable file
|
|
@ -24,9 +24,12 @@
|
||||||
|
|
||||||
package zutil.osal;
|
package zutil.osal;
|
||||||
|
|
||||||
|
import zutil.osal.linux.OsalLinuxImpl;
|
||||||
|
import zutil.osal.macos.OsalMacOSImpl;
|
||||||
|
import zutil.osal.windows.OsalWindowsImpl;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
|
|
||||||
6
src/zutil/osal/HalLinuxImpl.java → src/zutil/osal/linux/HalLinuxImpl.java
Normal file → Executable file
6
src/zutil/osal/HalLinuxImpl.java → src/zutil/osal/linux/HalLinuxImpl.java
Normal file → Executable file
|
|
@ -22,12 +22,14 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package zutil.osal;
|
package zutil.osal.linux;
|
||||||
|
|
||||||
|
import zutil.osal.HardwareAbstractionLayer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ziver on 2015-04-07.
|
* Created by Ziver on 2015-04-07.
|
||||||
*/
|
*/
|
||||||
public class HalLinuxImpl implements HardwareAbstractionLayer{
|
public class HalLinuxImpl implements HardwareAbstractionLayer {
|
||||||
|
|
||||||
protected HalLinuxImpl(){}
|
protected HalLinuxImpl(){}
|
||||||
}
|
}
|
||||||
5
src/zutil/osal/OsalLinuxImpl.java → src/zutil/osal/linux/OsalLinuxImpl.java
Normal file → Executable file
5
src/zutil/osal/OsalLinuxImpl.java → src/zutil/osal/linux/OsalLinuxImpl.java
Normal file → Executable file
|
|
@ -22,7 +22,10 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package zutil.osal;
|
package zutil.osal.linux;
|
||||||
|
|
||||||
|
import zutil.osal.HardwareAbstractionLayer;
|
||||||
|
import zutil.osal.OSAbstractionLayer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
2
src/zutil/osal/app/linux/AptGet.java → src/zutil/osal/linux/app/AptGet.java
Normal file → Executable file
2
src/zutil/osal/app/linux/AptGet.java → src/zutil/osal/linux/app/AptGet.java
Normal file → Executable file
|
|
@ -22,7 +22,7 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package zutil.osal.app.linux;
|
package zutil.osal.linux.app;
|
||||||
|
|
||||||
import zutil.Timer;
|
import zutil.Timer;
|
||||||
import zutil.log.LogUtil;
|
import zutil.log.LogUtil;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package zutil.osal.app.linux;
|
package zutil.osal.linux.app;
|
||||||
|
|
||||||
import zutil.Timer;
|
import zutil.Timer;
|
||||||
import zutil.log.LogUtil;
|
import zutil.log.LogUtil;
|
||||||
25
src/zutil/osal/app/linux/ProcDiskstats.java → src/zutil/osal/linux/app/ProcDiskstats.java
Normal file → Executable file
25
src/zutil/osal/app/linux/ProcDiskstats.java → src/zutil/osal/linux/app/ProcDiskstats.java
Normal file → Executable file
|
|
@ -22,9 +22,10 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package zutil.osal.app.linux;
|
package zutil.osal.linux.app;
|
||||||
|
|
||||||
import zutil.StringUtil;
|
import zutil.StringUtil;
|
||||||
|
import zutil.Timer;
|
||||||
import zutil.log.LogUtil;
|
import zutil.log.LogUtil;
|
||||||
import zutil.net.ThroughputCalculator;
|
import zutil.net.ThroughputCalculator;
|
||||||
|
|
||||||
|
|
@ -38,7 +39,7 @@ import java.util.logging.Logger;
|
||||||
/**
|
/**
|
||||||
* Documentation from https://www.kernel.org/doc/Documentation/block/stat.txt
|
* Documentation from https://www.kernel.org/doc/Documentation/block/stat.txt
|
||||||
*
|
*
|
||||||
* Created by ezivkoc on 2015-05-19.
|
* Created by Ziver on 2015-05-19.
|
||||||
*/
|
*/
|
||||||
public class ProcDiskstats {
|
public class ProcDiskstats {
|
||||||
private static final Logger log = LogUtil.getLogger();
|
private static final Logger log = LogUtil.getLogger();
|
||||||
|
|
@ -46,15 +47,23 @@ public class ProcDiskstats {
|
||||||
private static final int TTL = 500; // update stats every 0.5 second
|
private static final int TTL = 500; // update stats every 0.5 second
|
||||||
|
|
||||||
private static HashMap<String, HddStats> hdds = new HashMap<String, HddStats>();
|
private static HashMap<String, HddStats> hdds = new HashMap<String, HddStats>();
|
||||||
private static long updateTimestamp;
|
private static Timer updateTimer = new Timer(TTL);
|
||||||
|
|
||||||
|
|
||||||
private synchronized static void update(){
|
private synchronized static void update(){
|
||||||
if(System.currentTimeMillis() - updateTimestamp < TTL)
|
if(!updateTimer.hasTimedOut())
|
||||||
return;
|
return;
|
||||||
updateTimestamp = System.currentTimeMillis();
|
|
||||||
try {
|
try {
|
||||||
BufferedReader in = new BufferedReader(new FileReader(PROC_PATH));
|
BufferedReader in = new BufferedReader(new FileReader(PROC_PATH));
|
||||||
|
parse(in);
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.log(Level.SEVERE, null, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected static void parse(BufferedReader in) throws IOException {
|
||||||
|
updateTimer.start();
|
||||||
String line = null;
|
String line = null;
|
||||||
while((line=in.readLine()) != null){
|
while((line=in.readLine()) != null){
|
||||||
String[] str = line.trim().split("\\s+", 4);
|
String[] str = line.trim().split("\\s+", 4);
|
||||||
|
|
@ -67,12 +76,10 @@ public class ProcDiskstats {
|
||||||
hdds.get(devName).update(str[3]);
|
hdds.get(devName).update(str[3]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.log(Level.SEVERE, null, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static HddStats getStats(String devName){
|
public static HddStats getStats(String devName){
|
||||||
update();
|
update();
|
||||||
return hdds.get(devName);
|
return hdds.get(devName);
|
||||||
135
src/zutil/osal/linux/app/ProcNetArp.java
Executable file
135
src/zutil/osal/linux/app/ProcNetArp.java
Executable file
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Ziver Koc
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package zutil.osal.linux.app;
|
||||||
|
|
||||||
|
import zutil.Timer;
|
||||||
|
import zutil.converter.Converter;
|
||||||
|
import zutil.log.LogUtil;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the Linux ARP cache available as Java api
|
||||||
|
* <p>
|
||||||
|
* Created by Ziver on 2015-05-19.
|
||||||
|
*/
|
||||||
|
public class ProcNetArp {
|
||||||
|
private static final Logger logger = LogUtil.getLogger();
|
||||||
|
private static final String PROC_PATH = "/proc/net/arp";
|
||||||
|
private static final int TTL = 500; // update stats every 0.5 second
|
||||||
|
|
||||||
|
private static HashMap<InetAddress, ArpEntry> arpTable = new HashMap<>();
|
||||||
|
private static Timer updateTimer = new Timer(TTL);
|
||||||
|
|
||||||
|
|
||||||
|
private synchronized static void update() {
|
||||||
|
if (!updateTimer.hasTimedOut())
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
BufferedReader in = new BufferedReader(new FileReader(PROC_PATH));
|
||||||
|
parse(in);
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.log(Level.SEVERE, null, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected static void parse(BufferedReader in) throws IOException {
|
||||||
|
updateTimer.start();
|
||||||
|
String line = null;
|
||||||
|
in.readLine(); // Skipp headers
|
||||||
|
while ((line = in.readLine()) != null) {
|
||||||
|
String[] str = line.split("\\s+");
|
||||||
|
|
||||||
|
if (str.length >= 6) {
|
||||||
|
InetAddress ip = InetAddress.getByName(str[0]);
|
||||||
|
if (!arpTable.containsKey(ip))
|
||||||
|
arpTable.put(ip, new ArpEntry());
|
||||||
|
ArpEntry entry = arpTable.get(ip);
|
||||||
|
entry.ip = ip;
|
||||||
|
entry.hwAddress = Converter.hexToByte(str[3].replaceAll(":", ""));
|
||||||
|
entry.netIf = str[5];
|
||||||
|
} else
|
||||||
|
logger.warning(PROC_PATH + " contains unrecognized format");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Collection<ArpEntry> getAllArpEntries() {
|
||||||
|
update();
|
||||||
|
return arpTable.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArpEntry getArpEntry(InetAddress ip) {
|
||||||
|
update();
|
||||||
|
return arpTable.get(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class ArpEntry {
|
||||||
|
private InetAddress ip;
|
||||||
|
private byte[] hwAddress;
|
||||||
|
private String netIf;
|
||||||
|
|
||||||
|
|
||||||
|
public InetAddress getIp() {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getHwAddress() {
|
||||||
|
return hwAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHwAddressString() {
|
||||||
|
StringBuilder str = new StringBuilder();
|
||||||
|
for (int i = 0; i < hwAddress.length; i++) {
|
||||||
|
if (i != 0)
|
||||||
|
str.append(':');
|
||||||
|
str.append(Converter.toHexString(hwAddress[i]));
|
||||||
|
}
|
||||||
|
return str.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNetworkInterface() {
|
||||||
|
return netIf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
for (ArpEntry entry : getAllArpEntries()) {
|
||||||
|
System.out.println(entry.getIp().getHostAddress() + " => " + entry.getHwAddressString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package zutil.osal.app.linux;
|
package zutil.osal.linux.app;
|
||||||
|
|
||||||
import zutil.Timer;
|
import zutil.Timer;
|
||||||
import zutil.log.LogUtil;
|
import zutil.log.LogUtil;
|
||||||
|
|
@ -38,7 +38,7 @@ import java.util.logging.Logger;
|
||||||
/**
|
/**
|
||||||
* Documentation from https://www.kernel.org/doc/Documentation/block/stat.txt
|
* Documentation from https://www.kernel.org/doc/Documentation/block/stat.txt
|
||||||
*
|
*
|
||||||
* Created by ezivkoc on 2015-05-19.
|
* Created by Ziver on 2015-05-19.
|
||||||
*/
|
*/
|
||||||
public class ProcStat {
|
public class ProcStat {
|
||||||
private static final Logger log = LogUtil.getLogger();
|
private static final Logger log = LogUtil.getLogger();
|
||||||
|
|
@ -53,11 +53,19 @@ public class ProcStat {
|
||||||
|
|
||||||
|
|
||||||
private synchronized static void update(){
|
private synchronized static void update(){
|
||||||
if(updateTimer.hasTimedOut())
|
if(!updateTimer.hasTimedOut())
|
||||||
return;
|
return;
|
||||||
updateTimer.start();
|
|
||||||
try {
|
try {
|
||||||
BufferedReader in = new BufferedReader(new FileReader(PROC_PATH));
|
BufferedReader in = new BufferedReader(new FileReader(PROC_PATH));
|
||||||
|
parse(in);
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.log(Level.SEVERE, null, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected static void parse(BufferedReader in) throws IOException {
|
||||||
|
updateTimer.start();
|
||||||
String line = null;
|
String line = null;
|
||||||
while((line=in.readLine()) != null){
|
while((line=in.readLine()) != null){
|
||||||
String[] str = line.split("\\s+");
|
String[] str = line.split("\\s+");
|
||||||
|
|
@ -77,12 +85,10 @@ public class ProcStat {
|
||||||
processes = Long.parseLong(str[1]);
|
processes = Long.parseLong(str[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.log(Level.SEVERE, null, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static CpuStats getTotalCpuStats(){
|
public static CpuStats getTotalCpuStats(){
|
||||||
update();
|
update();
|
||||||
return cpuTotal;
|
return cpuTotal;
|
||||||
2
src/zutil/osal/app/linux/Ps.java → src/zutil/osal/linux/app/Ps.java
Normal file → Executable file
2
src/zutil/osal/app/linux/Ps.java → src/zutil/osal/linux/app/Ps.java
Normal file → Executable file
|
|
@ -22,7 +22,7 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package zutil.osal.app.linux;
|
package zutil.osal.linux.app;
|
||||||
|
|
||||||
import zutil.osal.OSAbstractionLayer;
|
import zutil.osal.OSAbstractionLayer;
|
||||||
|
|
||||||
4
src/zutil/osal/OsalMacOSImpl.java → src/zutil/osal/macos/OsalMacOSImpl.java
Normal file → Executable file
4
src/zutil/osal/OsalMacOSImpl.java → src/zutil/osal/macos/OsalMacOSImpl.java
Normal file → Executable file
|
|
@ -22,9 +22,11 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package zutil.osal;
|
package zutil.osal.macos;
|
||||||
|
|
||||||
import com.mysql.jdbc.NotImplemented;
|
import com.mysql.jdbc.NotImplemented;
|
||||||
|
import zutil.osal.HardwareAbstractionLayer;
|
||||||
|
import zutil.osal.OSAbstractionLayer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
5
src/zutil/osal/OsalWindowsImpl.java → src/zutil/osal/windows/OsalWindowsImpl.java
Normal file → Executable file
5
src/zutil/osal/OsalWindowsImpl.java → src/zutil/osal/windows/OsalWindowsImpl.java
Normal file → Executable file
|
|
@ -22,7 +22,10 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package zutil.osal;
|
package zutil.osal.windows;
|
||||||
|
|
||||||
|
import zutil.osal.HardwareAbstractionLayer;
|
||||||
|
import zutil.osal.OSAbstractionLayer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package zutil.osal.app.windows;
|
package zutil.osal.windows.app;
|
||||||
|
|
||||||
import zutil.osal.OSAbstractionLayer;
|
import zutil.osal.OSAbstractionLayer;
|
||||||
import zutil.parser.CSVParser;
|
import zutil.parser.CSVParser;
|
||||||
58
test/zutil/osal/linux/app/ProcNetArpTest.java
Executable file
58
test/zutil/osal/linux/app/ProcNetArpTest.java
Executable file
|
|
@ -0,0 +1,58 @@
|
||||||
|
package zutil.osal.linux.app;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import zutil.io.StringInputStream;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ezivkoc on 2016-09-30.
|
||||||
|
*/
|
||||||
|
public class ProcNetArpTest {
|
||||||
|
@Test
|
||||||
|
public void parse() throws Exception {
|
||||||
|
StringInputStream buff = new StringInputStream(
|
||||||
|
"IP address HW type Flags HW address Mask Device\n" +
|
||||||
|
"192.168.1.140 0x1 0x0 fc:f8:ae:3f:66:64 * eth0\n" +
|
||||||
|
"169.254.67.249 0x1 0x0 78:ab:bb:c4:05:b3 * eth0\n" +
|
||||||
|
"192.168.1.127 0x1 0x2 00:05:cd:39:aa:07 * eth0\n" +
|
||||||
|
"192.168.1.1 0x1 0x2 c0:56:27:c2:5e:82 * eth0\n" +
|
||||||
|
"192.168.1.190 0x1 0x0 fc:f8:ae:3f:66:64 * eth0\n" +
|
||||||
|
"192.168.1.253 0x1 0x0 00:00:00:00:00:00 * eth0\n" +
|
||||||
|
"192.168.1.105 0x1 0x2 24:0a:64:49:c9:7b * eth0\n" +
|
||||||
|
"192.168.1.110 0x1 0x0 00:00:00:00:00:00 * eth0\n" +
|
||||||
|
"192.168.1.2 0x1 0x0 00:00:00:00:00:00 * eth0\n" +
|
||||||
|
"192.168.1.137 0x1 0x2 78:ab:bb:c4:05:b3 * eth0\n" +
|
||||||
|
"192.168.1.128 0x1 0x2 6c:ad:f8:be:ef:ba * eth0\n" +
|
||||||
|
"192.168.1.30 0x1 0x0 00:00:00:00:00:00 * eth0\n" +
|
||||||
|
"192.168.1.12 0x1 0x0 00:00:00:00:00:00 * eth0\n" +
|
||||||
|
"192.168.1.178 0x1 0x2 6c:ad:f8:be:ef:ba * eth0\n");
|
||||||
|
ProcNetArp.parse(new BufferedReader(new InputStreamReader(buff)));
|
||||||
|
|
||||||
|
ProcNetArp.ArpEntry entry = ProcNetArp.getArpEntry(InetAddress.getByName("192.168.1.140"));
|
||||||
|
assertEquals("192.168.1.140", entry.getIp().getHostAddress());
|
||||||
|
assertEquals("fc:f8:ae:3f:66:64", entry.getHwAddressString());
|
||||||
|
assertEquals("eth0", entry.getNetworkInterface());
|
||||||
|
|
||||||
|
entry = ProcNetArp.getArpEntry(InetAddress.getByName("192.168.1.1"));
|
||||||
|
assertEquals("192.168.1.1", entry.getIp().getHostAddress());
|
||||||
|
assertEquals("c0:56:27:c2:5e:82", entry.getHwAddressString());
|
||||||
|
assertEquals("eth0", entry.getNetworkInterface());
|
||||||
|
|
||||||
|
entry = ProcNetArp.getArpEntry(InetAddress.getByName("192.168.1.178"));
|
||||||
|
assertEquals("192.168.1.178", entry.getIp().getHostAddress());
|
||||||
|
assertEquals("6c:ad:f8:be:ef:ba", entry.getHwAddressString());
|
||||||
|
assertEquals("eth0", entry.getNetworkInterface());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseTwoTimes() throws Exception {
|
||||||
|
parse();
|
||||||
|
parse();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue