diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100755
index 0000000..f775b1c
Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100755
index 0000000..a447c9f
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip
\ No newline at end of file
diff --git a/jawampa-core/pom.xml b/jawampa-core/pom.xml
index 302c19d..f2a6966 100644
--- a/jawampa-core/pom.xml
+++ b/jawampa-core/pom.xml
@@ -44,17 +44,27 @@
io.reactivex
rxjava
- 1.0.8
+ 1.0.17
com.fasterxml.jackson.core
jackson-databind
- 2.4.4
+ 2.9.2
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-cbor
+ 2.9.2
org.msgpack
jackson-dataformat-msgpack
- 0.7.0-p7
+ 0.8.13
+
+
+ de.undercouch
+ bson4jackson
+ 2.7.0
diff --git a/jawampa-core/src/main/java/ws/wamp/jawampa/ApplicationError.java b/jawampa-core/src/main/java/ws/wamp/jawampa/ApplicationError.java
index 160a198..8e65b08 100644
--- a/jawampa-core/src/main/java/ws/wamp/jawampa/ApplicationError.java
+++ b/jawampa-core/src/main/java/ws/wamp/jawampa/ApplicationError.java
@@ -16,10 +16,9 @@
package ws.wamp.jawampa;
-import ws.wamp.jawampa.connection.IWampConnectorProvider;
-
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import ws.wamp.jawampa.connection.IWampConnectorProvider;
/**
* Base class for all WAMP related exceptions that can/may be
@@ -46,11 +45,11 @@ public ObjectNode keywordArguments() {
}
public ApplicationError(String uri) {
- this(uri, null, null);
+ this(uri, (ArrayNode) null, (ObjectNode) null);
}
public ApplicationError(String uri, ArrayNode args) {
- this(uri, args, null);
+ this(uri, args, (ObjectNode) null);
}
public ApplicationError(String uri, ArrayNode args, ObjectNode kwArgs) {
@@ -61,6 +60,22 @@ public ApplicationError(String uri, ArrayNode args, ObjectNode kwArgs) {
this.kwArgs = kwArgs;
}
+ public ApplicationError(String uri, Throwable cause) {
+ this(uri, null, null, cause);
+ }
+
+ public ApplicationError(String uri, ArrayNode args, Throwable cause) {
+ this(uri, args, null, cause);
+ }
+
+ public ApplicationError(String uri, ArrayNode args, ObjectNode kwArgs, Throwable cause) {
+ super(uri, cause);
+ if (uri == null) throw new NullPointerException();
+ this.uri = uri;
+ this.args = args;
+ this.kwArgs = kwArgs;
+ }
+
@Override
public String toString() {
StringBuilder s = new StringBuilder();
diff --git a/jawampa-core/src/main/java/ws/wamp/jawampa/CallFlags.java b/jawampa-core/src/main/java/ws/wamp/jawampa/CallFlags.java
index f25be3e..6e29d4e 100644
--- a/jawampa-core/src/main/java/ws/wamp/jawampa/CallFlags.java
+++ b/jawampa-core/src/main/java/ws/wamp/jawampa/CallFlags.java
@@ -21,5 +21,11 @@ public enum CallFlags {
* Set the disclose_me flag on the Call message to true.
* This will allow the remote call endpoint to access information on this client's WAMP session.
*/
- DiscloseMe
+ DiscloseMe,
+
+ MatchExact, //default, exact procedure URI match and not necessary to specify but will override if multiple match flags are present
+ MatchPrefix, // match for all precedure calls that have the given uri prefix
+ MatchWildcard,
+
+ ReceiveProgress // Should progress messages be sent if the call is long running
}
diff --git a/jawampa-core/src/main/java/ws/wamp/jawampa/PublishFlags.java b/jawampa-core/src/main/java/ws/wamp/jawampa/PublishFlags.java
index c020644..c11d999 100644
--- a/jawampa-core/src/main/java/ws/wamp/jawampa/PublishFlags.java
+++ b/jawampa-core/src/main/java/ws/wamp/jawampa/PublishFlags.java
@@ -28,5 +28,7 @@ public enum PublishFlags {
* between client and router is established. If the flag is set the response from the router
* to a publish message will be used as a result for a publish request.
*/
- RequireAcknowledge;
+ RequireAcknowledge,
+
+ DiscloseMe;
}
diff --git a/jawampa-core/src/main/java/ws/wamp/jawampa/RegisterFlags.java b/jawampa-core/src/main/java/ws/wamp/jawampa/RegisterFlags.java
index dc765c7..eb1ac91 100644
--- a/jawampa-core/src/main/java/ws/wamp/jawampa/RegisterFlags.java
+++ b/jawampa-core/src/main/java/ws/wamp/jawampa/RegisterFlags.java
@@ -20,5 +20,22 @@ public enum RegisterFlags {
/**
* the registered procedure is called with the caller's sessionID as part of the call details object.
*/
- DiscloseCaller;
+ DiscloseCaller,
+
+ /**
+ * How are duplicate registrations of this procedure handled?
+ */
+ InvokeSingle, // default, duplicate registrations cause errors and not necessary to specify but will override if multiple invoke flags are present
+ InvokeLast, // the last client to register is called when the precedure is executed
+ InvokeFirst, // the first client to register is called when the precedure is executed
+ InvokeRoundRobin, // all clients that are registered will be called in a round robin fashion
+ InvokeRandom, // all clients that are registered will be called in a random fashion
+
+ /**
+ * How is this procedure registration matched against procedure call uris
+ */
+ MatchExact, //default, exact procedure URI match and not necessary to specify but will override if multiple match flags are present
+ MatchPrefix, // match for all precedure calls that have the given uri prefix
+ MatchWildcard // matching using .. wildcards
+ ;
}
diff --git a/jawampa-core/src/main/java/ws/wamp/jawampa/WampClient.java b/jawampa-core/src/main/java/ws/wamp/jawampa/WampClient.java
index e0f2dad..68788b5 100644
--- a/jawampa-core/src/main/java/ws/wamp/jawampa/WampClient.java
+++ b/jawampa-core/src/main/java/ws/wamp/jawampa/WampClient.java
@@ -16,6 +16,11 @@
package ws.wamp.jawampa;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import java.net.URI;
import java.util.Arrays;
import java.util.EnumSet;
@@ -34,9 +39,6 @@
import ws.wamp.jawampa.internal.ArgArrayBuilder;
import ws.wamp.jawampa.internal.Promise;
import ws.wamp.jawampa.internal.UriValidator;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Provides the client-side functionality for WAMP.
@@ -443,6 +445,61 @@ public T call(PubSubData ev) {
});
}
+ public Observable makeSubscription(final String topic, SubscriptionFlags flags, final JavaType eventType)
+ {
+ return makeSubscription(topic, flags).map(new Func1() {
+ @Override
+ public T call(PubSubData ev) {
+ if (eventType == null) {
+ // We don't need a value
+ return null;
+ }
+
+ if (ev.arguments == null || ev.arguments.size() < 1)
+ throw OnErrorThrowable.from(new ApplicationError(ApplicationError.MISSING_VALUE));
+
+ JsonNode eventNode = ev.arguments.get(0);
+ if (eventNode.isNull()) return null;
+
+ T eventValue;
+ try {
+ eventValue = clientConfig.objectMapper().convertValue(eventNode, eventType);
+ } catch (IllegalArgumentException e) {
+ throw OnErrorThrowable.from(new ApplicationError(ApplicationError.INVALID_VALUE_TYPE, e));
+ }
+ return eventValue;
+ }
+ });
+ }
+
+ public Observable makeSubscription(final String topic, SubscriptionFlags flags, final TypeReference> typeReference)
+ {
+ return makeSubscription(topic, flags).map(new Func1() {
+ @Override
+ public T call(PubSubData ev) {
+ if (typeReference == null) {
+ // We don't need a value
+ return null;
+ }
+
+ if (ev.arguments == null || ev.arguments.size() < 1)
+ throw OnErrorThrowable.from(new ApplicationError(ApplicationError.MISSING_VALUE));
+
+ JsonNode eventNode = ev.arguments.get(0);
+ if (eventNode.isNull()) return null;
+
+ T eventValue;
+ try {
+ eventValue = clientConfig.objectMapper().convertValue(eventNode, typeReference);
+ } catch (IllegalArgumentException e) {
+ throw OnErrorThrowable.from(new ApplicationError(ApplicationError.INVALID_VALUE_TYPE));
+ }
+ return eventValue;
+ }
+ });
+ }
+
+
/**
* Returns an observable that allows to subscribe on the given topic.
* The actual subscription will only be made after subscribe() was called
@@ -474,27 +531,59 @@ public Observable> makeSubscriptionWithDetails(final String
return makeSubscription(topic, flags).map(new Func1>() {
@Override
public EventDetails call(PubSubData ev) {
+ //get the complete topic name
+ //which may not be the same as method parameter 'topic' during wildcard or prefix subscriptions
+ String actualTopic = null;
+ if(ev.details != null && ev.details.get("topic") != null){
+ actualTopic = ev.details.get("topic").asText();
+ }
if (eventClass == null || eventClass == Void.class) {
// We don't need a value
- return null;
+ return new EventDetails(null, actualTopic);
}
-
+
+ if (ev.arguments == null || ev.arguments.size() < 1)
+ throw OnErrorThrowable.from(new ApplicationError(ApplicationError.MISSING_VALUE));
+
+ JsonNode eventNode = ev.arguments.get(0);
+ if (eventNode.isNull()) return new EventDetails(null, actualTopic);
+
+ T eventValue;
+ try {
+ eventValue = clientConfig.objectMapper().convertValue(eventNode, eventClass);
+ } catch (IllegalArgumentException e) {
+ throw OnErrorThrowable.from(new ApplicationError(ApplicationError.INVALID_VALUE_TYPE));
+ }
+ return new EventDetails(eventValue, actualTopic);
+ }
+ });
+ }
+
+ public Observable> makeSubscriptionWithDetails(final String topic, SubscriptionFlags flags, final JavaType javaType)
+ {
+ return makeSubscription(topic, flags).map(new Func1>() {
+ @Override
+ public EventDetails call(PubSubData ev) {
//get the complete topic name
- //which may not be the same as method parameter 'topic' during wildcard or prefix subscriptions
+ //which may not be the same as method parameter 'topic' during wildcard or prefix subscriptions
String actualTopic = null;
if(ev.details != null && ev.details.get("topic") != null){
actualTopic = ev.details.get("topic").asText();
}
+ if (javaType == null) {
+ // We don't need a value
+ return new EventDetails(null, actualTopic);
+ }
if (ev.arguments == null || ev.arguments.size() < 1)
throw OnErrorThrowable.from(new ApplicationError(ApplicationError.MISSING_VALUE));
JsonNode eventNode = ev.arguments.get(0);
- if (eventNode.isNull()) return null;
+ if (eventNode.isNull()) return new EventDetails(null, actualTopic);
T eventValue;
try {
- eventValue = clientConfig.objectMapper().convertValue(eventNode, eventClass);
+ eventValue = clientConfig.objectMapper().convertValue(eventNode, javaType);
} catch (IllegalArgumentException e) {
throw OnErrorThrowable.from(new ApplicationError(ApplicationError.INVALID_VALUE_TYPE));
}
@@ -503,6 +592,41 @@ public EventDetails call(PubSubData ev) {
});
}
+ public Observable> makeSubscriptionWithDetails(final String topic, SubscriptionFlags flags, final TypeReference> typeRef)
+ {
+ return makeSubscription(topic, flags).map(new Func1>() {
+ @Override
+ public EventDetails call(PubSubData ev) {
+ //get the complete topic name
+ //which may not be the same as method parameter 'topic' during wildcard or prefix subscriptions
+ String actualTopic = null;
+ if(ev.details != null && ev.details.get("topic") != null){
+ actualTopic = ev.details.get("topic").asText();
+ }
+ if (typeRef == null) {
+ // We don't need a value
+ return new EventDetails(null, actualTopic);
+ }
+
+ if (ev.arguments == null || ev.arguments.size() < 1)
+ throw OnErrorThrowable.from(new ApplicationError(ApplicationError.MISSING_VALUE));
+
+ JsonNode eventNode = ev.arguments.get(0);
+ if (eventNode.isNull()) return new EventDetails(null, actualTopic);
+
+ T eventValue;
+ try {
+ eventValue = clientConfig.objectMapper().convertValue(eventNode, typeRef);
+ } catch (IllegalArgumentException e) {
+ throw OnErrorThrowable.from(new ApplicationError(ApplicationError.INVALID_VALUE_TYPE));
+ }
+ return new EventDetails(eventValue, actualTopic);
+ }
+ });
+ }
+
+
+
/**
* Returns an observable that allows to subscribe on the given topic.
* The actual subscription will only be made after subscribe() was called
diff --git a/jawampa-core/src/main/java/ws/wamp/jawampa/WampError.java b/jawampa-core/src/main/java/ws/wamp/jawampa/WampError.java
index 71936b0..51a4177 100644
--- a/jawampa-core/src/main/java/ws/wamp/jawampa/WampError.java
+++ b/jawampa-core/src/main/java/ws/wamp/jawampa/WampError.java
@@ -31,4 +31,8 @@ public class WampError extends Exception {
public WampError(String message) {
super(message);
}
+
+ public WampError(String message, Throwable cause) {
+ super(message, cause);
+ }
}
diff --git a/jawampa-core/src/main/java/ws/wamp/jawampa/WampSerialization.java b/jawampa-core/src/main/java/ws/wamp/jawampa/WampSerialization.java
index 9c62766..197274e 100644
--- a/jawampa-core/src/main/java/ws/wamp/jawampa/WampSerialization.java
+++ b/jawampa-core/src/main/java/ws/wamp/jawampa/WampSerialization.java
@@ -17,12 +17,12 @@
package ws.wamp.jawampa;
import com.fasterxml.jackson.databind.ObjectMapper;
-
-import org.msgpack.jackson.dataformat.MessagePackFactory;
-
+import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
+import de.undercouch.bson4jackson.BsonFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import org.msgpack.jackson.dataformat.MessagePackFactory;
/**
* Possible serialization methods for WAMP
@@ -32,6 +32,9 @@ public enum WampSerialization {
Invalid("", true, null),
/** Use the JSON serialization */
Json("wamp.2.json", true, new ObjectMapper()),
+ /** Use universal binary JSON (BSON) serialization */
+ Ubjson("wamp.2.ubjson", false, new ObjectMapper(new BsonFactory())),
+ Cbor("wamp.2.cbor", false, new ObjectMapper(new CBORFactory())),
/** Use the MessagePack serialization */
MessagePack("wamp.2.msgpack", false, new ObjectMapper(new MessagePackFactory()));
@@ -60,8 +63,13 @@ public String toString() {
public static WampSerialization fromString(String serialization) {
if (serialization == null) return Invalid;
- else if (serialization.equals("wamp.2.json")) return Json;
- else if (serialization.equals("wamp.2.msgpack")) return MessagePack;
+
+ serialization = serialization.toLowerCase();
+
+ if (serialization.contains("ubjson")) return Ubjson;
+ else if (serialization.contains("json")) return Json;
+ else if (serialization.contains("cbor")) return Cbor;
+ else if (serialization.contains("msgpack")) return MessagePack;
return Invalid;
}
@@ -79,6 +87,8 @@ public static String makeWebsocketSubprotocolList(List serial
public static void addDefaultSerializations(List serializations) {
serializations.add(Json);
+ serializations.add(Ubjson);
+ serializations.add(Cbor);
serializations.add(MessagePack);
}
diff --git a/jawampa-core/src/main/java/ws/wamp/jawampa/client/SessionEstablishedState.java b/jawampa-core/src/main/java/ws/wamp/jawampa/client/SessionEstablishedState.java
index bdd3b28..8f02d1e 100644
--- a/jawampa-core/src/main/java/ws/wamp/jawampa/client/SessionEstablishedState.java
+++ b/jawampa-core/src/main/java/ws/wamp/jawampa/client/SessionEstablishedState.java
@@ -16,14 +16,14 @@
package ws.wamp.jawampa.client;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
-import java.util.concurrent.RejectedExecutionException;
-
import rx.Subscriber;
import rx.functions.Action0;
import rx.functions.Action1;
@@ -39,7 +39,6 @@
import ws.wamp.jawampa.SubscriptionFlags;
import ws.wamp.jawampa.WampClient;
import ws.wamp.jawampa.WampMessages;
-import ws.wamp.jawampa.WampRoles;
import ws.wamp.jawampa.WampMessages.AbortMessage;
import ws.wamp.jawampa.WampMessages.CallMessage;
import ws.wamp.jawampa.WampMessages.ChallengeMessage;
@@ -59,14 +58,12 @@
import ws.wamp.jawampa.WampMessages.UnsubscribedMessage;
import ws.wamp.jawampa.WampMessages.WampMessage;
import ws.wamp.jawampa.WampMessages.WelcomeMessage;
+import ws.wamp.jawampa.WampRoles;
import ws.wamp.jawampa.connection.IConnectionController;
import ws.wamp.jawampa.connection.IWampConnectionPromise;
import ws.wamp.jawampa.internal.IdGenerator;
import ws.wamp.jawampa.internal.IdValidator;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
/**
* The client is connected to the router and the session was established
*/
@@ -427,6 +424,9 @@ public void performPublish(final String topic, final EnumSet flags
if (flags != null && flags.contains(PublishFlags.DontExcludeMe)) {
options.put("exclude_me", false);
}
+ if (flags != null && flags.contains(PublishFlags.DiscloseMe)) {
+ options.put("disclose_me", true);
+ }
if (flags != null && flags.contains(PublishFlags.RequireAcknowledge)) {
// An acknowledge from the router in the form of a PUBLISHED or ERROR message
@@ -458,9 +458,22 @@ public void performCall(final String procedure,
ObjectNode options = stateController.clientConfig().objectMapper().createObjectNode();
- boolean discloseMe = flags != null && flags.contains(CallFlags.DiscloseMe) ? true : false;
- if (discloseMe) {
- options.put("disclose_me", discloseMe);
+ if (flags != null) {
+ if (flags.contains(CallFlags.DiscloseMe)) {
+ options.put("disclose_me", true);
+ }
+
+ if (flags.contains(CallFlags.MatchExact)) {
+ //do nothing as this means we overrode
+ } else if (flags.contains(CallFlags.MatchPrefix)) {
+ options.put("match", "prefix");
+ } else if (flags.contains(CallFlags.MatchWildcard)) {
+ options.put("match", "wildcard");
+ }
+
+ if (flags.contains(CallFlags.ReceiveProgress)) {
+ options.put("receive_progress", true);
+ }
}
final CallMessage callMsg = new CallMessage(requestId, options, procedure,
@@ -490,8 +503,30 @@ public void performRegisterProcedure(final String topic, final EnumSet \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+ # TODO classpath?
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100755
index 0000000..4f0b068
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,145 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%