Compare commits
No commits in common. "master" and "2.0.0" have entirely different histories.
|
|
@ -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
|
||||
|
|
@ -1,4 +1,23 @@
|
|||
|
||||
# Java
|
||||
*.class
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# Scala
|
||||
*.class
|
||||
*.log
|
||||
# sbt specific
|
||||
dist/*
|
||||
target/
|
||||
lib_managed/
|
||||
src_managed/
|
||||
project/boot/
|
||||
project/plugins/project/
|
||||
# Scala-IDE specific
|
||||
.scala_dependencies
|
||||
|
||||
# Maven
|
||||
target/
|
||||
|
||||
|
|
@ -11,17 +30,16 @@ nbactions.xml
|
|||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
# Eclipse
|
||||
.settings/
|
||||
|
||||
# Project
|
||||
Mtik.java
|
||||
|
||||
# Jekyll
|
||||
_site/
|
||||
|
||||
.classpath
|
||||
|
||||
.project
|
||||
|
||||
# Maven
|
||||
pom.xml.versionsBackup
|
||||
|
||||
# MacOS
|
||||
.DS_Store
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
language: java
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
Thank you for considering contributing to this project. This library is one tiny way I am giving back to the open source community, and I am
|
||||
always amazed and humbled when users contribute back.
|
||||
|
||||
There are several ways of contributing to this small open source library:
|
||||
|
||||
* You can ask for help
|
||||
* You can log a bug
|
||||
* You can contribute code
|
||||
|
||||
# Ask for help
|
||||
If you experience a problem using the mikrotik-java library, the best way to get in touch with me is to either send me an
|
||||
email or open an issue on GitHub. While you can seek help on StackOverflow or the Mikrotik Forums (both of which I've seen
|
||||
people do), I do not read those sites often. If you wish to log your question there (it makes sense for it to be searchable or
|
||||
in a place where other interested people can find it) and want me to take a look at it, please send me an email pointing to
|
||||
the question.
|
||||
|
||||
I find that it is very easy to help users of this library if they include the following things which are usually quite relevant:
|
||||
|
||||
* A snipped of sample code showing what they're doing
|
||||
* The stack trace (if there is one) when it goes wrong
|
||||
* Version of the library they're using
|
||||
|
||||
# Log a bug
|
||||
If you are pretty sure you have found a bug in the library, please log a bug by opening an issue on GithHub. When things get buggy,
|
||||
the following information (mostly the same as for asking help) becomes important:
|
||||
|
||||
* A snipped of sample code showing what they're doing
|
||||
* The stack trace (if there is one) when it goes wrong
|
||||
* Version of the library they're using
|
||||
* The version of RouterOS they're connecting to
|
||||
|
||||
I take bug reports very seriously, and will usually bring out immediate minor versions to fix show stoppers.
|
||||
|
||||
# Contribute code
|
||||
If you want to contribute code to this library, you're welcome. Please read this section before submitting pull requests.
|
||||
|
||||
## If you're fixing a bug
|
||||
If you've fixed a bug, please submit the pull request on GitHub. If there is an already existing issue being fixed, please
|
||||
reference that issue so I can look at what you're fixing, and if not, please explain the issue you're fixing in the pull request.
|
||||
|
||||
## If you want new functionality or API changes
|
||||
Please contact me via email before you create a pull request for feature additions or API changes. The mikrotik-java library is stable and,
|
||||
for it's current purpose, feature complete. The situations in which I will add features to the library or change it's interface are:
|
||||
* If Mikrotik adds new RouterOS functionality that requires an API change to remain feature complete
|
||||
* If some very bad design flaw in the interface is discovered
|
||||
* If somebody has a truly compelling reason for adding to the library. To be compelling means the added functionality must be useful
|
||||
for more users than just the submitter
|
||||
* If some new pattern or convention arises in the Java standard libraries that is useful
|
||||
|
||||
I *really* hate declining people's PRs, but I will do so if I don't think they add value as explained above, or if I can't figure out why
|
||||
they want the change. For this reason, if you are at all unsure if your PR will be accepted, please contact me and discuss what you want
|
||||
before submitting a PR.
|
||||
|
||||
Gideon
|
||||
167
README.md
167
README.md
|
|
@ -1,119 +1,69 @@
|
|||
# mikrotik-java
|
||||
mikrotik-java
|
||||
=============
|
||||
|
||||
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.
|
||||
|
||||
## Versions
|
||||
Versions
|
||||
--------
|
||||
|
||||

|
||||
The current stable version is 2.0.0. This version is not backwards compatible with version 1.x.
|
||||
|
||||
**The current stable version is 3.0.8**
|
||||
* Changed ResultListener to receive errors and completion notifications.
|
||||
|
||||
Version 3.0.8 fixes a null pointer error
|
||||
Previous versions:
|
||||
|
||||
### Changes in version 3.0:
|
||||
* 1.1.5 fixed even more comamnd line parsing bugs.
|
||||
* 1.1.4 fixed command line parsing bugs #7 and #8.
|
||||
* 1.1.3 fixed severe command line parsing bugs, #3, #4 and #5.
|
||||
* 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.
|
||||
|
||||
Version 3.0 addresses the problems the API had around TLS encryption. The way secure connections are implemented is changed so that the user has complete control over the creation of TLS sockets. To this end:
|
||||
* A new method, `connect(SocketFactory fact, String host, int port, int timeout)`, was added to allow for better user control over sockets and especially encryption.
|
||||
* The `connectTLS()` API methods were removed.
|
||||
* Most of the overloaded `connect()` methods were removed.
|
||||
* Added a pre-built `jar` file to the downloads.
|
||||
The latest development version is 2.0.0 which is available using the 2.0.0 branch.
|
||||
|
||||
Further changes include:
|
||||
* The previously deprecated `disconnect()` method is removed.
|
||||
|
||||
#### Versions 1.x and 2.x
|
||||
Getting the API
|
||||
---------------
|
||||
|
||||
Versions 1 and 2 are considered *obsolete* and will no longer be supported or patched.
|
||||
You can clone or fork the repository. Alternatively, you can get the jar from Maven Central.
|
||||
|
||||
## Getting the API
|
||||
|
||||
Maven users can use the artifact from Maven Central with this dependency:
|
||||
Examples
|
||||
========
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>me.legrange</groupId>
|
||||
<artifactId>mikrotik</artifactId>
|
||||
<version>3.0.8</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
You can also download the pre-built jar file, or a zip or tar.gz file with the source for the latest release [here](https://github.com/GideonLeGrange/mikrotik-java/releases/latest)
|
||||
|
||||
## Asking for help or contributing
|
||||
|
||||
I welcome contributions, be it bug fixes or other improvements.
|
||||
|
||||
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for information on how to contribute to this project.
|
||||
|
||||
# Using the API
|
||||
|
||||
How to use the API is best illustrated by 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. The command line syntax gives you an indication of what commands you can pass, but the RouterOS API used by this library does not support everyting.
|
||||
|
||||
Some things to consider when debugging your API calls are:
|
||||
* The RouterOS API does not support auto-completion. You need to write out command and parameter names. For example, you can't say `/ip/hotspot/user/add name=john add=10.0.0.1`, you need to write out `address`.
|
||||
* You need to quote values with spaces in. You can't say `name=Joe Blogs`, you need to use `name="Joe Blogs"`
|
||||
* Exceptions with a root cause of `ApiCommandException` are errors received from the remote RouterOS device and contain the error message received.
|
||||
|
||||
## Opening a connection
|
||||
Here is a simple example: Connect to a router and reboot it.
|
||||
|
||||
```java
|
||||
ApiConnection con = ApiConnection.connect("10.0.1.1"); // connect to router
|
||||
con.login("admin","password"); // log in to router
|
||||
con.execute("/system/reboot"); // execute a command
|
||||
con.close(); // disconnect from router
|
||||
con.disconnect(); // disconnect from router
|
||||
```
|
||||
The above example shows a easy way of creating an unencrypted connection using the default API port and timeout, which is useful for development and testing.
|
||||
|
||||
### TLS encryption
|
||||
|
||||
For production environments, encrypting API traffic is recommended. To do this you need to open a TLS connection to the router by passing an instance of the `SocketFactory` you wish to use to construct the TLS socket to the API:
|
||||
If your router does not use the default API port, the port can be specified when connecting:
|
||||
|
||||
```java
|
||||
ApiConnection con = ApiConnection.connect(SSLSocketFactory.getDefault(), "10.0.1.1", ApiConnection.DEFAULT_TLS_PORT, ApiConnection.DEFAULT_CONNECTION_TIMEOUT);
|
||||
ApiConnection con = ApiConnection.connect("10.0.1.1", 1337); // connect to router on port 1337
|
||||
```
|
||||
|
||||
Above an instance of the default SSL socket factory is passed to the API. This will work as long as the router's certificate has been added to the local key store. Besides allowing the user to specify the socket factory, the above method also gives full control over the TCP Port and connection timeout.
|
||||
|
||||
RouterOS also supports anonymous TLS. An example showing how to create a socket factory for anonymous TLS is `AnonymousSocketFactory` in the examples directory.
|
||||
|
||||
### 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 OS 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()``` call. For example:
|
||||
To open an encrypted (TLS) connection is as simple, assuming the default API-SSL port is used:
|
||||
|
||||
```java
|
||||
ApiConnection con = ApiConnection.connect(SSLSocketFactory.getDefault(), "10.0.1.1", ApiConnection.DEFAULT_TLS_PORT, 2000); // connect to router on the default API port and fail in 2 seconds
|
||||
ApiConnection con = ApiConnection.connectTLS("10.0.1.1"); // connect to router using TLS
|
||||
```
|
||||
|
||||
### Constants
|
||||
Some constants are provided in `ApiConnection` to make it easier for users to construct connections with default ports and timeouts:
|
||||
#### Notes about TLS:
|
||||
* 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.
|
||||
|
||||
Constant | Use for | Value
|
||||
---------|---------|------
|
||||
DEFAULT_PORT | Default TCP `port` value for unencrypyted connections | 8728
|
||||
DEFAULT_TLS_PORT | Default TCP `port` value for encrypyted connections | 8729
|
||||
DEFAULT_CONNECTION_TIMEOUT | Default connection `timeout` value (ms) | 60000
|
||||
|
||||
### Try with resources
|
||||
|
||||
The API can also be used in a "try with resources" statement which will ensure that the connection is closed:
|
||||
In following examples the connection, login and disconnection code will not be repeated.
|
||||
|
||||
```java
|
||||
try (ApiConnection con = ApiConnection.connect(SocketFactory.getDefault(), Config.HOST, ApiConnection.DEFAULT_PORT, 2000)) {
|
||||
con.login(Config.USERNAME, Config.PASSWORD);
|
||||
con.execute("/user/add name=eric");
|
||||
}
|
||||
```
|
||||
Reading data
|
||||
------------
|
||||
|
||||
In following examples the connection, login and disconnection code will not be repeated. In all cases it is assumed that an `ApiConnection` has been established, `login()` has been called, and that the connection is called `con`.
|
||||
|
||||
## Reading data
|
||||
|
||||
A simple example that returns a result - Print all interfaces:
|
||||
A simple example that returns a result: Print all interfaces.
|
||||
|
||||
|
||||
```java
|
||||
|
|
@ -123,14 +73,6 @@ for (Map<String,String> r : rs) {
|
|||
}
|
||||
```
|
||||
|
||||
Results are returned as a list of maps of String key/value pairs. The reason for this is that a command can return multiple results, which have multpile variables. For example, to print the names of all the interfaces returned in the command above, do:
|
||||
|
||||
```java
|
||||
for (Map<String, String> map : rs) {
|
||||
System.out.println(map.get("name"));
|
||||
}
|
||||
```
|
||||
|
||||
### Filtering results
|
||||
|
||||
The same query, but with the results filtered: Print all interfaces of type 'vlan'.
|
||||
|
|
@ -141,13 +83,14 @@ List<Map<String, String>> rs = con.execute("/interface/print where type=vlan");
|
|||
|
||||
### Selecting returned fields
|
||||
|
||||
The same query, but we only want certain result fields names: Print all interfaces of type 'vlan' and return just their name:
|
||||
The same query, but we only want certain result fields names Print all interfaces of type 'vlan' and return their name.
|
||||
|
||||
```java
|
||||
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.
|
||||
|
||||
|
|
@ -175,20 +118,8 @@ And now remove the object:
|
|||
con.execute("/interface/gre/remove .id=gre1");
|
||||
```
|
||||
|
||||
### 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:
|
||||
|
||||
```java
|
||||
con.execute("/ip/firewall/filter/add action=accept chain=forward time=00:00:01-01,mon")
|
||||
```
|
||||
Assuming the rule can be accessed as `.id=*1`, you un-set it by using `value-name` as seen below:
|
||||
|
||||
```java
|
||||
con.execute("/ip/firewall/filter/unset .id=*1 value-name=time");
|
||||
```
|
||||
|
||||
## Asynchronous commands
|
||||
Asynchronous commands
|
||||
---------------------
|
||||
|
||||
We can run some commands asynchronously in order to continue receiving updates:
|
||||
|
||||
|
|
@ -214,37 +145,21 @@ String tag = con.execute("/interface/wireless/monitor .id=wlan1 return signal-to
|
|||
);
|
||||
```
|
||||
|
||||
The `ResultListener` interface has three methods the user needs to implement:
|
||||
* `receive()` is called to receive results produced by the router from the API.
|
||||
* `error()` is called when an exception is raised based on a 'trap' received from the router or another (typically connection) problem.
|
||||
* `completed()` is called when the router has indicated that the command has completed or has been cancelled.
|
||||
|
||||
The above command will run and send results asynchronously as they become available, until it is canceled. The command (identified by the unique String returned) is canceled like this:
|
||||
|
||||
```java
|
||||
con.cancel(tag);
|
||||
```
|
||||
|
||||
## Command timeouts
|
||||
From version 2.0.0 of the API the error() and completed() methods are part of the ResultListener interface.
|
||||
|
||||
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:
|
||||
|
||||
```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
|
||||
References
|
||||
==========
|
||||
|
||||
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
|
||||
|
||||
|
|
|
|||
294
pom.xml
294
pom.xml
|
|
@ -1,168 +1,134 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.sonatype.oss</groupId>
|
||||
<artifactId>oss-parent</artifactId>
|
||||
<version>9</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>me.legrange</groupId>
|
||||
<artifactId>mikrotik</artifactId>
|
||||
<version>3.0.8.1</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>Mikrotik API Java Client Library</name>
|
||||
<url>https://github.com/GideonLeGrange/mikrotik-java</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<url>git@github.com:GideonLeGrange/mikrotik-java.git</url>
|
||||
<connection>scm:git:git@github.com:GideonLeGrange/mikrotik-java.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:GideonLeGrange/mikrotik-java.git</developerConnection>
|
||||
</scm>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<configuration>
|
||||
<showDeprecation>true</showDeprecation>
|
||||
<source>8</source>
|
||||
<target>8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-jar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>**/examples</exclude>
|
||||
<exclude>**/examples/*</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2.1</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>3.7.0</version>
|
||||
<configuration>
|
||||
<excludePackageNames>example;*.impl</excludePackageNames>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<parent>
|
||||
<groupId>org.sonatype.oss</groupId>
|
||||
<artifactId>oss-parent</artifactId>
|
||||
<version>9</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>me.legrange</groupId>
|
||||
<artifactId>mikrotik</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Mikrotik API Java Client Library</name>
|
||||
<url>https://github.com/GideonLeGrange/mikrotik-java</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<url>git@github.com:GideonLeGrange/mikrotik-java.git</url>
|
||||
<connection>scm:git:git@github.com:GideonLeGrange/mikrotik-java.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:GideonLeGrange/mikrotik-java.git</developerConnection>
|
||||
</scm>
|
||||
<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>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<configuration>
|
||||
<showDeprecation>true</showDeprecation>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-jar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>**/examples</exclude>
|
||||
<exclude>**/examples/*</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2.1</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.9.1</version>
|
||||
<configuration>
|
||||
<excludePackageNames>example;*.impl</excludePackageNames>
|
||||
</configuration>
|
||||
<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>
|
||||
<executions>
|
||||
<execution>
|
||||
<configuration>
|
||||
<keyname>C27A8E67</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>
|
||||
</build>
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>ossrh</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
</profile>
|
||||
</profiles>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</build>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 gideon.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package examples;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @since 3.0
|
||||
* @author Gideon le Grange https://github.com/GideonLeGrange
|
||||
*/
|
||||
public class AnonymousSocketFactory extends SocketFactory {
|
||||
|
||||
@Override
|
||||
public Socket createSocket() throws IOException {
|
||||
return fixSocket((SSLSocket) SSLSocketFactory.getDefault().createSocket());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
|
||||
return fixSocket((SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
|
||||
return fixSocket((SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port, localHost, localPort));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress address, int port) throws IOException {
|
||||
return fixSocket((SSLSocket) SSLSocketFactory.getDefault().createSocket(address, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
|
||||
return fixSocket((SSLSocket) SSLSocketFactory.getDefault().createSocket(address, port, localAddress, localPort));
|
||||
}
|
||||
|
||||
private Socket fixSocket(SSLSocket ssl) {
|
||||
List<String> cs = new LinkedList<>();
|
||||
// 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
|
||||
for (String s : ssl.getSupportedCipherSuites()) {
|
||||
if (s.startsWith("TLS_DH_anon")) {
|
||||
cs.add(s);
|
||||
}
|
||||
}
|
||||
ssl.setEnabledCipherSuites(cs.toArray(new String[]{}));
|
||||
return ssl;
|
||||
}
|
||||
|
||||
public static SocketFactory getDefault() {
|
||||
if (fact == null) {
|
||||
fact = new AnonymousSocketFactory();
|
||||
}
|
||||
return fact;
|
||||
}
|
||||
|
||||
private AnonymousSocketFactory() {
|
||||
|
||||
}
|
||||
|
||||
private static AnonymousSocketFactory fact;
|
||||
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package examples;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import me.legrange.mikrotik.MikrotikApiException;
|
||||
|
||||
/**
|
||||
* Example to show that different character sets may work some times.
|
||||
*
|
||||
* @author gideon
|
||||
*/
|
||||
public class CharacterSets extends Example {
|
||||
|
||||
private static final String JAPANESE = "事報ハヤ久送とゅ歳用ト候新放すルドう二5春園ユヲロ然納レ部悲と被状クヘ芸一ーあぽだ野健い産隊ず";
|
||||
private static final String CRYLLIC = "Лорем ипсум долор сит амет, легере елояуентиам хис ид. Елигенди нолуиссе вих ут. Нихил";
|
||||
private static final String ARABIC = "تجهيز والمانيا تم قام. وحتّى المتاخمة ما وقد. أسر أمدها تكبّد عل. فقد بسبب ترتيب استدعى أم, مما مع غرّة، لأداء. الشتاء، عسكرياً";
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
CharacterSets ex = new CharacterSets();
|
||||
ex.connect();
|
||||
ex.test();
|
||||
ex.disconnect();
|
||||
}
|
||||
|
||||
private void test() throws MikrotikApiException {
|
||||
con.execute("/ip/hotspot/user/add name=userJ comment='" + JAPANESE + "'");
|
||||
con.execute("/ip/hotspot/user/add name=userC comment='" + CRYLLIC + "'");
|
||||
con.execute("/ip/hotspot/user/add name=userA comment='" + ARABIC + "'");
|
||||
|
||||
for (Map<String, String> res : con.execute("/ip/hotspot/user/print return name,comment")) {
|
||||
System.out.printf("%s : %s\n", res.get("name"), res.get("comment"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,14 @@ package examples;
|
|||
|
||||
/**
|
||||
* Config class for examples
|
||||
*
|
||||
* @author gideon
|
||||
*/
|
||||
public class Config {
|
||||
|
||||
public static final String HOST = "ce2.ter.cpt.adept.za.net";
|
||||
public static final String USERNAME = "adept";
|
||||
public static final String PASSWORD = "34ffp9";
|
||||
|
||||
public static final String HOST = "10.0.1.1";
|
||||
public static final String USERNAME = "admin";
|
||||
public static final String PASSWORD = "";
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 gideon.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package examples;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import me.legrange.mikrotik.ApiConnection;
|
||||
import me.legrange.mikrotik.MikrotikApiException;
|
||||
|
||||
/**
|
||||
* Example: Open an Anonymous TLS connection
|
||||
*
|
||||
* @author gideon
|
||||
*/
|
||||
public class ConnectTLSAnonymous {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
ConnectTLSAnonymous ex = new ConnectTLSAnonymous();
|
||||
ex.connect();
|
||||
ex.test();
|
||||
ex.disconnect();
|
||||
}
|
||||
|
||||
private void test() throws MikrotikApiException {
|
||||
List<Map<String, String>> results = con.execute("/interface/print");
|
||||
for (Map<String, String> result : results) {
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
|
||||
protected void connect() throws Exception {
|
||||
con = ApiConnection.connect(AnonymousSocketFactory.getDefault(), Config.HOST, ApiConnection.DEFAULT_TLS_PORT, ApiConnection.DEFAULT_CONNECTION_TIMEOUT);
|
||||
con.login(Config.USERNAME, Config.PASSWORD);
|
||||
}
|
||||
|
||||
protected void disconnect() throws Exception {
|
||||
con.close();
|
||||
}
|
||||
|
||||
private ApiConnection con;
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 gideon.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package examples;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import me.legrange.mikrotik.ApiConnection;
|
||||
import me.legrange.mikrotik.MikrotikApiException;
|
||||
|
||||
/**
|
||||
* Example: Open a TLS connection
|
||||
*
|
||||
* @author gideon
|
||||
*/
|
||||
public class ConnectTLSCertificate {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
ConnectTLSCertificate ex = new ConnectTLSCertificate();
|
||||
ex.connect();
|
||||
ex.test();
|
||||
ex.disconnect();
|
||||
}
|
||||
|
||||
private void test() throws MikrotikApiException {
|
||||
List<Map<String, String>> results = con.execute("/interface/print");
|
||||
for (Map<String, String> result : results) {
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
|
||||
protected void connect() throws Exception {
|
||||
con = ApiConnection.connect(SSLSocketFactory.getDefault(), Config.HOST, ApiConnection.DEFAULT_TLS_PORT, ApiConnection.DEFAULT_CONNECTION_TIMEOUT);
|
||||
con.login(Config.USERNAME, Config.PASSWORD);
|
||||
}
|
||||
|
||||
protected void disconnect() throws Exception {
|
||||
con.close();
|
||||
}
|
||||
|
||||
private ApiConnection con;
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package examples;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import me.legrange.mikrotik.ApiConnection;
|
||||
|
||||
/**
|
||||
|
|
@ -10,12 +9,12 @@ import me.legrange.mikrotik.ApiConnection;
|
|||
abstract class Example {
|
||||
|
||||
protected void connect() throws Exception {
|
||||
con = ApiConnection.connect(SocketFactory.getDefault(), Config.HOST, ApiConnection.DEFAULT_PORT, 2000);
|
||||
con = ApiConnection.connect(Config.HOST);
|
||||
con.login(Config.USERNAME, Config.PASSWORD);
|
||||
}
|
||||
|
||||
protected void disconnect() throws Exception {
|
||||
con.close();
|
||||
con.disconnect();
|
||||
}
|
||||
|
||||
protected ApiConnection con;
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import me.legrange.mikrotik.MikrotikApiException;
|
|||
* Example 1: A very simple command: Reboot the remote router
|
||||
* @author gideon
|
||||
*/
|
||||
public class SimpleCommand extends Example {
|
||||
public class Example1 extends Example {
|
||||
|
||||
public static void main(String...args) throws Exception {
|
||||
SimpleCommand ex = new SimpleCommand();
|
||||
Example1 ex = new Example1();
|
||||
ex.connect();
|
||||
ex.test();
|
||||
ex.disconnect();
|
||||
|
|
@ -9,10 +9,10 @@ import me.legrange.mikrotik.MikrotikApiException;
|
|||
*
|
||||
* @author gideon
|
||||
*/
|
||||
public class SimpleCommandWithResults extends Example {
|
||||
public class Example2 extends Example {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
SimpleCommandWithResults ex = new SimpleCommandWithResults();
|
||||
Example2 ex = new Example2();
|
||||
ex.connect();
|
||||
ex.test();
|
||||
ex.disconnect();
|
||||
|
|
@ -9,10 +9,10 @@ import me.legrange.mikrotik.MikrotikApiException;
|
|||
*
|
||||
* @author gideon
|
||||
*/
|
||||
public class CommandWithWhere extends Example {
|
||||
public class Example3 extends Example {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
CommandWithWhere ex = new CommandWithWhere();
|
||||
Example3 ex = new Example3();
|
||||
ex.connect();
|
||||
ex.test();
|
||||
ex.disconnect();
|
||||
|
|
@ -9,10 +9,10 @@ import me.legrange.mikrotik.ResultListener;
|
|||
*
|
||||
* @author gideon
|
||||
*/
|
||||
public class AsyncCommand extends Example {
|
||||
public class Example4 extends Example {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
AsyncCommand ex = new AsyncCommand();
|
||||
Example4 ex = new Example4();
|
||||
ex.connect();
|
||||
ex.test();
|
||||
ex.disconnect();
|
||||
|
|
@ -9,10 +9,10 @@ import me.legrange.mikrotik.ResultListener;
|
|||
*
|
||||
* @author gideon
|
||||
*/
|
||||
public class AsyncWithErrorHandling extends Example {
|
||||
public class Example5 extends Example {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
AsyncWithErrorHandling ex = new AsyncWithErrorHandling();
|
||||
Example5 ex = new Example5();
|
||||
ex.connect();
|
||||
ex.test();
|
||||
ex.disconnect();
|
||||
|
|
@ -23,7 +23,6 @@ public class AsyncWithErrorHandling extends Example {
|
|||
String id = con.execute("/interface/wireless/monitor .id=wlan1", new ResultListener() {
|
||||
private int prev = 0;
|
||||
|
||||
@Override
|
||||
public void receive(Map<String, String> result) {
|
||||
int val = Integer.parseInt(result.get("signal-strength"));
|
||||
String sym = (val == prev) ? " " : ((val < prev) ? "-" : "+");
|
||||
|
|
@ -31,13 +30,11 @@ public class AsyncWithErrorHandling extends Example {
|
|||
prev = val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(MikrotikApiException ex) {
|
||||
System.out.printf("An error ocurred: %s\n", ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completed() {
|
||||
System.out.printf("The request has been completed\n");
|
||||
}
|
||||
|
|
@ -7,10 +7,10 @@ import me.legrange.mikrotik.MikrotikApiException;
|
|||
*
|
||||
* @author gideon
|
||||
*/
|
||||
public class AddAndModify extends Example {
|
||||
public class Example6 extends Example {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
AddAndModify ex = new AddAndModify();
|
||||
Example6 ex = new Example6();
|
||||
ex.connect();
|
||||
ex.test();
|
||||
ex.disconnect();
|
||||
|
|
@ -9,10 +9,10 @@ import me.legrange.mikrotik.MikrotikApiException;
|
|||
*
|
||||
* @author gideon
|
||||
*/
|
||||
public class DownloadConfig extends Example {
|
||||
public class Example7 extends Example {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
DownloadConfig ex = new DownloadConfig();
|
||||
Example7 ex = new Example7();
|
||||
ex.connect();
|
||||
ex.test();
|
||||
ex.disconnect();
|
||||
|
|
@ -21,9 +21,9 @@ public class DownloadConfig extends Example {
|
|||
private void test() throws MikrotikApiException, InterruptedException {
|
||||
con.execute("/export file=conf");
|
||||
List<Map<String, String>> res = con.execute("/file/print detail where name=conf.rsc");
|
||||
con.execute("/file/remove .id=conf.rsc");
|
||||
con.execute("/file/remove conf.rsc");
|
||||
String text = res.get(0).get("contents");
|
||||
for (String line : text.split("\r")) {
|
||||
for (String line : text.split("\r")) {
|
||||
System.out.println(line);
|
||||
}
|
||||
}
|
||||
|
|
@ -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,25 +0,0 @@
|
|||
package examples;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import me.legrange.mikrotik.ApiConnection;
|
||||
import me.legrange.mikrotik.MikrotikApiException;
|
||||
|
||||
/**
|
||||
* Example 9: Try with resources
|
||||
*
|
||||
* @author gideon
|
||||
*/
|
||||
public class TryWithResources {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
TryWithResources ex = new TryWithResources();
|
||||
ex.test();
|
||||
}
|
||||
|
||||
private void test() throws MikrotikApiException, InterruptedException {
|
||||
try (ApiConnection con = ApiConnection.connect(SocketFactory.getDefault(), Config.HOST, ApiConnection.DEFAULT_PORT, 2000)) {
|
||||
con.login(Config.USERNAME, Config.PASSWORD);
|
||||
con.execute("/user/add name=eric");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package me.legrange.mikrotik;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.net.SocketFactory;
|
||||
import me.legrange.mikrotik.impl.ApiConnectionImpl;
|
||||
|
||||
/**
|
||||
|
|
@ -11,53 +10,46 @@ import me.legrange.mikrotik.impl.ApiConnectionImpl;
|
|||
*
|
||||
* @author GideonLeGrange
|
||||
*/
|
||||
public abstract class ApiConnection implements AutoCloseable {
|
||||
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
|
||||
* the supplied socket factory to create the socket.
|
||||
*
|
||||
* @param fact SocketFactory to use for TCP socket creation.
|
||||
* 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
|
||||
* @since 3.0
|
||||
*/
|
||||
public static ApiConnection connect(SocketFactory fact, String host, int port, int timeout) throws MikrotikApiException {
|
||||
return ApiConnectionImpl.connect(fact, host, port, timeout);
|
||||
public static ApiConnection connectTLS(String host, int port) throws MikrotikApiException {
|
||||
return ApiConnectionImpl.connect(host, port, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new API connection to the give device on the default API port, using anonymous TLS for encryption.
|
||||
* @param host The host to which to connect.
|
||||
* @return The ApiConnection
|
||||
*/
|
||||
public static ApiConnection connectTLS(String host) throws MikrotikApiException {
|
||||
return ApiConnectionImpl.connect(host, DEFAULT_TLS_PORT, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return The ApiConnection
|
||||
*/
|
||||
public static ApiConnection connect(String host, int port) throws MikrotikApiException {
|
||||
return ApiConnectionImpl.connect(host, port, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new API connection to the give device on the default API port.
|
||||
*
|
||||
* @param host The host to which to connect.
|
||||
* @return The ApiConnection
|
||||
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if there is a
|
||||
* problem connecting
|
||||
*/
|
||||
public static ApiConnection connect(String host) throws MikrotikApiException {
|
||||
return connect(SocketFactory.getDefault(), host, DEFAULT_PORT, DEFAULT_COMMAND_TIMEOUT);
|
||||
public static ApiConnection connect(String host) throws MikrotikApiException {
|
||||
return connect(host, DEFAULT_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -67,65 +59,40 @@ public abstract class ApiConnection implements AutoCloseable {
|
|||
*/
|
||||
public abstract boolean isConnected();
|
||||
|
||||
/**
|
||||
* Disconnect from the remote API
|
||||
*/
|
||||
public abstract void disconnect() throws MikrotikApiException;
|
||||
|
||||
/**
|
||||
* 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 Thrown if the API encounters an error on login.
|
||||
*/
|
||||
public abstract void login(String username, String password) throws MikrotikApiException;
|
||||
public abstract void login(String username, String password) throws MikrotikApiException, InterruptedException;
|
||||
|
||||
/**
|
||||
* execute a command and return a list of results.
|
||||
*
|
||||
/** execute a command and return a list of results.
|
||||
* @param cmd Command to execute
|
||||
* @return The list of results
|
||||
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if the API encounters an error executing a command.
|
||||
*/
|
||||
public abstract List<Map<String, String>> execute(String cmd) throws MikrotikApiException;
|
||||
|
||||
/**
|
||||
* execute a command and attach a result listener to receive it's results.
|
||||
/** 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 me.legrange.mikrotik.MikrotikApiException Thrown if the API encounters an error executing a command.
|
||||
* @throws MikrotikApiException
|
||||
*/
|
||||
public abstract String execute(String cmd, ResultListener lis) throws MikrotikApiException;
|
||||
|
||||
/**
|
||||
* cancel a command
|
||||
*
|
||||
* @param tag The tag of the command to cancel
|
||||
* @throws me.legrange.mikrotik.MikrotikApiException Thrown if there is a
|
||||
* problem cancelling the command
|
||||
*/
|
||||
/** cancel a command */
|
||||
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()
|
||||
* 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.
|
||||
* @since 2.1
|
||||
*/
|
||||
public abstract void setTimeout(int timeout) throws MikrotikApiException;
|
||||
|
||||
/**
|
||||
* Disconnect from the remote API
|
||||
*
|
||||
* @throws me.legrange.mikrotik.ApiConnectionException Thrown if there is a
|
||||
* problem closing the connection.
|
||||
* @since 2.2
|
||||
*/
|
||||
@Override
|
||||
public abstract void close() throws ApiConnectionException;
|
||||
/** 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;
|
||||
|
||||
}
|
||||
|
|
@ -6,20 +6,10 @@ package me.legrange.mikrotik;
|
|||
*/
|
||||
public class ApiConnectionException extends MikrotikApiException {
|
||||
|
||||
/**
|
||||
* Create a new exception.
|
||||
*
|
||||
* @param msg The message
|
||||
*/
|
||||
public ApiConnectionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new exception
|
||||
* @param msg The message
|
||||
* @param err The underlying cause
|
||||
*/
|
||||
public ApiConnectionException(String msg, Throwable err) {
|
||||
super(msg, err);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,21 +7,10 @@ package me.legrange.mikrotik;
|
|||
*/
|
||||
public class MikrotikApiException extends Exception {
|
||||
|
||||
|
||||
/**
|
||||
* Create a new exception
|
||||
* @param msg The message
|
||||
*/
|
||||
public MikrotikApiException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new exception
|
||||
* @param msg The message
|
||||
* @param err The underlying cause
|
||||
*/
|
||||
public MikrotikApiException(String msg, Throwable err) {
|
||||
super(msg, err);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
command = action [ query ] [ return ]
|
||||
action = ("/" word)+
|
||||
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
|
||||
hasExpr = name
|
||||
eqExpr = name "=" value
|
||||
lessExpr = name "<" value
|
||||
moreExpr = name ">" value
|
||||
notExpr = name "!=" value
|
||||
nestedExpr = "(" expr ")"
|
||||
return = "return" (name)+
|
||||
|
||||
|
||||
|
|
@ -4,40 +4,30 @@ import me.legrange.mikrotik.MikrotikApiException;
|
|||
|
||||
/**
|
||||
* Thrown when the Mikrotik returns an error when receiving our command.
|
||||
*
|
||||
* @author GideonLeGrange
|
||||
*/
|
||||
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. Null if there is no tag
|
||||
*/
|
||||
/** return the tag associated with this exception, if there is one */
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
ApiCommandException(String msg) {
|
||||
ApiCommandException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
ApiCommandException(String msg, Throwable err) {
|
||||
ApiCommandException(String msg, Throwable err) {
|
||||
super(msg, err);
|
||||
}
|
||||
|
||||
public int getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
ApiCommandException(Error err) {
|
||||
super(err.getMessage());
|
||||
tag = err.getTag();
|
||||
category = err.getCategory();
|
||||
}
|
||||
|
||||
private String tag = null;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,22 @@
|
|||
package me.legrange.mikrotik.impl;
|
||||
|
||||
import me.legrange.mikrotik.*;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.net.SocketFactory;
|
||||
import me.legrange.mikrotik.ApiConnection;
|
||||
import me.legrange.mikrotik.ApiConnectionException;
|
||||
import me.legrange.mikrotik.MikrotikApiException;
|
||||
import me.legrange.mikrotik.ResultListener;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
//import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
/**
|
||||
* The Mikrotik API connection implementation. This is the class used to connect
|
||||
|
|
@ -32,90 +29,97 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
/**
|
||||
* Create a new API connection to the give device on the supplied port
|
||||
*
|
||||
* @param fact The socket factory used to construct the connection socket.
|
||||
* @param host The host to which to connect.
|
||||
* @param port The TCP port to use.
|
||||
* @param timeOut The connection timeout
|
||||
* @param secure Is TLS required
|
||||
* @return The ApiConnection
|
||||
* @throws me.legrange.mikrotik.ApiConnectionException Thrown if there is a
|
||||
* problem connecting
|
||||
*/
|
||||
public static ApiConnection connect(SocketFactory fact, String host, int port, int timeOut) throws ApiConnectionException {
|
||||
public static ApiConnection connect(String host, int port, boolean secure) throws ApiConnectionException {
|
||||
ApiConnectionImpl con = new ApiConnectionImpl();
|
||||
con.open(host, port, fact, timeOut);
|
||||
con.open(host, port, secure);
|
||||
return con;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the state of connection.
|
||||
*
|
||||
* @return if connection is established to router it returns true.
|
||||
*/
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the remote API
|
||||
*
|
||||
* @throws me.legrange.mikrotik.ApiConnectionException Thrown if there is a
|
||||
* problem disconnecting
|
||||
*/
|
||||
@Override
|
||||
public void login(String username, String password) throws MikrotikApiException {
|
||||
if (username.trim().isEmpty()) {
|
||||
throw new ApiConnectionException("API username cannot be empty");
|
||||
}
|
||||
Command cmd = new Command("/login");
|
||||
cmd.addParameter("name", username);
|
||||
cmd.addParameter("password", password);
|
||||
List<Map<String, String>> list = execute(cmd, timeout);
|
||||
if (!list.isEmpty()) {
|
||||
Map<String, String> res = list.get(0);
|
||||
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
|
||||
public List<Map<String, String>> execute(String cmd) throws MikrotikApiException {
|
||||
return execute(Parser.parse(cmd), timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(String cmd, ResultListener lis) throws MikrotikApiException {
|
||||
return execute(Parser.parse(cmd), lis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(String tag) throws MikrotikApiException {
|
||||
execute(String.format("/cancel tag=%s", tag));
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws ApiConnectionException {
|
||||
public void disconnect() throws ApiConnectionException {
|
||||
if (!connected) {
|
||||
throw new ApiConnectionException(("Not/no longer connected to remote Mikrotik"));
|
||||
}
|
||||
connected = false;
|
||||
processor.interrupt();
|
||||
reader.interrupt();
|
||||
try {
|
||||
in.close();
|
||||
out.close();
|
||||
sock.close();
|
||||
} catch (IOException ex) {
|
||||
throw new ApiConnectionException(String.format("Error closing socket: %s", ex.getMessage()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Map<String, String>> execute(Command cmd, int timeout) throws MikrotikApiException {
|
||||
/**
|
||||
* Log in to the remote router.
|
||||
*
|
||||
* @param username - username of the user on the router
|
||||
* @param password - password for the user
|
||||
*/
|
||||
public void login(String username, String password) throws MikrotikApiException, InterruptedException {
|
||||
List<Map<String, String>> list = execute("/login");
|
||||
Map<String, String> res = list.get(0);
|
||||
String hash = res.get("ret");
|
||||
String chal = Util.hexStrToStr("00") + new String(makePass(password)) + Util.hexStrToStr(hash);
|
||||
chal = Util.hashMD5(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
|
||||
*/
|
||||
public List<Map<String, String>> execute(String cmd) throws MikrotikApiException {
|
||||
return execute(Parser.parse(cmd));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public String execute(String cmd, ResultListener lis) throws MikrotikApiException {
|
||||
return execute(Parser.parse(cmd), lis);
|
||||
}
|
||||
|
||||
/**
|
||||
* cancel a command
|
||||
*/
|
||||
public void cancel(String tag) throws MikrotikApiException {
|
||||
execute(String.format("/cancel tag=%s", tag));
|
||||
}
|
||||
|
||||
private List<Map<String, String>> execute(Command cmd) throws MikrotikApiException {
|
||||
SyncListener l = new SyncListener();
|
||||
execute(cmd, l);
|
||||
return l.getResults(timeout);
|
||||
return l.getResults();
|
||||
}
|
||||
|
||||
private String execute(Command cmd, ResultListener lis) throws MikrotikApiException {
|
||||
|
|
@ -133,17 +137,26 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
}
|
||||
|
||||
private ApiConnectionImpl() {
|
||||
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
|
||||
*/
|
||||
private void open(String host, int port, SocketFactory fact, int conTimeout) throws ApiConnectionException {
|
||||
private void open(String host, int port, boolean secure) throws ApiConnectionException {
|
||||
try {
|
||||
InetAddress ia = InetAddress.getByName(host.trim());
|
||||
sock = fact.createSocket();
|
||||
sock.connect(new InetSocketAddress(ia, port), conTimeout);
|
||||
InetAddress ia = InetAddress.getByName(host);
|
||||
if (secure) {
|
||||
sock = openSSLSocket(ia, port);
|
||||
} else {
|
||||
sock = new Socket(ia, port);
|
||||
}
|
||||
in = new DataInputStream(sock.getInputStream());
|
||||
out = new DataOutputStream(sock.getOutputStream());
|
||||
connected = true;
|
||||
|
|
@ -162,31 +175,54 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
}
|
||||
}
|
||||
|
||||
private synchronized String nextTag() {
|
||||
return Integer.toHexString(_tag.incrementAndGet());
|
||||
/**
|
||||
* open and configure a SSL socket.
|
||||
*/
|
||||
private Socket openSSLSocket(InetAddress ia, int port) throws IOException {
|
||||
SSLSocket ssl = (SSLSocket) SSLSocketFactory.getDefault().createSocket(ia, port);
|
||||
List<String> cs = new LinkedList<String>();
|
||||
// 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
|
||||
for (String s : ssl.getSupportedCipherSuites()) {
|
||||
if (s.startsWith("TLS_DH_anon")) {
|
||||
cs.add(s);
|
||||
}
|
||||
}
|
||||
ssl.setEnabledCipherSuites(cs.toArray(new String[]{}));
|
||||
return ssl;
|
||||
}
|
||||
|
||||
private char[] makePass(String pass) {
|
||||
if (true) {
|
||||
return pass.toCharArray();
|
||||
}
|
||||
char[] res = new char[pass.length() + 1];
|
||||
System.arraycopy(pass.toCharArray(), 0, res, 0, pass.length());
|
||||
res[pass.length()] = 0x0;
|
||||
return res;
|
||||
}
|
||||
|
||||
private synchronized String nextTag() {
|
||||
_tag++;
|
||||
return Integer.toHexString(_tag);
|
||||
}
|
||||
private static final int DEFAULT_PORT = 8728;
|
||||
private Socket sock = null;
|
||||
private DataOutputStream out = null;
|
||||
private DataInputStream in = null;
|
||||
private boolean connected = false;
|
||||
private Reader reader;
|
||||
private Processor processor;
|
||||
private final Map<String, ResultListener> listeners;
|
||||
private final AtomicInteger _tag = new AtomicInteger(0);
|
||||
private int timeout = ApiConnection.DEFAULT_COMMAND_TIMEOUT;
|
||||
private final Map<String, ResultListener> listeners = new HashMap<String, ResultListener>();
|
||||
private Integer _tag = 0;
|
||||
|
||||
/**
|
||||
* thread to read data from the socket and process it into Strings
|
||||
*/
|
||||
private class Reader extends Thread {
|
||||
|
||||
private Reader() {
|
||||
super("Mikrotik API Reader");
|
||||
}
|
||||
|
||||
private String take() throws ApiConnectionException, ApiDataException {
|
||||
Object val;
|
||||
Object val = null;
|
||||
try {
|
||||
val = queue.take();
|
||||
} catch (InterruptedException ex) {
|
||||
|
|
@ -209,25 +245,20 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
while (connected) {
|
||||
try {
|
||||
String s = Util.decode(in);
|
||||
put(s);
|
||||
} catch (ApiDataException ex) {
|
||||
put(ex);
|
||||
} catch (ApiConnectionException ex) {
|
||||
if (connected || !sock.isClosed()) {
|
||||
put(ex);
|
||||
if (s != null) {
|
||||
queue.put(s);
|
||||
}
|
||||
} catch (ApiDataException ex) {
|
||||
try {
|
||||
queue.put(ex);
|
||||
} catch (InterruptedException ex2) {
|
||||
}
|
||||
} catch (ApiConnectionException ex) {
|
||||
} catch (InterruptedException ex1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void put(Object data) {
|
||||
try {
|
||||
queue.put(data);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private final LinkedBlockingQueue queue = new LinkedBlockingQueue(40);
|
||||
private LinkedBlockingQueue queue = new LinkedBlockingQueue(40);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -235,10 +266,6 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
*/
|
||||
private class Processor extends Thread {
|
||||
|
||||
private Processor() {
|
||||
super("Mikrotik API Result Processor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (connected) {
|
||||
|
|
@ -248,31 +275,28 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
} catch (ApiCommandException ex) {
|
||||
String tag = ex.getTag();
|
||||
if (tag != null) {
|
||||
res = new Error(tag, ex.getMessage(), ex.getCategory());
|
||||
res = new Error(tag, ex.getMessage());
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} catch (MikrotikApiException ex) {
|
||||
ex.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
if (res.getTag() != null) {
|
||||
ResultListener l = listeners.get(res.getTag());
|
||||
if (l != null) {
|
||||
if (res instanceof Result) {
|
||||
l.receive((Result) res);
|
||||
} else if (res instanceof Done) {
|
||||
if (l instanceof SyncListener) {
|
||||
((SyncListener) l).completed((Done) res);
|
||||
} else {
|
||||
l.completed();
|
||||
}
|
||||
listeners.remove(res.getTag());
|
||||
} else if (res instanceof Error) {
|
||||
l.error(new ApiCommandException((Error) res));
|
||||
ResultListener l = listeners.get(res.getTag());
|
||||
if (l != null) {
|
||||
if (res instanceof Result) {
|
||||
l.receive((Result) res);
|
||||
} else if (res instanceof Done) {
|
||||
if (l instanceof SyncListener) {
|
||||
((SyncListener) l).completed((Done) res);
|
||||
} else {
|
||||
l.completed();
|
||||
}
|
||||
listeners.remove(res.getTag());
|
||||
} else if (res instanceof Error) {
|
||||
l.error(new ApiCommandException((Error) res));
|
||||
}
|
||||
} else {
|
||||
nextTag();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -280,7 +304,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
private void nextLine() throws ApiConnectionException, ApiDataException {
|
||||
if (lines.isEmpty()) {
|
||||
String block = reader.take();
|
||||
String[] parts = block.split("\n");
|
||||
String parts[] = block.split("\n");
|
||||
lines.addAll(Arrays.asList(parts));
|
||||
}
|
||||
line = lines.remove(0);
|
||||
|
|
@ -290,15 +314,6 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
return !lines.isEmpty() || !reader.isEmpty();
|
||||
}
|
||||
|
||||
private String peekLine() throws ApiConnectionException, ApiDataException {
|
||||
if (lines.isEmpty()) {
|
||||
String block = reader.take();
|
||||
String[] parts = block.split("\n");
|
||||
lines.addAll(Arrays.asList(parts));
|
||||
}
|
||||
return lines.get(0);
|
||||
}
|
||||
|
||||
private Response unpack() throws MikrotikApiException {
|
||||
if (line == null) {
|
||||
nextLine();
|
||||
|
|
@ -306,13 +321,12 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
switch (line) {
|
||||
case "!re":
|
||||
return unpackRe();
|
||||
case "!empty":
|
||||
case "!done":
|
||||
return unpackDone();
|
||||
case "!trap":
|
||||
return unpackError();
|
||||
case "!halt":
|
||||
return unpackError();
|
||||
case "":
|
||||
default:
|
||||
throw new ApiDataException(String.format("Unexpected line '%s'", line));
|
||||
}
|
||||
|
|
@ -320,27 +334,23 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
|
||||
private Result unpackRe() throws ApiDataException, ApiConnectionException {
|
||||
nextLine();
|
||||
int l = 0;
|
||||
Result res = new Result();
|
||||
while (!line.startsWith(("!"))) {
|
||||
l++;
|
||||
if (line.startsWith(("="))) {
|
||||
String[] parts = line.split("=", 3);
|
||||
String parts[] = line.split("=", 3);
|
||||
if (parts.length == 3) {
|
||||
if (!parts[2].endsWith("\r")) {
|
||||
res.put(parts[1], unpackResult(parts[2]));
|
||||
res.put(parts[1], parts[2]);
|
||||
} else {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(parts[2]);
|
||||
while (!lines.isEmpty()) {
|
||||
nextLine();
|
||||
sb.append(line);
|
||||
}
|
||||
res.put(parts[1], sb.toString());
|
||||
res.put(parts[1], unpackMultiLine(parts[2]));
|
||||
}
|
||||
} else {
|
||||
throw new ApiDataException(String.format("Malformed line '%s'", line));
|
||||
}
|
||||
} else if (line.startsWith(".tag=")) {
|
||||
String[] parts = line.split("=", 2);
|
||||
String parts[] = line.split("=", 2);
|
||||
if (parts.length == 2) {
|
||||
res.setTag(parts[1]);
|
||||
}
|
||||
|
|
@ -357,20 +367,12 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
return res;
|
||||
}
|
||||
|
||||
private String unpackResult(String first) throws ApiConnectionException, ApiDataException {
|
||||
StringBuilder buf = new StringBuilder(first);
|
||||
line = null;
|
||||
|
||||
while (hasNextLine()) {
|
||||
String peek = peekLine();
|
||||
if (!(peek.startsWith("!") || peek.startsWith("=") || peek.startsWith(".tag="))) {
|
||||
nextLine();
|
||||
buf.append("\n");
|
||||
buf.append(line);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
private String unpackMultiLine(String head) throws ApiConnectionException, ApiDataException {
|
||||
StringBuilder buf = new StringBuilder(head);
|
||||
do {
|
||||
nextLine();
|
||||
buf.append(line);
|
||||
} while (line.endsWith("\r"));
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
|
@ -381,12 +383,12 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
|
||||
while (!line.startsWith("!")) {
|
||||
if (line.startsWith(".tag=")) {
|
||||
String[] parts = line.split("=", 2);
|
||||
String parts[] = line.split("=", 2);
|
||||
if (parts.length == 2) {
|
||||
done.setTag(parts[1]);
|
||||
}
|
||||
} else if (line.startsWith(("=ret"))) {
|
||||
String[] parts = line.split("=", 3);
|
||||
String parts[] = line.split("=", 3);
|
||||
if (parts.length == 3) {
|
||||
done.setHash(parts[2]);
|
||||
} else {
|
||||
|
|
@ -410,16 +412,13 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
if (hasNextLine()) {
|
||||
while (!line.startsWith("!")) {
|
||||
if (line.startsWith(".tag=")) {
|
||||
String[] parts = line.split("=", 2);
|
||||
String parts[] = line.split("=", 2);
|
||||
if (parts.length == 2) {
|
||||
err.setTag(parts[1]);
|
||||
}
|
||||
} else if (line.startsWith("=message=")) {
|
||||
err.setMessage(line.split("=", 3)[2]);
|
||||
}
|
||||
else if (line.startsWith("=category=")) {
|
||||
err.setCategory(Integer.parseInt(line.split("=", 3)[2]));
|
||||
}
|
||||
if (hasNextLine()) {
|
||||
nextLine();
|
||||
} else {
|
||||
|
|
@ -431,22 +430,32 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
return err;
|
||||
}
|
||||
|
||||
private final List<String> lines = new LinkedList<>();
|
||||
private void queue(Response res) {
|
||||
String tag = res.getTag();
|
||||
if (tag != null) {
|
||||
ResultListener rl = listeners.get(tag);
|
||||
if (rl != null) {
|
||||
if (res instanceof Result) {
|
||||
rl.receive((Result) res);
|
||||
} else {
|
||||
// rl.error((Error)res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private List<String> lines = new LinkedList<String>();
|
||||
private String line;
|
||||
}
|
||||
|
||||
private static class SyncListener implements ResultListener {
|
||||
private class SyncListener implements ResultListener {
|
||||
|
||||
@Override
|
||||
public synchronized void error(MikrotikApiException ex) {
|
||||
this.err = ex;
|
||||
notifyAll();
|
||||
notify();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void completed() {
|
||||
complete = true;
|
||||
notifyAll();
|
||||
notify();
|
||||
}
|
||||
|
||||
synchronized void completed(Done done) {
|
||||
|
|
@ -455,26 +464,18 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
res.put("ret", done.getHash());
|
||||
results.add(res);
|
||||
}
|
||||
complete = true;
|
||||
notifyAll();
|
||||
notify();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(Map<String, String> result) {
|
||||
results.add(result);
|
||||
}
|
||||
|
||||
private List<Map<String, String>> getResults(int timeout) throws MikrotikApiException {
|
||||
private List<Map<String, String>> getResults() throws MikrotikApiException {
|
||||
try {
|
||||
synchronized (this) { // don't wait if we already have a result.
|
||||
int waitTime = timeout;
|
||||
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));
|
||||
}
|
||||
if ((err == null) && results.isEmpty()) {
|
||||
wait();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
|
|
@ -485,9 +486,7 @@ public final class ApiConnectionImpl extends ApiConnection {
|
|||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private final List<Map<String, String>> results = new LinkedList<>();
|
||||
private List<Map<String, String>> results = new LinkedList<Map<String, String>>();
|
||||
private MikrotikApiException err;
|
||||
private boolean complete = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,9 +73,9 @@ class Command {
|
|||
List<Parameter> getParameters() {
|
||||
return params;
|
||||
}
|
||||
private final String cmd;
|
||||
private final List<Parameter> params = new LinkedList<>();
|
||||
private final List<String> queries = new LinkedList<>();
|
||||
private final List<String> properties = new LinkedList<>();
|
||||
private String cmd;
|
||||
private List<Parameter> params = new LinkedList<Parameter>();
|
||||
private List<String> queries = new LinkedList<String>();
|
||||
private List<String> properties = new LinkedList<String>();
|
||||
private String tag;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,11 @@ 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).
|
||||
*
|
||||
* @author GideonLeGrange
|
||||
*/
|
||||
class Error extends Response {
|
||||
|
||||
private String message;
|
||||
private int category;
|
||||
|
||||
Error(String tag, String message, int category) {
|
||||
Error(String tag, String message) {
|
||||
super(tag);
|
||||
this.message = message;
|
||||
}
|
||||
|
|
@ -22,16 +18,11 @@ class Error extends Response {
|
|||
String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
int getCategory() {
|
||||
return category;
|
||||
}
|
||||
private String message;
|
||||
|
||||
|
||||
void setCategory(int category) {
|
||||
this.category = category;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,27 +3,21 @@ package me.legrange.mikrotik.impl;
|
|||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import me.legrange.mikrotik.impl.Scanner.Token;
|
||||
|
||||
/**
|
||||
* Parse the pseudo-command line into command objects.
|
||||
*
|
||||
* @author GideonLeGrange
|
||||
*/
|
||||
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 {
|
||||
Parser parser = new Parser(text);
|
||||
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 {
|
||||
command();
|
||||
while (!is(Token.WHERE, Token.RETURN, Token.EOL)) {
|
||||
|
|
@ -57,13 +51,8 @@ class Parser {
|
|||
next();
|
||||
if (token == Token.EQUALS) {
|
||||
next();
|
||||
StringBuilder val = new StringBuilder();
|
||||
if (token == Token.PIPE) { // handle cases like hotspot=!auth
|
||||
val.append(token);
|
||||
next();
|
||||
}
|
||||
expect(Token.TEXT);
|
||||
val.append(text);
|
||||
StringBuilder val = new StringBuilder(text);
|
||||
next();
|
||||
while (is(Token.COMMA, Token.SLASH)) {
|
||||
val.append(token);
|
||||
|
|
@ -73,7 +62,8 @@ class Parser {
|
|||
next();
|
||||
}
|
||||
cmd.addParameter(new Parameter(name, val.toString()));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
cmd.addParameter(new Parameter(name));
|
||||
}
|
||||
}
|
||||
|
|
@ -84,7 +74,7 @@ class Parser {
|
|||
}
|
||||
|
||||
private void expr() throws ParseException {
|
||||
expect(Token.NOT, Token.TEXT, Token.LEFT_BRACKET);
|
||||
expect(Token.NOT, Token.TEXT);
|
||||
switch (token) {
|
||||
case NOT:
|
||||
notExpr();
|
||||
|
|
@ -92,14 +82,11 @@ class Parser {
|
|||
case TEXT: {
|
||||
String name = text;
|
||||
next();
|
||||
expect(Token.EQUALS, Token.LESS, Token.MORE, Token.NOT_EQUALS);
|
||||
expect(Token.EQUALS, Token.LESS, Token.MORE);
|
||||
switch (token) {
|
||||
case EQUALS:
|
||||
eqExpr(name);
|
||||
break;
|
||||
case NOT_EQUALS:
|
||||
notExpr(name);
|
||||
break;
|
||||
case LESS:
|
||||
lessExpr(name);
|
||||
break;
|
||||
|
|
@ -111,36 +98,22 @@ class Parser {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case LEFT_BRACKET:
|
||||
nestedExpr();
|
||||
break;
|
||||
}
|
||||
// if you get here, you had a expression, see if you want more.
|
||||
switch (token) {
|
||||
case AND:
|
||||
andExpr();
|
||||
break;
|
||||
case OR:
|
||||
orExpr();
|
||||
case AND : andExpr();
|
||||
break;
|
||||
case OR : orExpr();
|
||||
}
|
||||
}
|
||||
|
||||
private void nestedExpr() throws ParseException {
|
||||
expect(Token.LEFT_BRACKET);
|
||||
next();
|
||||
expr();
|
||||
expect(Token.RIGHT_BRACKET);
|
||||
next();
|
||||
}
|
||||
|
||||
private void andExpr() throws ParseException {
|
||||
next(); // eat and
|
||||
expr();
|
||||
cmd.addQuery("?#&");
|
||||
}
|
||||
|
||||
private void orExpr() throws ParseException {
|
||||
private void orExpr() throws ParseException {
|
||||
next(); // eat or
|
||||
expr();
|
||||
cmd.addQuery("?#|");
|
||||
|
|
@ -153,7 +126,7 @@ class Parser {
|
|||
cmd.addQuery("?#!");
|
||||
}
|
||||
|
||||
private void eqExpr(String name) throws ParseException {
|
||||
private void eqExpr(String name) throws ParseException {
|
||||
next(); // eat =
|
||||
expect(Token.TEXT);
|
||||
cmd.addQuery(String.format("?%s=%s", name, text));
|
||||
|
|
@ -166,13 +139,6 @@ class Parser {
|
|||
next();
|
||||
}
|
||||
|
||||
private void notExpr(String name) throws ScanException {
|
||||
next(); // eat !=
|
||||
cmd.addQuery(String.format("?%s=%s", name, text));
|
||||
cmd.addQuery("?#!");
|
||||
next();
|
||||
}
|
||||
|
||||
private void moreExpr(String name) throws ScanException {
|
||||
next(); // eat >
|
||||
cmd.addQuery(String.format("?>%s=%s", name, text));
|
||||
|
|
@ -193,24 +159,22 @@ class Parser {
|
|||
}
|
||||
next();
|
||||
}
|
||||
cmd.addProperty(props.toArray(new String[props.size()]));
|
||||
cmd.addProperty(props.toArray(new String[]{}));
|
||||
}
|
||||
|
||||
private void expect(Token... tokens) throws ParseException {
|
||||
private void expect(Token...tokens) throws ParseException {
|
||||
if (!is(tokens))
|
||||
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) {
|
||||
if (this.token == want) return true;
|
||||
}
|
||||
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 {
|
||||
token = scanner.next();
|
||||
while (token == Token.WS) {
|
||||
|
|
|
|||
|
|
@ -24,5 +24,5 @@ abstract class Response {
|
|||
this.tag = tag;
|
||||
}
|
||||
|
||||
private String tag;
|
||||
private String tag;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package me.legrange.mikrotik.impl;
|
||||
package me.legrange.mikrotik.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
|
@ -15,7 +15,6 @@ class Result extends Response implements Map<String, String> {
|
|||
return map.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
|
@ -25,64 +24,53 @@ class Result extends Response implements Map<String, String> {
|
|||
return String.format("tag=%s, data=%s", getTag(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return map.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return map.containsValue(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Object o) {
|
||||
return map.get(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String put(String k, String v) {
|
||||
return map.put(k, v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remove(Object o) {
|
||||
return map.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends String> map) {
|
||||
this.map.putAll(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
map.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return map.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> values() {
|
||||
return map.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, String>> entrySet() {
|
||||
return map.entrySet();
|
||||
}
|
||||
|
||||
Result() {
|
||||
super(null);
|
||||
this.map = new HashMap<>();
|
||||
this.map = new HashMap<String, String>();
|
||||
}
|
||||
|
||||
private final Map<String, String> map;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package me.legrange.mikrotik.impl;
|
||||
|
||||
import me.legrange.mikrotik.MikrotikApiException;
|
||||
|
||||
/**
|
||||
* Exception thrown if the scanner encounters an error while scanning a command line.
|
||||
* @author GideonLeGrange
|
||||
|
|
@ -14,4 +16,6 @@ public class ScanException extends ParseException {
|
|||
super(msg, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,22 +13,18 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package me.legrange.mikrotik.impl;
|
||||
|
||||
import java.util.Locale;
|
||||
import static me.legrange.mikrotik.impl.Scanner.Token.*;
|
||||
/**
|
||||
* A simple scanner.
|
||||
*
|
||||
* @author gideon
|
||||
*/
|
||||
class Scanner {
|
||||
|
||||
enum Token {
|
||||
|
||||
SLASH("/"), COMMA(","), EOL(), WS, TEXT,
|
||||
LESS("<"), MORE(">"), EQUALS("="), NOT_EQUALS("!="), PIPE("!"),
|
||||
LEFT_BRACKET("("), RIGHT_BRACKET(")"),
|
||||
LESS("<"), MORE(">"), EQUALS("="),
|
||||
WHERE, NOT, AND, OR, RETURN;
|
||||
|
||||
@Override
|
||||
|
|
@ -44,106 +40,77 @@ class Scanner {
|
|||
symb = null;
|
||||
}
|
||||
|
||||
private final String symb;
|
||||
}
|
||||
|
||||
/**
|
||||
* create a scanner for the given line of text
|
||||
*/
|
||||
private final String symb;
|
||||
}
|
||||
|
||||
/** create a scanner for the given line of text */
|
||||
Scanner(String line) {
|
||||
this.line = line;
|
||||
nextChar();
|
||||
}
|
||||
|
||||
/**
|
||||
* return the next token from the text
|
||||
*/
|
||||
/** return the next token from the text */
|
||||
Token next() throws ScanException {
|
||||
text = null;
|
||||
switch (c) {
|
||||
case '\n':
|
||||
return EOL;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n' : return Token.EOL;
|
||||
case ' ' :
|
||||
case '\t' :
|
||||
return whiteSpace();
|
||||
case ',':
|
||||
case ',' :
|
||||
nextChar();
|
||||
return COMMA;
|
||||
case '/':
|
||||
return Token.COMMA;
|
||||
case '/' :
|
||||
nextChar();
|
||||
return SLASH;
|
||||
case '<':
|
||||
return Token.SLASH;
|
||||
case '<' :
|
||||
nextChar();
|
||||
return LESS;
|
||||
case '>':
|
||||
return Token.LESS;
|
||||
case '>' :
|
||||
nextChar();
|
||||
return MORE;
|
||||
case '=':
|
||||
return Token.MORE;
|
||||
case '=' :
|
||||
nextChar();
|
||||
return EQUALS;
|
||||
case '(' :
|
||||
nextChar();
|
||||
return LEFT_BRACKET;
|
||||
case ')' :
|
||||
nextChar();
|
||||
return RIGHT_BRACKET;
|
||||
case '!':
|
||||
return pipe();
|
||||
case '"':
|
||||
return Token.EQUALS;
|
||||
case '"' :
|
||||
return quotedText('"');
|
||||
case '\'':
|
||||
case '\'' :
|
||||
return quotedText('\'');
|
||||
default:
|
||||
default :
|
||||
return name();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* return the text associated with the last token returned
|
||||
*/
|
||||
String text() {
|
||||
if (text != null) {
|
||||
return text.toString();
|
||||
}
|
||||
/** return the text associated with the last token returned */
|
||||
String text() {
|
||||
if (text != null) return text.toString();
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* return the position of the scanner
|
||||
*/
|
||||
int pos() {
|
||||
return pos;
|
||||
}
|
||||
/** return the position of the scanner */
|
||||
int pos() { return pos; }
|
||||
|
||||
/**
|
||||
* process 'name' tokens which could be key words or text
|
||||
*/
|
||||
/** process 'name' tokens which could be key words or text */
|
||||
private Token name() throws ScanException {
|
||||
text = new StringBuilder();
|
||||
while (!in(c, "[ \t\r\n=<>!)]")) {
|
||||
while (!in(c, "[ \t\r\n=]")) {
|
||||
text.append(c);
|
||||
nextChar();
|
||||
}
|
||||
String val = text.toString().toLowerCase(Locale.getDefault());
|
||||
switch (val) {
|
||||
case "where":
|
||||
return WHERE;
|
||||
case "not":
|
||||
return NOT;
|
||||
case "and":
|
||||
return AND;
|
||||
case "or":
|
||||
return OR;
|
||||
case "return":
|
||||
return RETURN;
|
||||
String val = text.toString().toLowerCase();
|
||||
switch (val) {
|
||||
case "where" : return Token.WHERE;
|
||||
case "not" : return Token.NOT;
|
||||
case "and" : return Token.AND;
|
||||
case "or" : return Token.OR;
|
||||
case "return" : return Token.RETURN;
|
||||
}
|
||||
return TEXT;
|
||||
return Token.TEXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* process quoted text
|
||||
*/
|
||||
/** process quoted text */
|
||||
private Token quotedText(char quote) throws ScanException {
|
||||
nextChar(); // eat the '"'
|
||||
text = new StringBuilder();
|
||||
|
|
@ -155,46 +122,29 @@ class Scanner {
|
|||
nextChar();
|
||||
}
|
||||
nextChar(); // eat the '"'
|
||||
return TEXT;
|
||||
return Token.TEXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* process notEquals !
|
||||
*/
|
||||
private Token pipe() {
|
||||
nextChar(); // eat !
|
||||
if (c == '=') {
|
||||
nextChar(); // eat =
|
||||
return NOT_EQUALS;
|
||||
}
|
||||
return PIPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* process white space
|
||||
*/
|
||||
/** process white space */
|
||||
private Token whiteSpace() {
|
||||
while ((c == ' ') || (c == '\t')) {
|
||||
nextChar();
|
||||
}
|
||||
return WS;
|
||||
return Token.WS;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the next character from the line of text
|
||||
*/
|
||||
/** return the next character from the line of text */
|
||||
private void nextChar() {
|
||||
if (pos < line.length()) {
|
||||
c = line.charAt(pos);
|
||||
pos++;
|
||||
} else {
|
||||
c = line.charAt(pos);
|
||||
pos ++;
|
||||
}
|
||||
else {
|
||||
c = '\n';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the character matches the give expression
|
||||
*/
|
||||
/** check if the character matches the give expression */
|
||||
private boolean in(char c, String cs) {
|
||||
return ("" + c).matches(cs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
|
|
@ -72,7 +71,7 @@ final class Util {
|
|||
}
|
||||
buf[i] = (byte) (c & 0xFF);
|
||||
}
|
||||
String res = new String(buf, Charset.forName("UTF-8"));
|
||||
String res = new String(buf);
|
||||
if (result.length() > 0) {
|
||||
result.append("\n");
|
||||
}
|
||||
|
|
@ -122,11 +121,11 @@ final class Util {
|
|||
* @return - converted string.
|
||||
*/
|
||||
static String hexStrToStr(String s) {
|
||||
StringBuilder ret = new StringBuilder();
|
||||
String ret = "";
|
||||
for (int i = 0; i < s.length(); i += 2) {
|
||||
ret.append((char) Integer.parseInt(s.substring(i, i + 2), 16));
|
||||
ret += (char) Integer.parseInt(s.substring(i, i + 2), 16);
|
||||
}
|
||||
return ret.toString();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -134,7 +133,7 @@ final class Util {
|
|||
* stream.
|
||||
*/
|
||||
private static void encode(String word, OutputStream out) throws UnsupportedEncodingException, IOException {
|
||||
byte bytes[] = word.getBytes("UTF-8");
|
||||
byte bytes[] = word.getBytes("US-ASCII");
|
||||
int len = bytes.length;
|
||||
if (len < 0x80) {
|
||||
out.write(len);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package me.legrange.mikrotik;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class ApiConnectionTest extends TestCase {
|
||||
|
||||
private static final String HOSTNAME = "192.168.88.1";
|
||||
private static final String ADMIN = "admin";
|
||||
private static final String PASSWORD = "plasma";
|
||||
|
||||
public void testConnectTLSStringInt() throws MikrotikApiException, InterruptedException {
|
||||
/* ApiConnection connect = ApiConnection.connectTLS(HOSTNAME, 8729);
|
||||
connect.login(ADMIN, PASSWORD);
|
||||
List<Map<String, String>> result = connect.execute("/system/resource/print");
|
||||
|
||||
assertEquals(1, result.size());
|
||||
assertEquals("MikroTik", result.get(0).get("platform")); */
|
||||
}
|
||||
|
||||
public void testConnectStringInt() throws MikrotikApiException, InterruptedException {
|
||||
/* ApiConnection connect = ApiConnection.connect(HOSTNAME, 8728);
|
||||
connect.login(ADMIN, PASSWORD);
|
||||
List<Map<String, String>> result = connect.execute("/system/resource/print");
|
||||
|
||||
assertEquals(1, result.size());
|
||||
assertEquals("MikroTik", result.get(0).get("platform")); */
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue