Compare commits
2 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
4c2f69a609 | |
|
|
fcaec18675 |
|
|
@ -1,24 +0,0 @@
|
||||||
# This workflow will build a Java project with Maven
|
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
|
||||||
|
|
||||||
name: Java CI with Maven
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Set up JDK 1.8
|
|
||||||
uses: actions/setup-java@v1
|
|
||||||
with:
|
|
||||||
java-version: 1.8
|
|
||||||
- name: Build with Maven
|
|
||||||
run: mvn -B package --file pom.xml
|
|
||||||
|
|
@ -22,6 +22,3 @@ nbactions.xml
|
||||||
|
|
||||||
# Maven
|
# Maven
|
||||||
pom.xml.versionsBackup
|
pom.xml.versionsBackup
|
||||||
|
|
||||||
# MacOS
|
|
||||||
.DS_Store
|
|
||||||
|
|
|
||||||
26
README.md
26
README.md
|
|
@ -6,11 +6,11 @@ This project provides a Java client to manipulate Mikrotik routers using the rem
|
||||||
|
|
||||||
## Versions
|
## Versions
|
||||||
|
|
||||||

|
[](https://travis-ci.org/GideonLeGrange/mikrotik-java)
|
||||||
|
|
||||||
**The current stable version is 3.0.8**
|
**The current stable version is 3.0.4**
|
||||||
|
|
||||||
Version 3.0.8 fixes a null pointer error
|
Version 3.0.4 captures several months worth of small code clean-ups and documentation improvements.
|
||||||
|
|
||||||
### Changes in version 3.0:
|
### Changes in version 3.0:
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ Maven users can use the artifact from Maven Central with this dependency:
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>me.legrange</groupId>
|
<groupId>me.legrange</groupId>
|
||||||
<artifactId>mikrotik</artifactId>
|
<artifactId>mikrotik</artifactId>
|
||||||
<version>3.0.8</version>
|
<version>3.0.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -167,14 +167,6 @@ Change the IP address in the object created by the above example:
|
||||||
con.execute("/interface/gre/set .id=gre1 remote-address=10.0.1.1");
|
con.execute("/interface/gre/set .id=gre1 remote-address=10.0.1.1");
|
||||||
```
|
```
|
||||||
|
|
||||||
### Remove an existing object
|
|
||||||
|
|
||||||
And now remove the object:
|
|
||||||
|
|
||||||
```java
|
|
||||||
con.execute("/interface/gre/remove .id=gre1");
|
|
||||||
```
|
|
||||||
|
|
||||||
### Un-setting a variable on an object
|
### Un-setting a variable on an object
|
||||||
|
|
||||||
Un-setting a variable is a bit different, and you need to use a parameter called `value-name`. This isn't well documented. Let's say you have a firewall rule that was set up like this:
|
Un-setting a variable is a bit different, and you need to use a parameter called `value-name`. This isn't well documented. Let's say you have a firewall rule that was set up like this:
|
||||||
|
|
@ -188,6 +180,14 @@ Assuming the rule can be accessed as `.id=*1`, you un-set it by using `value-nam
|
||||||
con.execute("/ip/firewall/filter/unset .id=*1 value-name=time");
|
con.execute("/ip/firewall/filter/unset .id=*1 value-name=time");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Remove an existing object
|
||||||
|
|
||||||
|
And now remove the object:
|
||||||
|
|
||||||
|
```java
|
||||||
|
con.execute("/interface/gre/remove .id=gre1");
|
||||||
|
```
|
||||||
|
|
||||||
## Asynchronous commands
|
## Asynchronous commands
|
||||||
|
|
||||||
We can run some commands asynchronously in order to continue receiving updates:
|
We can run some commands asynchronously in order to continue receiving updates:
|
||||||
|
|
@ -225,6 +225,8 @@ The above command will run and send results asynchronously as they become availa
|
||||||
con.cancel(tag);
|
con.cancel(tag);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
From version 2.0.0 of the API the error() and completed() methods are part of the ResultListener interface.
|
||||||
|
|
||||||
## Command timeouts
|
## Command timeouts
|
||||||
|
|
||||||
Command timeouts can be used to make sure that synchronous commands either return or fail within a specific time. Command timeouts are separate from the connection timeout used in ```connect()```, and can be set using ```setTimeout()```. Here is an example:
|
Command timeouts can be used to make sure that synchronous commands either return or fail within a specific time. Command timeouts are separate from the connection timeout used in ```connect()```, and can be set using ```setTimeout()```. Here is an example:
|
||||||
|
|
|
||||||
10
pom.xml
10
pom.xml
|
|
@ -9,7 +9,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>me.legrange</groupId>
|
<groupId>me.legrange</groupId>
|
||||||
<artifactId>mikrotik</artifactId>
|
<artifactId>mikrotik</artifactId>
|
||||||
<version>3.0.8.1</version>
|
<version>3.0.4</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>Mikrotik API Java Client Library</name>
|
<name>Mikrotik API Java Client Library</name>
|
||||||
<url>https://github.com/GideonLeGrange/mikrotik-java</url>
|
<url>https://github.com/GideonLeGrange/mikrotik-java</url>
|
||||||
|
|
@ -33,8 +33,8 @@
|
||||||
<version>2.3.2</version>
|
<version>2.3.2</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<showDeprecation>true</showDeprecation>
|
<showDeprecation>true</showDeprecation>
|
||||||
<source>8</source>
|
<source>1.7</source>
|
||||||
<target>8</target>
|
<target>1.7</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
@ -73,7 +73,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<version>3.7.0</version>
|
<version>2.9.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<excludePackageNames>example;*.impl</excludePackageNames>
|
<excludePackageNames>example;*.impl</excludePackageNames>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
@ -161,7 +161,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>4.13.1</version>
|
<version>3.8.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ package examples;
|
||||||
*/
|
*/
|
||||||
public class Config {
|
public class Config {
|
||||||
|
|
||||||
public static final String HOST = "ce2.ter.cpt.adept.za.net";
|
public static final String HOST = "192.168.1.1";
|
||||||
public static final String USERNAME = "adept";
|
public static final String USERNAME = "admin";
|
||||||
public static final String PASSWORD = "34ffp9";
|
public static final String PASSWORD = "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
package examples;
|
|
||||||
|
|
||||||
import me.legrange.mikrotik.MikrotikApiException;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example 2: A command that returns results. Print all interfaces
|
|
||||||
*
|
|
||||||
* @author gideon
|
|
||||||
*/
|
|
||||||
public class NestedExpressions extends Example {
|
|
||||||
|
|
||||||
public static void main(String... args) throws Exception {
|
|
||||||
NestedExpressions ex = new NestedExpressions();
|
|
||||||
ex.connect();
|
|
||||||
ex.test("/ip/firewall/nat/print where (src-address=\"192.168.15.52\" or src-address=\"192.168.15.53\")");
|
|
||||||
ex.test("/ip/firewall/nat/print where chain=api_test and (src-address=192.168.15.52) and action=log ");
|
|
||||||
ex.test("/ip/firewall/nat/print where chain=api_test and (src-address=192.168.15.53 or src-address=192.168.15.52) and action=log ");
|
|
||||||
ex.test("/ip/firewall/nat/print where chain=api_test and (src-address=\"192.168.15.53\" or src-address=\"192.168.15.52\") and action=log ");
|
|
||||||
ex.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void test(String cmd) throws MikrotikApiException {
|
|
||||||
System.out.println("Command: " + cmd);
|
|
||||||
List<Map<String, String>> results = con.execute(cmd);
|
|
||||||
for (Map<String, String> result : results) {
|
|
||||||
System.out.println(result);
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
package examples;
|
|
||||||
|
|
||||||
import me.legrange.mikrotik.MikrotikApiException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example 1: A very simple command: Reboot the remote router
|
|
||||||
* @author gideon
|
|
||||||
*/
|
|
||||||
public class ScriptCommand extends Example {
|
|
||||||
|
|
||||||
public static void main(String...args) throws Exception {
|
|
||||||
ScriptCommand ex = new ScriptCommand();
|
|
||||||
ex.connect();
|
|
||||||
ex.test();
|
|
||||||
ex.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void test() throws MikrotikApiException {
|
|
||||||
con.execute("/ip/hotspot/user/profile/add name=\"cahyo-cek-2\" idle-timeout=none keepalive-timeout=2m status-autorefresh=1m shared-users=1 add-mac-cookie=true mac-cookie-timeout=3d parent-queue=none rate-limit=512k/512k on-login=:put(\",re,3000,12d,3000,,Enable,\")");
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/*
|
|
||||||
/ip/hotspot/user/profile/add name="cahyo-cek-2" idle-timeout=none keepalive-timeout=2m status-autorefresh=1m shared-users=1 add-mac-cookie=true mac-cookie-timeout=3d parent-queue=none rate-limit=512k/512k on-login=:put (",re,3000,12d,3000,,Enable,");
|
|
||||||
/system scheduler add name="usercahyo-cek-2-$user" interval=12:00:00 on-even="/ip hotspot active remove [find user=\"$user\"] ; /ip hotspot user set \"$user\" disable=yes ; /system scheduler remove [find name=usercahyo-cek-2-$user]"
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
command = action [ query ] [ return ]
|
command = action [ query ] [ return ]
|
||||||
action = ("/" word)+
|
action = ("/" word)+
|
||||||
query = "where" expr
|
query = "where" expr
|
||||||
expr = expr "and" expr | expr "or" expr | "not" expr | hasExpr | eqExpr | lessExpr | moreExpr | notExpr | nestedExpr
|
expr = expr "and" expr | expr "or" expr | "not" expr | hasExpr | eqExpr | lessExpr | moreExpr | notExpr
|
||||||
hasExpr = name
|
hasExpr = name
|
||||||
eqExpr = name "=" value
|
eqExpr = name "=" value
|
||||||
lessExpr = name "<" value
|
lessExpr = name "<" value
|
||||||
moreExpr = name ">" value
|
moreExpr = name ">" value
|
||||||
notExpr = name "!=" value
|
notExpr = name "!=" value
|
||||||
nestedExpr = "(" expr ")"
|
|
||||||
return = "return" (name)+
|
return = "return" (name)+
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,40 +4,31 @@ import me.legrange.mikrotik.MikrotikApiException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when the Mikrotik returns an error when receiving our command.
|
* Thrown when the Mikrotik returns an error when receiving our command.
|
||||||
*
|
|
||||||
* @author GideonLeGrange
|
* @author GideonLeGrange
|
||||||
*/
|
*/
|
||||||
public class ApiCommandException extends MikrotikApiException {
|
public class ApiCommandException extends MikrotikApiException {
|
||||||
|
|
||||||
private String tag = null;
|
|
||||||
private int category = 0;
|
|
||||||
|
|
||||||
/**
|
/** return the tag associated with this exception, if there is one
|
||||||
* return the tag associated with this exception, if there is one
|
* @return the tag associated with this exception. Null if there is no tag*/
|
||||||
*
|
|
||||||
* @return the tag associated with this exception. Null if there is no tag
|
|
||||||
*/
|
|
||||||
public String getTag() {
|
public String getTag() {
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApiCommandException(String msg) {
|
ApiCommandException(String msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
ApiCommandException(String msg, Throwable err) {
|
ApiCommandException(String msg, Throwable err) {
|
||||||
super(msg, err);
|
super(msg, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCategory() {
|
|
||||||
return category;
|
|
||||||
}
|
|
||||||
|
|
||||||
ApiCommandException(Error err) {
|
ApiCommandException(Error err) {
|
||||||
super(err.getMessage());
|
super(err.getMessage());
|
||||||
tag = err.getTag();
|
tag = err.getTag();
|
||||||
category = err.getCategory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String tag = null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import java.io.UnsupportedEncodingException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
@ -14,7 +15,6 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
import me.legrange.mikrotik.ApiConnection;
|
import me.legrange.mikrotik.ApiConnection;
|
||||||
import me.legrange.mikrotik.ApiConnectionException;
|
import me.legrange.mikrotik.ApiConnectionException;
|
||||||
|
|
@ -56,19 +56,12 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
if (username.trim().isEmpty()) {
|
if (username.trim().isEmpty()) {
|
||||||
throw new ApiConnectionException("API username cannot be empty");
|
throw new ApiConnectionException("API username cannot be empty");
|
||||||
}
|
}
|
||||||
Command cmd = new Command("/login");
|
List<Map<String, String>> list = execute("/login");
|
||||||
cmd.addParameter("name", username);
|
Map<String, String> res = list.get(0);
|
||||||
cmd.addParameter("password", password);
|
String hash = res.get("ret");
|
||||||
List<Map<String, String>> list = execute(cmd, timeout);
|
String chal = Util.hexStrToStr("00") + new String(password.toCharArray()) + Util.hexStrToStr(hash);
|
||||||
if (!list.isEmpty()) {
|
chal = Util.hashMD5(chal);
|
||||||
Map<String, String> res = list.get(0);
|
execute("/login name=" + username + " response=00" + chal);
|
||||||
if (res.containsKey("ret")) {
|
|
||||||
String hash = res.get("ret");
|
|
||||||
String chal = Util.hexStrToStr("00") + new String(password.toCharArray()) + Util.hexStrToStr(hash);
|
|
||||||
chal = Util.hashMD5(chal);
|
|
||||||
execute("/login name=" + username + " response=00" + chal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -90,6 +83,13 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
public void setTimeout(int timeout) throws MikrotikApiException {
|
public void setTimeout(int timeout) throws MikrotikApiException {
|
||||||
if (timeout > 0) {
|
if (timeout > 0) {
|
||||||
this.timeout = timeout;
|
this.timeout = timeout;
|
||||||
|
if (sock != null) {
|
||||||
|
try {
|
||||||
|
sock.setSoTimeout(timeout);
|
||||||
|
} catch (SocketException ex) {
|
||||||
|
throw new MikrotikApiException(String.format("Error setting socket timeout: %s", ex.getMessage()), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new MikrotikApiException(String.format("Invalid timeout value '%d'; must be postive", timeout));
|
throw new MikrotikApiException(String.format("Invalid timeout value '%d'; must be postive", timeout));
|
||||||
}
|
}
|
||||||
|
|
@ -144,6 +144,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
InetAddress ia = InetAddress.getByName(host.trim());
|
InetAddress ia = InetAddress.getByName(host.trim());
|
||||||
sock = fact.createSocket();
|
sock = fact.createSocket();
|
||||||
sock.connect(new InetSocketAddress(ia, port), conTimeout);
|
sock.connect(new InetSocketAddress(ia, port), conTimeout);
|
||||||
|
sock.setSoTimeout(timeout);
|
||||||
in = new DataInputStream(sock.getInputStream());
|
in = new DataInputStream(sock.getInputStream());
|
||||||
out = new DataOutputStream(sock.getOutputStream());
|
out = new DataOutputStream(sock.getOutputStream());
|
||||||
connected = true;
|
connected = true;
|
||||||
|
|
@ -163,7 +164,8 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized String nextTag() {
|
private synchronized String nextTag() {
|
||||||
return Integer.toHexString(_tag.incrementAndGet());
|
_tag++;
|
||||||
|
return Integer.toHexString(_tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Socket sock = null;
|
private Socket sock = null;
|
||||||
|
|
@ -173,7 +175,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
private Reader reader;
|
private Reader reader;
|
||||||
private Processor processor;
|
private Processor processor;
|
||||||
private final Map<String, ResultListener> listeners;
|
private final Map<String, ResultListener> listeners;
|
||||||
private final AtomicInteger _tag = new AtomicInteger(0);
|
private Integer _tag = 0;
|
||||||
private int timeout = ApiConnection.DEFAULT_COMMAND_TIMEOUT;
|
private int timeout = ApiConnection.DEFAULT_COMMAND_TIMEOUT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -186,7 +188,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String take() throws ApiConnectionException, ApiDataException {
|
private String take() throws ApiConnectionException, ApiDataException {
|
||||||
Object val;
|
Object val = null;
|
||||||
try {
|
try {
|
||||||
val = queue.take();
|
val = queue.take();
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
|
|
@ -214,8 +216,9 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
put(ex);
|
put(ex);
|
||||||
} catch (ApiConnectionException ex) {
|
} catch (ApiConnectionException ex) {
|
||||||
if (connected || !sock.isClosed()) {
|
if (connected || !sock.isClosed()) {
|
||||||
put(ex);
|
connected = false;
|
||||||
}
|
}
|
||||||
|
put(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -223,7 +226,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
private void put(Object data) {
|
private void put(Object data) {
|
||||||
try {
|
try {
|
||||||
queue.put(data);
|
queue.put(data);
|
||||||
} catch (InterruptedException ignored) {
|
} catch (InterruptedException ex) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,31 +251,27 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
} catch (ApiCommandException ex) {
|
} catch (ApiCommandException ex) {
|
||||||
String tag = ex.getTag();
|
String tag = ex.getTag();
|
||||||
if (tag != null) {
|
if (tag != null) {
|
||||||
res = new Error(tag, ex.getMessage(), ex.getCategory());
|
res = new Error(tag, ex.getMessage());
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} catch (MikrotikApiException ex) {
|
} catch (MikrotikApiException ex) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (res.getTag() != null) {
|
ResultListener l = listeners.get(res.getTag());
|
||||||
ResultListener l = listeners.get(res.getTag());
|
if (l != null) {
|
||||||
if (l != null) {
|
if (res instanceof Result) {
|
||||||
if (res instanceof Result) {
|
l.receive((Result) res);
|
||||||
l.receive((Result) res);
|
} else if (res instanceof Done) {
|
||||||
} else if (res instanceof Done) {
|
if (l instanceof SyncListener) {
|
||||||
if (l instanceof SyncListener) {
|
((SyncListener) l).completed((Done) res);
|
||||||
((SyncListener) l).completed((Done) res);
|
} else {
|
||||||
} else {
|
l.completed();
|
||||||
l.completed();
|
|
||||||
}
|
|
||||||
listeners.remove(res.getTag());
|
|
||||||
} else if (res instanceof Error) {
|
|
||||||
l.error(new ApiCommandException((Error) res));
|
|
||||||
}
|
}
|
||||||
|
listeners.remove(res.getTag());
|
||||||
|
} else if (res instanceof Error) {
|
||||||
|
l.error(new ApiCommandException((Error) res));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
nextTag();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +279,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
private void nextLine() throws ApiConnectionException, ApiDataException {
|
private void nextLine() throws ApiConnectionException, ApiDataException {
|
||||||
if (lines.isEmpty()) {
|
if (lines.isEmpty()) {
|
||||||
String block = reader.take();
|
String block = reader.take();
|
||||||
String[] parts = block.split("\n");
|
String parts[] = block.split("\n");
|
||||||
lines.addAll(Arrays.asList(parts));
|
lines.addAll(Arrays.asList(parts));
|
||||||
}
|
}
|
||||||
line = lines.remove(0);
|
line = lines.remove(0);
|
||||||
|
|
@ -293,7 +292,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
private String peekLine() throws ApiConnectionException, ApiDataException {
|
private String peekLine() throws ApiConnectionException, ApiDataException {
|
||||||
if (lines.isEmpty()) {
|
if (lines.isEmpty()) {
|
||||||
String block = reader.take();
|
String block = reader.take();
|
||||||
String[] parts = block.split("\n");
|
String parts[] = block.split("\n");
|
||||||
lines.addAll(Arrays.asList(parts));
|
lines.addAll(Arrays.asList(parts));
|
||||||
}
|
}
|
||||||
return lines.get(0);
|
return lines.get(0);
|
||||||
|
|
@ -306,10 +305,10 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
switch (line) {
|
switch (line) {
|
||||||
case "!re":
|
case "!re":
|
||||||
return unpackRe();
|
return unpackRe();
|
||||||
case "!empty":
|
|
||||||
case "!done":
|
case "!done":
|
||||||
return unpackDone();
|
return unpackDone();
|
||||||
case "!trap":
|
case "!trap":
|
||||||
|
return unpackError();
|
||||||
case "!halt":
|
case "!halt":
|
||||||
return unpackError();
|
return unpackError();
|
||||||
case "":
|
case "":
|
||||||
|
|
@ -320,10 +319,12 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
|
|
||||||
private Result unpackRe() throws ApiDataException, ApiConnectionException {
|
private Result unpackRe() throws ApiDataException, ApiConnectionException {
|
||||||
nextLine();
|
nextLine();
|
||||||
|
int l = 0;
|
||||||
Result res = new Result();
|
Result res = new Result();
|
||||||
while (!line.startsWith(("!"))) {
|
while (!line.startsWith(("!"))) {
|
||||||
|
l++;
|
||||||
if (line.startsWith(("="))) {
|
if (line.startsWith(("="))) {
|
||||||
String[] parts = line.split("=", 3);
|
String parts[] = line.split("=", 3);
|
||||||
if (parts.length == 3) {
|
if (parts.length == 3) {
|
||||||
if (!parts[2].endsWith("\r")) {
|
if (!parts[2].endsWith("\r")) {
|
||||||
res.put(parts[1], unpackResult(parts[2]));
|
res.put(parts[1], unpackResult(parts[2]));
|
||||||
|
|
@ -340,7 +341,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
throw new ApiDataException(String.format("Malformed line '%s'", line));
|
throw new ApiDataException(String.format("Malformed line '%s'", line));
|
||||||
}
|
}
|
||||||
} else if (line.startsWith(".tag=")) {
|
} else if (line.startsWith(".tag=")) {
|
||||||
String[] parts = line.split("=", 2);
|
String parts[] = line.split("=", 2);
|
||||||
if (parts.length == 2) {
|
if (parts.length == 2) {
|
||||||
res.setTag(parts[1]);
|
res.setTag(parts[1]);
|
||||||
}
|
}
|
||||||
|
|
@ -381,12 +382,12 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
|
|
||||||
while (!line.startsWith("!")) {
|
while (!line.startsWith("!")) {
|
||||||
if (line.startsWith(".tag=")) {
|
if (line.startsWith(".tag=")) {
|
||||||
String[] parts = line.split("=", 2);
|
String parts[] = line.split("=", 2);
|
||||||
if (parts.length == 2) {
|
if (parts.length == 2) {
|
||||||
done.setTag(parts[1]);
|
done.setTag(parts[1]);
|
||||||
}
|
}
|
||||||
} else if (line.startsWith(("=ret"))) {
|
} else if (line.startsWith(("=ret"))) {
|
||||||
String[] parts = line.split("=", 3);
|
String parts[] = line.split("=", 3);
|
||||||
if (parts.length == 3) {
|
if (parts.length == 3) {
|
||||||
done.setHash(parts[2]);
|
done.setHash(parts[2]);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -410,16 +411,13 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
if (hasNextLine()) {
|
if (hasNextLine()) {
|
||||||
while (!line.startsWith("!")) {
|
while (!line.startsWith("!")) {
|
||||||
if (line.startsWith(".tag=")) {
|
if (line.startsWith(".tag=")) {
|
||||||
String[] parts = line.split("=", 2);
|
String parts[] = line.split("=", 2);
|
||||||
if (parts.length == 2) {
|
if (parts.length == 2) {
|
||||||
err.setTag(parts[1]);
|
err.setTag(parts[1]);
|
||||||
}
|
}
|
||||||
} else if (line.startsWith("=message=")) {
|
} else if (line.startsWith("=message=")) {
|
||||||
err.setMessage(line.split("=", 3)[2]);
|
err.setMessage(line.split("=", 3)[2]);
|
||||||
}
|
}
|
||||||
else if (line.startsWith("=category=")) {
|
|
||||||
err.setCategory(Integer.parseInt(line.split("=", 3)[2]));
|
|
||||||
}
|
|
||||||
if (hasNextLine()) {
|
if (hasNextLine()) {
|
||||||
nextLine();
|
nextLine();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,12 @@
|
||||||
package me.legrange.mikrotik.impl;
|
package me.legrange.mikrotik.impl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to encapsulate API error information. We need to pass both the message and the tag (if one was used).
|
* Used to encapsulate API error information. We need to pass both the message and the tag (if one was used).
|
||||||
*
|
|
||||||
* @author GideonLeGrange
|
* @author GideonLeGrange
|
||||||
*/
|
*/
|
||||||
class Error extends Response {
|
class Error extends Response {
|
||||||
|
|
||||||
private String message;
|
Error(String tag, String message) {
|
||||||
private int category;
|
|
||||||
|
|
||||||
Error(String tag, String message, int category) {
|
|
||||||
super(tag);
|
super(tag);
|
||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
@ -22,16 +18,11 @@ class Error extends Response {
|
||||||
String getMessage() {
|
String getMessage() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMessage(String message) {
|
void setMessage(String message) {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
int getCategory() {
|
|
||||||
return category;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCategory(int category) {
|
|
||||||
this.category = category;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,30 +3,24 @@ package me.legrange.mikrotik.impl;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.legrange.mikrotik.impl.Scanner.Token;
|
import me.legrange.mikrotik.impl.Scanner.Token;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the pseudo-command line into command objects.
|
* Parse the pseudo-command line into command objects.
|
||||||
*
|
|
||||||
* @author GideonLeGrange
|
* @author GideonLeGrange
|
||||||
*/
|
*/
|
||||||
class Parser {
|
class Parser {
|
||||||
|
|
||||||
/**
|
/** parse the given bit of text into a Command object */
|
||||||
* parse the given bit of text into a Command object
|
|
||||||
*/
|
|
||||||
static Command parse(String text) throws ParseException {
|
static Command parse(String text) throws ParseException {
|
||||||
Parser parser = new Parser(text);
|
Parser parser = new Parser(text);
|
||||||
return parser.parse();
|
return parser.parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** run parse on the internal data and return the command object */
|
||||||
* run parse on the internal data and return the command object
|
|
||||||
*/
|
|
||||||
private Command parse() throws ParseException {
|
private Command parse() throws ParseException {
|
||||||
command();
|
command();
|
||||||
while (!is(Token.WHERE, Token.RETURN, Token.EOL)) {
|
while (!is(Token.WHERE, Token.RETURN, Token.EOL)) {
|
||||||
param();
|
param();
|
||||||
}
|
}
|
||||||
if (token == Token.WHERE) {
|
if (token == Token.WHERE) {
|
||||||
|
|
@ -38,7 +32,7 @@ class Parser {
|
||||||
expect(Token.EOL);
|
expect(Token.EOL);
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void command() throws ParseException {
|
private void command() throws ParseException {
|
||||||
StringBuilder path = new StringBuilder();
|
StringBuilder path = new StringBuilder();
|
||||||
do {
|
do {
|
||||||
|
|
@ -73,7 +67,8 @@ class Parser {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
cmd.addParameter(new Parameter(name, val.toString()));
|
cmd.addParameter(new Parameter(name, val.toString()));
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
cmd.addParameter(new Parameter(name));
|
cmd.addParameter(new Parameter(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +79,7 @@ class Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expr() throws ParseException {
|
private void expr() throws ParseException {
|
||||||
expect(Token.NOT, Token.TEXT, Token.LEFT_BRACKET);
|
expect(Token.NOT, Token.TEXT);
|
||||||
switch (token) {
|
switch (token) {
|
||||||
case NOT:
|
case NOT:
|
||||||
notExpr();
|
notExpr();
|
||||||
|
|
@ -97,7 +92,7 @@ class Parser {
|
||||||
case EQUALS:
|
case EQUALS:
|
||||||
eqExpr(name);
|
eqExpr(name);
|
||||||
break;
|
break;
|
||||||
case NOT_EQUALS:
|
case NOT_EQUALS :
|
||||||
notExpr(name);
|
notExpr(name);
|
||||||
break;
|
break;
|
||||||
case LESS:
|
case LESS:
|
||||||
|
|
@ -111,36 +106,23 @@ class Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LEFT_BRACKET:
|
|
||||||
nestedExpr();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
// if you get here, you had a expression, see if you want more.
|
// if you get here, you had a expression, see if you want more.
|
||||||
switch (token) {
|
switch (token) {
|
||||||
case AND:
|
case AND : andExpr();
|
||||||
andExpr();
|
|
||||||
break;
|
break;
|
||||||
case OR:
|
case OR : orExpr();
|
||||||
orExpr();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void nestedExpr() throws ParseException {
|
|
||||||
expect(Token.LEFT_BRACKET);
|
|
||||||
next();
|
|
||||||
expr();
|
|
||||||
expect(Token.RIGHT_BRACKET);
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void andExpr() throws ParseException {
|
private void andExpr() throws ParseException {
|
||||||
next(); // eat and
|
next(); // eat and
|
||||||
expr();
|
expr();
|
||||||
cmd.addQuery("?#&");
|
cmd.addQuery("?#&");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void orExpr() throws ParseException {
|
private void orExpr() throws ParseException {
|
||||||
next(); // eat or
|
next(); // eat or
|
||||||
expr();
|
expr();
|
||||||
cmd.addQuery("?#|");
|
cmd.addQuery("?#|");
|
||||||
|
|
@ -153,7 +135,7 @@ class Parser {
|
||||||
cmd.addQuery("?#!");
|
cmd.addQuery("?#!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void eqExpr(String name) throws ParseException {
|
private void eqExpr(String name) throws ParseException {
|
||||||
next(); // eat =
|
next(); // eat =
|
||||||
expect(Token.TEXT);
|
expect(Token.TEXT);
|
||||||
cmd.addQuery(String.format("?%s=%s", name, text));
|
cmd.addQuery(String.format("?%s=%s", name, text));
|
||||||
|
|
@ -172,7 +154,7 @@ class Parser {
|
||||||
cmd.addQuery("?#!");
|
cmd.addQuery("?#!");
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void moreExpr(String name) throws ScanException {
|
private void moreExpr(String name) throws ScanException {
|
||||||
next(); // eat >
|
next(); // eat >
|
||||||
cmd.addQuery(String.format("?>%s=%s", name, text));
|
cmd.addQuery(String.format("?>%s=%s", name, text));
|
||||||
|
|
@ -196,21 +178,19 @@ class Parser {
|
||||||
cmd.addProperty(props.toArray(new String[props.size()]));
|
cmd.addProperty(props.toArray(new String[props.size()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expect(Token... tokens) throws ParseException {
|
private void expect(Token...tokens) throws ParseException {
|
||||||
if (!is(tokens))
|
if (!is(tokens))
|
||||||
throw new ParseException(String.format("Expected %s but found %s at position %d", Arrays.asList(tokens), this.token, scanner.pos()));
|
throw new ParseException(String.format("Expected %s but found %s at position %d", Arrays.asList(tokens), this.token, scanner.pos()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean is(Token... tokens) {
|
private boolean is(Token...tokens) {
|
||||||
for (Token want : tokens) {
|
for (Token want : tokens) {
|
||||||
if (this.token == want) return true;
|
if (this.token == want) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** move to the next token returned by the scanner */
|
||||||
* move to the next token returned by the scanner
|
|
||||||
*/
|
|
||||||
private void next() throws ScanException {
|
private void next() throws ScanException {
|
||||||
token = scanner.next();
|
token = scanner.next();
|
||||||
while (token == Token.WS) {
|
while (token == Token.WS) {
|
||||||
|
|
@ -224,7 +204,7 @@ class Parser {
|
||||||
scanner = new Scanner(line);
|
scanner = new Scanner(line);
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Scanner scanner;
|
private final Scanner scanner;
|
||||||
private Token token;
|
private Token token;
|
||||||
private String text;
|
private String text;
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ class Scanner {
|
||||||
|
|
||||||
SLASH("/"), COMMA(","), EOL(), WS, TEXT,
|
SLASH("/"), COMMA(","), EOL(), WS, TEXT,
|
||||||
LESS("<"), MORE(">"), EQUALS("="), NOT_EQUALS("!="), PIPE("!"),
|
LESS("<"), MORE(">"), EQUALS("="), NOT_EQUALS("!="), PIPE("!"),
|
||||||
LEFT_BRACKET("("), RIGHT_BRACKET(")"),
|
|
||||||
WHERE, NOT, AND, OR, RETURN;
|
WHERE, NOT, AND, OR, RETURN;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -81,12 +80,6 @@ class Scanner {
|
||||||
case '=':
|
case '=':
|
||||||
nextChar();
|
nextChar();
|
||||||
return EQUALS;
|
return EQUALS;
|
||||||
case '(' :
|
|
||||||
nextChar();
|
|
||||||
return LEFT_BRACKET;
|
|
||||||
case ')' :
|
|
||||||
nextChar();
|
|
||||||
return RIGHT_BRACKET;
|
|
||||||
case '!':
|
case '!':
|
||||||
return pipe();
|
return pipe();
|
||||||
case '"':
|
case '"':
|
||||||
|
|
@ -121,7 +114,7 @@ class Scanner {
|
||||||
*/
|
*/
|
||||||
private Token name() throws ScanException {
|
private Token name() throws ScanException {
|
||||||
text = new StringBuilder();
|
text = new StringBuilder();
|
||||||
while (!in(c, "[ \t\r\n=<>!)]")) {
|
while (!in(c, "[ \t\r\n=<>!]")) {
|
||||||
text.append(c);
|
text.append(c);
|
||||||
nextChar();
|
nextChar();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -192,4 +192,5 @@ final class Util {
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue