commit
adc5f55b60
|
|
@ -19,3 +19,6 @@ nbactions.xml
|
||||||
.classpath
|
.classpath
|
||||||
|
|
||||||
.project
|
.project
|
||||||
|
|
||||||
|
# Maven
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
|
|
||||||
78
README.md
78
README.md
|
|
@ -1,29 +1,29 @@
|
||||||
mikrotik-java
|
# mikrotik-java
|
||||||
=============
|
|
||||||
|
|
||||||
A Java client library implementation for the Mikrotik RouterOS API.
|
A Java client library implementation for the Mikrotik RouterOS API.
|
||||||
|
|
||||||
This project provides a Java client to manipulate Mikrotik routers using the remote API. Simple things must be easy to do, and complex things must be possible.
|
This project provides a Java client to manipulate Mikrotik routers using the remote API. Simple things must be easy to do, and complex things must be possible.
|
||||||
|
|
||||||
Versions
|
## Versions
|
||||||
--------
|
|
||||||
|
|
||||||
This library uses [semantic versioning](http://semver.org/)
|
This library uses [semantic versioning](http://semver.org/)
|
||||||
|
|
||||||
**The current stable version is 2.0.2.**
|
**The current stable version is 2.1**
|
||||||
|
|
||||||
### Changes in version 2.0.2:
|
### Changes in version 2.1:
|
||||||
|
|
||||||
* Fixed bug #13 - processor thread wasn't being stopped on disconnect(), causing non-exit of application in some cases.
|
Version 2.1 adds the ability to use connection and command timeouts as requested by users. To see how to use these, please refer to the examples below. For more information, look at issue [#16](https://github.com/GideonLeGrange/mikrotik-java/issues/16).
|
||||||
|
|
||||||
### Previous 2.0 versions:
|
#### Previous 2.0 versions:
|
||||||
|
|
||||||
|
* 2.0.3 Fixed bug #18 - An empty username in ```login()``` caused the API to hang.
|
||||||
|
* 2.0.2 Fixed bug #13 - processor thread wasn't being stopped on disconnect(), causing non-exit of application in some cases.
|
||||||
* 2.0.1 fixed parsing of !=, < and > operators in a where clause.
|
* 2.0.1 fixed parsing of !=, < and > operators in a where clause.
|
||||||
* 2.0.0 changed ResultListener to receive errors and completion notifications. This version is not backwards compatible with version 1.x.
|
* 2.0.0 changed ResultListener to receive errors and completion notifications. This version is not backwards compatible with version 1.x.
|
||||||
|
|
||||||
The last version 1.x release was 1.1.6 which was the first version to be pushed to Maven Central. Version 1 is considered *deprecated* and will no longer be supported or patched.
|
The last version 1.x release was 1.1.6 which was the first version to be pushed to Maven Central. Version 1 is considered *deprecated* and will no longer be supported or patched.
|
||||||
|
|
||||||
Previous 1.1 versions:
|
#### Previous 1.1 versions:
|
||||||
|
|
||||||
* 1.1.5 fixed even more comamnd line parsing bugs.
|
* 1.1.5 fixed even more comamnd line parsing bugs.
|
||||||
* 1.1.4 fixed command line parsing bugs #7 and #8.
|
* 1.1.4 fixed command line parsing bugs #7 and #8.
|
||||||
|
|
@ -31,8 +31,7 @@ Previous 1.1 versions:
|
||||||
* 1.1.2 added support for handling multi-line results, like for example /file print.
|
* 1.1.2 added support for handling multi-line results, like for example /file print.
|
||||||
* 1.1 added TLS (SSL) support to encrypt API traffic.
|
* 1.1 added TLS (SSL) support to encrypt API traffic.
|
||||||
|
|
||||||
Getting the API
|
## Getting the API
|
||||||
---------------
|
|
||||||
|
|
||||||
I recommend using the Maven artifact from Maven Central using this dependency:
|
I recommend using the Maven artifact from Maven Central using this dependency:
|
||||||
|
|
||||||
|
|
@ -40,21 +39,20 @@ I recommend using the Maven artifact from Maven Central using this dependency:
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>me.legrange</groupId>
|
<groupId>me.legrange</groupId>
|
||||||
<artifactId>mikrotik</artifactId>
|
<artifactId>mikrotik</artifactId>
|
||||||
<version>2.0.2</version>
|
<version>2.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
You can also clone or fork the repository, or download the source as a zip or tar.gz from [Releases](https://github.com/GideonLeGrange/mikrotik-java/releases)
|
You can also clone or fork the repository, or download the source as a zip or tar.gz from [Releases](https://github.com/GideonLeGrange/mikrotik-java/releases)
|
||||||
|
|
||||||
Contributing
|
## Contributing
|
||||||
------------
|
|
||||||
|
|
||||||
I welcome contributions, be it bug fixes or other improvements. If you fix or change something, please submit a pull request. If you want to report a bug, please open an issue.
|
I welcome contributions, be it bug fixes or other improvements. If you fix or change something, please submit a pull request. If you want to report a bug, please open an issue.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
Examples
|
|
||||||
========
|
|
||||||
These examples should illustrate how to use this library. Please note that I assume that the user is proficient in Java and understands the Mikrotik command line syntax.
|
These examples should illustrate how to use this library. Please note that I assume that the user is proficient in Java and understands the Mikrotik command line syntax.
|
||||||
|
|
||||||
|
## Opening a connection
|
||||||
Here is a simple example: Connect to a router and reboot it.
|
Here is a simple example: Connect to a router and reboot it.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
|
|
@ -75,7 +73,24 @@ To open an encrypted (TLS) connection is as simple, assuming the default API-SSL
|
||||||
ApiConnection con = ApiConnection.connectTLS("10.0.1.1"); // connect to router using TLS
|
ApiConnection con = ApiConnection.connectTLS("10.0.1.1"); // connect to router using TLS
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Notes about TLS:
|
### Connection timeouts
|
||||||
|
|
||||||
|
By default, the API will generate an exception if it cannot connect to the specified router. This can take place immediately (typically if the router returns a 'Connection refused' error), but can also take up to 60 seconds if the router host is firewalled or if there are other network problems. This 60 seconds is the 'default connection timeout' an can be overridded by passing the preferred timeout to the APi as last parameter in a ```connect()``` or ```connectTLS()``` call. Here is the non-TLS example:
|
||||||
|
|
||||||
|
```java
|
||||||
|
ApiConnection con = ApiConnection.connect("10.0.1.1", ApiConnection.DEFAULT_PORT, 2000); // connect to router on the default API port and fail in 2 seconds
|
||||||
|
```
|
||||||
|
|
||||||
|
Connecting using TLS is similar:
|
||||||
|
|
||||||
|
```java
|
||||||
|
ApiConnection con = ApiConnection.connect("10.0.1.1", ApiConnection.DEFAULT_TLS_PORT, 2000); // connect to router on the default TLS API port and fail in 2 seconds
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that ```ApiConnection.DEFAULT_PORT``` and ```ApiConnection.DEFAULT_TLS_PORT``` are provided to allow users who use the default ports to safely use the overloaded timeout method.
|
||||||
|
|
||||||
|
### Notes about TLS:
|
||||||
|
|
||||||
* Currently only anonymous TLS is supported, not certificates.
|
* Currently only anonymous TLS is supported, not certificates.
|
||||||
* There is a compatibility problem between the current versions of RouterOS supporting API over TLS and the Java Cryptography Extension (JCE) in Java 7 and earlier. TLS encryption works in Java 8 and later. For more information, feel free to contact me.
|
* There is a compatibility problem between the current versions of RouterOS supporting API over TLS and the Java Cryptography Extension (JCE) in Java 7 and earlier. TLS encryption works in Java 8 and later. For more information, feel free to contact me.
|
||||||
|
|
||||||
|
|
@ -83,8 +98,7 @@ ApiConnection con = ApiConnection.connectTLS("10.0.1.1"); // connect to router u
|
||||||
|
|
||||||
In following examples the connection, login and disconnection code will not be repeated.
|
In following examples the connection, login and disconnection code will not be repeated.
|
||||||
|
|
||||||
Reading data
|
## Reading data
|
||||||
------------
|
|
||||||
|
|
||||||
A simple example that returns a result: Print all interfaces.
|
A simple example that returns a result: Print all interfaces.
|
||||||
|
|
||||||
|
|
@ -120,8 +134,7 @@ The same query, but we only want certain result fields names: Print all interfac
|
||||||
List<Map<String, String>> rs = con.execute("/interface/print where type=vlan return name");
|
List<Map<String, String>> rs = con.execute("/interface/print where type=vlan return name");
|
||||||
```
|
```
|
||||||
|
|
||||||
Writing data
|
## Writing data
|
||||||
------------
|
|
||||||
|
|
||||||
Creating, modifying and deleting configuration objects is of course possible.
|
Creating, modifying and deleting configuration objects is of course possible.
|
||||||
|
|
||||||
|
|
@ -149,8 +162,7 @@ And now remove the object:
|
||||||
con.execute("/interface/gre/remove .id=gre1");
|
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:
|
||||||
|
|
||||||
|
|
@ -184,13 +196,25 @@ con.cancel(tag);
|
||||||
|
|
||||||
From version 2.0.0 of the API the error() and completed() methods are part of the ResultListener interface.
|
From version 2.0.0 of the API the error() and completed() methods are part of the ResultListener interface.
|
||||||
|
|
||||||
References
|
## 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 ```connectTLS()```, and can be set using ```setTimeout()```. Here is an example:
|
||||||
|
|
||||||
|
```java
|
||||||
|
ApiConnection con = ApiConnection.connect("10.0.1.1"); // connect to router
|
||||||
|
con.setTimeout(5000); // set command timeout to 5 seconds
|
||||||
|
con.login("admin","password"); // log in to router
|
||||||
|
con.execute("/system/reboot"); // execute a command
|
||||||
|
```
|
||||||
|
It is important to note that command timeouts can be set before ```login()``` is called, and can therefore influence the behaviour of login.
|
||||||
|
|
||||||
|
The default command timeout, if none is set by the user, is 60 seconds.
|
||||||
|
|
||||||
|
# References
|
||||||
|
|
||||||
The RouterOS API is documented here: http://wiki.mikrotik.com/wiki/Manual:API
|
The RouterOS API is documented here: http://wiki.mikrotik.com/wiki/Manual:API
|
||||||
|
|
||||||
Licence
|
# Licence
|
||||||
=======
|
|
||||||
|
|
||||||
This library is released under the Apache 2.0 licence. See the [Licence.md](Licence.md) file
|
This library is released under the Apache 2.0 licence. See the [Licence.md](Licence.md) file
|
||||||
|
|
||||||
|
|
|
||||||
98
pom.xml
98
pom.xml
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<groupId>me.legrange</groupId>
|
<groupId>me.legrange</groupId>
|
||||||
<artifactId>mikrotik</artifactId>
|
<artifactId>mikrotik</artifactId>
|
||||||
<version>2.0.2</version>
|
<version>2.1</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>Mikrotik API Java Client Library</name>
|
<name>Mikrotik API Java Client Library</name>
|
||||||
|
|
@ -88,37 +88,75 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-gpg-plugin</artifactId>
|
|
||||||
<version>1.5</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<configuration>
|
|
||||||
<keyname>D20D95D7</keyname>
|
|
||||||
</configuration>
|
|
||||||
<id>sign-artifacts</id>
|
|
||||||
<phase>verify</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>sign</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.sonatype.plugins</groupId>
|
|
||||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
|
||||||
<version>1.6.2</version>
|
|
||||||
<extensions>true</extensions>
|
|
||||||
<configuration>
|
|
||||||
<serverId>sonatype-nexus-staging</serverId>
|
|
||||||
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
|
||||||
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>release</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<version>2.4</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>attach-sources</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>2.10.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>attach-javadocs</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-gpg-plugin</artifactId>
|
||||||
|
<version>1.6</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>sign-artifacts</id>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>sign</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.sonatype.plugins</groupId>
|
||||||
|
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||||
|
<version>1.6.5</version>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<configuration>
|
||||||
|
<serverId>ossrh</serverId>
|
||||||
|
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||||
|
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<distributionManagement>
|
||||||
|
<snapshotRepository>
|
||||||
|
<id>ossrh</id>
|
||||||
|
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||||
|
</snapshotRepository>
|
||||||
|
</distributionManagement>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ package examples;
|
||||||
public class Config {
|
public class Config {
|
||||||
|
|
||||||
|
|
||||||
public static final String HOST = "10.0.1.134";
|
public static final String HOST = "192.168.1.34";
|
||||||
public static final String USERNAME = "admin";
|
public static final String USERNAME = "admin";
|
||||||
public static final String PASSWORD = "";
|
public static final String PASSWORD = "";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import me.legrange.mikrotik.ApiConnection;
|
||||||
abstract class Example {
|
abstract class Example {
|
||||||
|
|
||||||
protected void connect() throws Exception {
|
protected void connect() throws Exception {
|
||||||
con = ApiConnection.connect(Config.HOST);
|
con = ApiConnection.connect(Config.HOST, ApiConnection.DEFAULT_PORT, 2000);
|
||||||
con.login(Config.USERNAME, Config.PASSWORD);
|
con.login(Config.USERNAME, Config.PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ public class Example2 extends Example {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void test() throws MikrotikApiException {
|
private void test() throws MikrotikApiException {
|
||||||
|
con.setTimeout(50);
|
||||||
List<Map<String, String>> results = con.execute("/interface/print");
|
List<Map<String, String>> results = con.execute("/interface/print");
|
||||||
for (Map<String, String> result : results) {
|
for (Map<String, String> result : results) {
|
||||||
System.out.println(result);
|
System.out.println(result);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package examples;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import me.legrange.mikrotik.MikrotikApiException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example 9: Test special characters in usernames
|
||||||
|
*
|
||||||
|
* @author gideon
|
||||||
|
*/
|
||||||
|
public class Example9 extends Example {
|
||||||
|
|
||||||
|
public static void main(String... args) throws Exception {
|
||||||
|
Example9 ex = new Example9();
|
||||||
|
ex.connect();
|
||||||
|
ex.test();
|
||||||
|
ex.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void test() throws MikrotikApiException, InterruptedException {
|
||||||
|
List<Map<String, String>> res = con.execute("/user/add name=çãáõ");
|
||||||
|
for (Map<String, String> r : res) {
|
||||||
|
System.out.println(r);
|
||||||
|
}
|
||||||
|
// con.execute("/ip/firewall/filter/add chain=forward hotspot=!auth protocol=tcp src-port=8000-8084");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,28 @@ import me.legrange.mikrotik.impl.ApiConnectionImpl;
|
||||||
* @author GideonLeGrange
|
* @author GideonLeGrange
|
||||||
*/
|
*/
|
||||||
public abstract class ApiConnection {
|
public abstract class ApiConnection {
|
||||||
|
|
||||||
|
/** default TCP port used by Mikrotik API */
|
||||||
|
public static final int DEFAULT_PORT = 8728;
|
||||||
|
/** default TCP TLS port used by Mikrotik API */
|
||||||
|
public static final int DEFAULT_TLS_PORT = 8729;
|
||||||
|
/** default connection timeout to use when opening the connection */
|
||||||
|
public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
|
||||||
|
/** default command timeout used for synchronous commands */
|
||||||
|
public static final int DEFAULT_COMMAND_TIMEOUT = 60000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new API connection to the give device on the supplied port, using anonymous TLS for encryption.
|
||||||
|
* @param host The host to which to connect.
|
||||||
|
* @param port The TCP port to use.
|
||||||
|
* @param timeout The connection timeout to use when opening the connection.
|
||||||
|
* @return The ApiConnection
|
||||||
|
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if there is a problem connecting
|
||||||
|
*/
|
||||||
|
public static ApiConnection connectTLS(String host, int port, int timeout) throws MikrotikApiException {
|
||||||
|
return ApiConnectionImpl.connect(host, port, true, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new API connection to the give device on the supplied port, using anonymous TLS for encryption.
|
* Create a new API connection to the give device on the supplied port, using anonymous TLS for encryption.
|
||||||
|
|
@ -20,7 +42,7 @@ public abstract class ApiConnection {
|
||||||
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if there is a problem connecting
|
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if there is a problem connecting
|
||||||
*/
|
*/
|
||||||
public static ApiConnection connectTLS(String host, int port) throws MikrotikApiException {
|
public static ApiConnection connectTLS(String host, int port) throws MikrotikApiException {
|
||||||
return ApiConnectionImpl.connect(host, port, true);
|
return ApiConnectionImpl.connect(host, port, true, DEFAULT_CONNECTION_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -31,9 +53,20 @@ public abstract class ApiConnection {
|
||||||
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if there is a problem connecting
|
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if there is a problem connecting
|
||||||
*/
|
*/
|
||||||
public static ApiConnection connectTLS(String host) throws MikrotikApiException {
|
public static ApiConnection connectTLS(String host) throws MikrotikApiException {
|
||||||
return ApiConnectionImpl.connect(host, DEFAULT_TLS_PORT, true);
|
return ApiConnectionImpl.connect(host, DEFAULT_TLS_PORT, true, DEFAULT_CONNECTION_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new API connection to the give device on the supplied port
|
||||||
|
* @param host The host to which to connect.
|
||||||
|
* @param port The TCP port to use.
|
||||||
|
* @param timeout The connection timeout to use when opening the connection.
|
||||||
|
* @return The ApiConnection
|
||||||
|
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if there is a problem connecting
|
||||||
|
*/
|
||||||
|
public static ApiConnection connect(String host, int port, int timeout) throws MikrotikApiException {
|
||||||
|
return ApiConnectionImpl.connect(host, port, false, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new API connection to the give device on the supplied port
|
* Create a new API connection to the give device on the supplied port
|
||||||
|
|
@ -43,7 +76,7 @@ public abstract class ApiConnection {
|
||||||
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if there is a problem connecting
|
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if there is a problem connecting
|
||||||
*/
|
*/
|
||||||
public static ApiConnection connect(String host, int port) throws MikrotikApiException {
|
public static ApiConnection connect(String host, int port) throws MikrotikApiException {
|
||||||
return ApiConnectionImpl.connect(host, port, false);
|
return ApiConnectionImpl.connect(host, port, false, DEFAULT_CONNECTION_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -99,10 +132,18 @@ public abstract class ApiConnection {
|
||||||
* @param tag The tag of the command to cancel
|
* @param tag The tag of the command to cancel
|
||||||
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if there is a problem canceling the command */
|
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if there is a problem canceling the command */
|
||||||
public abstract void cancel(String tag) throws MikrotikApiException;
|
public abstract void cancel(String tag) throws MikrotikApiException;
|
||||||
|
|
||||||
|
|
||||||
|
/** set the command timeout. The command timeout is used to time out API
|
||||||
|
* commands after a specific time.
|
||||||
|
*
|
||||||
|
* Note: This is not the same as the timeout value passed in the connect() and
|
||||||
|
* connectTLS() methods. This timeout is specific to synchronous commands, that
|
||||||
|
* timeout is applied to opening the API socket.
|
||||||
|
*
|
||||||
|
* @param timeout The time out in milliseconds.
|
||||||
|
* @throws MikrotikApiException Thrown if the timeout specified is invalid.
|
||||||
|
*/
|
||||||
|
public abstract void setTimeout(int timeout) throws MikrotikApiException;
|
||||||
|
|
||||||
/** default TCP port used by Mikrotik API */
|
|
||||||
private static final int DEFAULT_PORT = 8728;
|
|
||||||
/** default TCP TLS port used by Mikrotik API */
|
|
||||||
private static final int DEFAULT_TLS_PORT = 8729;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -5,7 +5,9 @@ import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.SocketAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
@ -34,32 +36,22 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
* @param host The host to which to connect.
|
* @param host The host to which to connect.
|
||||||
* @param port The TCP port to use.
|
* @param port The TCP port to use.
|
||||||
* @param secure Is TLS required
|
* @param secure Is TLS required
|
||||||
|
* @param timeOut The connection timeout
|
||||||
* @return The ApiConnection
|
* @return The ApiConnection
|
||||||
* @throws me.legrange.mikrotik.ApiConnectionException Thrown if there is a
|
* @throws me.legrange.mikrotik.ApiConnectionException Thrown if there is a
|
||||||
* problem connecting
|
* problem connecting
|
||||||
*/
|
*/
|
||||||
public static ApiConnection connect(String host, int port, boolean secure) throws ApiConnectionException {
|
public static ApiConnection connect(String host, int port, boolean secure, int timeOut) throws ApiConnectionException {
|
||||||
ApiConnectionImpl con = new ApiConnectionImpl();
|
ApiConnectionImpl con = new ApiConnectionImpl();
|
||||||
con.open(host, port, secure);
|
con.open(host, port, secure, timeOut);
|
||||||
return con;
|
return con;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the state of connection.
|
|
||||||
*
|
|
||||||
* @return if connection is established to router it returns true.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
return connected;
|
return connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnect from the remote API
|
|
||||||
*
|
|
||||||
* @throws me.legrange.mikrotik.ApiConnectionException Thrown if there is a
|
|
||||||
* problem disconnecting
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnect() throws ApiConnectionException {
|
public void disconnect() throws ApiConnectionException {
|
||||||
if (!connected) {
|
if (!connected) {
|
||||||
|
|
@ -69,22 +61,19 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
processor.interrupt();
|
processor.interrupt();
|
||||||
reader.interrupt();
|
reader.interrupt();
|
||||||
try {
|
try {
|
||||||
|
in.close();
|
||||||
|
out.close();
|
||||||
sock.close();
|
sock.close();
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new ApiConnectionException(String.format("Error closing socket: %s", ex.getMessage()), ex);
|
throw new ApiConnectionException(String.format("Error closing socket: %s", ex.getMessage()), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Log in to the remote router.
|
|
||||||
*
|
|
||||||
* @param username - username of the user on the router
|
|
||||||
* @param password - password for the user
|
|
||||||
* @throws me.legrange.mikrotik.MikrotikApiException
|
|
||||||
* @throws java.lang.InterruptedException
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void login(String username, String password) throws MikrotikApiException, InterruptedException {
|
public void login(String username, String password) throws MikrotikApiException, InterruptedException {
|
||||||
|
if (username.trim().isEmpty()) {
|
||||||
|
throw new ApiConnectionException("API username cannot be empty");
|
||||||
|
}
|
||||||
List<Map<String, String>> list = execute("/login");
|
List<Map<String, String>> list = execute("/login");
|
||||||
Map<String, String> res = list.get(0);
|
Map<String, String> res = list.get(0);
|
||||||
String hash = res.get("ret");
|
String hash = res.get("ret");
|
||||||
|
|
@ -93,45 +82,35 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
execute("/login name=" + username + " response=00" + chal);
|
execute("/login name=" + username + " response=00" + chal);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* execute a command and return a list of results.
|
|
||||||
*
|
|
||||||
* @param cmd Command to execute
|
|
||||||
* @return The list of results
|
|
||||||
* @throws me.legrange.mikrotik.MikrotikApiException
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public List<Map<String, String>> execute(String cmd) throws MikrotikApiException {
|
public List<Map<String, String>> execute(String cmd) throws MikrotikApiException {
|
||||||
return execute(Parser.parse(cmd));
|
return execute(Parser.parse(cmd), timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* execute a command and attach a result listener to receive it's results.
|
|
||||||
*
|
|
||||||
* @param cmd Command to execute
|
|
||||||
* @param lis ResultListener that will receive the results
|
|
||||||
* @return A command object that can be used to cancel the command.
|
|
||||||
* @throws MikrotikApiException
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String cmd, ResultListener lis) throws MikrotikApiException {
|
public String execute(String cmd, ResultListener lis) throws MikrotikApiException {
|
||||||
return execute(Parser.parse(cmd), lis);
|
return execute(Parser.parse(cmd), lis);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* cancel a command
|
|
||||||
* @param tag
|
|
||||||
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if an error is experienced while canceling the
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void cancel(String tag) throws MikrotikApiException {
|
public void cancel(String tag) throws MikrotikApiException {
|
||||||
execute(String.format("/cancel tag=%s", tag));
|
execute(String.format("/cancel tag=%s", tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Map<String, String>> execute(Command cmd) throws MikrotikApiException {
|
@Override
|
||||||
|
public void setTimeout(int timeout) throws MikrotikApiException {
|
||||||
|
if (timeout > 0) {
|
||||||
|
this.timeout = timeout;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new MikrotikApiException(String.format("Invalid timeout value '%d'; must be postive", timeout));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Map<String, String>> execute(Command cmd, int timeout) throws MikrotikApiException {
|
||||||
SyncListener l = new SyncListener();
|
SyncListener l = new SyncListener();
|
||||||
execute(cmd, l);
|
execute(cmd, l);
|
||||||
return l.getResults();
|
return l.getResults(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String execute(Command cmd, ResultListener lis) throws MikrotikApiException {
|
private String execute(Command cmd, ResultListener lis) throws MikrotikApiException {
|
||||||
|
|
@ -152,23 +131,16 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
this.listeners = new ConcurrentHashMap<>();
|
this.listeners = new ConcurrentHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Start the API. Connects to the Mikrotik without using encryption
|
|
||||||
*/
|
|
||||||
private void open(String host, int port) throws ApiConnectionException {
|
|
||||||
open(host, port, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the API. Connects to the Mikrotik
|
* Start the API. Connects to the Mikrotik
|
||||||
*/
|
*/
|
||||||
private void open(String host, int port, boolean secure) throws ApiConnectionException {
|
private void open(String host, int port, boolean secure, int conTimeout) throws ApiConnectionException {
|
||||||
try {
|
try {
|
||||||
InetAddress ia = InetAddress.getByName(host.trim());
|
InetAddress ia = InetAddress.getByName(host.trim());
|
||||||
if (secure) {
|
if (secure) {
|
||||||
sock = openSSLSocket(ia, port);
|
sock = openSSLSocket(ia, port, conTimeout);
|
||||||
} else {
|
} else {
|
||||||
sock = new Socket(ia, port);
|
sock = openClearSocket(ia, port, conTimeout);
|
||||||
}
|
}
|
||||||
in = new DataInputStream(sock.getInputStream());
|
in = new DataInputStream(sock.getInputStream());
|
||||||
out = new DataOutputStream(sock.getOutputStream());
|
out = new DataOutputStream(sock.getOutputStream());
|
||||||
|
|
@ -188,11 +160,19 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Socket openClearSocket(InetAddress ia, int port, int timeOut) throws IOException {
|
||||||
|
Socket clear = new Socket();
|
||||||
|
SocketAddress addr = new InetSocketAddress(ia, port);
|
||||||
|
clear.connect(new InetSocketAddress(ia, port), timeOut);
|
||||||
|
return clear;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* open and configure a SSL socket.
|
* open and configure a SSL socket.
|
||||||
*/
|
*/
|
||||||
private Socket openSSLSocket(InetAddress ia, int port) throws IOException {
|
private Socket openSSLSocket(InetAddress ia, int port, int timeOut) throws IOException {
|
||||||
SSLSocket ssl = (SSLSocket) SSLSocketFactory.getDefault().createSocket(ia, port);
|
SSLSocket ssl = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
|
||||||
|
ssl.connect(new InetSocketAddress(ia, port), timeOut);
|
||||||
List<String> cs = new LinkedList<>();
|
List<String> cs = new LinkedList<>();
|
||||||
// not happy with this code. Without it, SSL throws a "Remote host closed connection during handshake" error
|
// not happy with this code. Without it, SSL throws a "Remote host closed connection during handshake" error
|
||||||
// caused by a "SSL peer shut down incorrectly" error
|
// caused by a "SSL peer shut down incorrectly" error
|
||||||
|
|
@ -209,7 +189,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
_tag++;
|
_tag++;
|
||||||
return Integer.toHexString(_tag);
|
return Integer.toHexString(_tag);
|
||||||
}
|
}
|
||||||
private static final int DEFAULT_PORT = 8728;
|
|
||||||
private Socket sock = null;
|
private Socket sock = null;
|
||||||
private DataOutputStream out = null;
|
private DataOutputStream out = null;
|
||||||
private DataInputStream in = null;
|
private DataInputStream in = null;
|
||||||
|
|
@ -218,6 +198,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
private Processor processor;
|
private Processor processor;
|
||||||
private final Map<String, ResultListener> listeners;
|
private final Map<String, ResultListener> listeners;
|
||||||
private Integer _tag = 0;
|
private Integer _tag = 0;
|
||||||
|
private int timeout = ApiConnection.DEFAULT_COMMAND_TIMEOUT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* thread to read data from the socket and process it into Strings
|
* thread to read data from the socket and process it into Strings
|
||||||
|
|
@ -315,15 +296,15 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
return !lines.isEmpty() || !reader.isEmpty();
|
return !lines.isEmpty() || !reader.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response unpack() throws MikrotikApiException {
|
private Response unpack() throws MikrotikApiException {
|
||||||
if (line == null) {
|
if (line == null) {
|
||||||
nextLine();
|
nextLine();
|
||||||
|
|
@ -337,8 +318,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
return unpackError();
|
return unpackError();
|
||||||
case "!halt":
|
case "!halt":
|
||||||
return unpackError();
|
return unpackError();
|
||||||
case "" :
|
case "":
|
||||||
System.out.printf("sock.isClosed() = %s, sock.isInputShutdown() = %s\n", sock.isClosed(), sock.isInputShutdown());
|
|
||||||
default:
|
default:
|
||||||
throw new ApiDataException(String.format("Unexpected line '%s'", line));
|
throw new ApiDataException(String.format("Unexpected line '%s'", line));
|
||||||
}
|
}
|
||||||
|
|
@ -376,8 +356,8 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String unpackResult(String first )throws ApiConnectionException, ApiDataException {
|
private String unpackResult(String first) throws ApiConnectionException, ApiDataException {
|
||||||
StringBuilder buf = new StringBuilder(first);
|
StringBuilder buf = new StringBuilder(first);
|
||||||
line = null;
|
line = null;
|
||||||
|
|
||||||
|
|
@ -387,8 +367,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
nextLine();
|
nextLine();
|
||||||
buf.append("\n");
|
buf.append("\n");
|
||||||
buf.append(line);
|
buf.append(line);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -476,6 +455,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void completed() {
|
public synchronized void completed() {
|
||||||
|
complete = true;
|
||||||
notify();
|
notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -485,6 +465,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
res.put("ret", done.getHash());
|
res.put("ret", done.getHash());
|
||||||
results.add(res);
|
results.add(res);
|
||||||
}
|
}
|
||||||
|
complete = true;
|
||||||
notify();
|
notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -493,11 +474,17 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
results.add(result);
|
results.add(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Map<String, String>> getResults() throws MikrotikApiException {
|
private List<Map<String, String>> getResults(int timeout) throws MikrotikApiException {
|
||||||
try {
|
try {
|
||||||
synchronized (this) { // don't wait if we already have a result.
|
synchronized (this) { // don't wait if we already have a result.
|
||||||
if ((err == null) && results.isEmpty()) {
|
int waitTime = timeout;
|
||||||
wait();
|
while (!complete && (waitTime > 0)) {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
wait(waitTime);
|
||||||
|
waitTime = waitTime - (int)(System.currentTimeMillis() - start);
|
||||||
|
if ((waitTime <= 0) && !complete) {
|
||||||
|
err = new ApiConnectionException(String.format("Command timed out after %d ms", timeout));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
|
|
@ -508,7 +495,9 @@ public final class ApiConnectionImpl extends ApiConnection {
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final List<Map<String, String>> results = new LinkedList<>();
|
private final List<Map<String, String>> results = new LinkedList<>();
|
||||||
private MikrotikApiException err;
|
private MikrotikApiException err;
|
||||||
|
private boolean complete = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue