Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-10.0.x-5133-webappcontext-extraclasspath-cleanup

This commit is contained in:
Joakim Erdfelt 2020-08-20 13:32:47 -05:00
commit f881a1a2fe
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
24 changed files with 271 additions and 77 deletions

10
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View File

@ -647,7 +647,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
* @return the max time, in milliseconds, a connection can take to connect to destinations
* @return the max time, in milliseconds, a connection can take to connect to destinations. Zero value means infinite timeout.
*/
@ManagedAttribute("The timeout, in milliseconds, for connect() operations")
public long getConnectTimeout()
@ -656,7 +656,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
* @param connectTimeout the max time, in milliseconds, a connection can take to connect to destinations
* @param connectTimeout the max time, in milliseconds, a connection can take to connect to destinations. Zero value means infinite timeout.
* @see java.net.Socket#connect(SocketAddress, int)
*/
public void setConnectTimeout(long connectTimeout)

View File

@ -40,7 +40,6 @@ public class HttpChannelOverHTTP extends HttpChannel
private final HttpConnectionOverHTTP connection;
private final HttpSenderOverHTTP sender;
private final HttpReceiverOverHTTP receiver;
private final LongAdder inMessages = new LongAdder();
private final LongAdder outMessages = new LongAdder();
public HttpChannelOverHTTP(HttpConnectionOverHTTP connection)
@ -93,7 +92,6 @@ public class HttpChannelOverHTTP extends HttpChannel
public void receive()
{
inMessages.increment();
receiver.receive();
}
@ -149,7 +147,7 @@ public class HttpChannelOverHTTP extends HttpChannel
protected long getMessagesIn()
{
return inMessages.longValue();
return receiver.getMessagesIn();
}
protected long getMessagesOut()

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.client.http;
import java.io.EOFException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.LongAdder;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpClientTransport;
@ -45,6 +46,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
{
private static final Logger LOG = LoggerFactory.getLogger(HttpReceiverOverHTTP.class);
private final LongAdder inMessages = new LongAdder();
private final HttpParser parser;
private RetainableByteBuffer networkBuffer;
private boolean shutdown;
@ -343,9 +345,11 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
return false;
int status = exchange.getResponse().getStatus();
if (status != HttpStatus.CONTINUE_100)
{
inMessages.increment();
complete = true;
}
return !responseSuccess(exchange);
}
@ -386,6 +390,11 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
getHttpConnection().close(failure);
}
long getMessagesIn()
{
return inMessages.longValue();
}
@Override
public String toString()
{

View File

@ -396,6 +396,38 @@ public class ConnectionPoolTest
assertEquals(0, connectionPool.getConnectionCount());
}
@ParameterizedTest
@MethodSource("pools")
public void testIdleTimeoutNoRequests(ConnectionPoolFactory factory) throws Exception
{
startServer(new EmptyServerHandler());
startClient(destination ->
{
try
{
ConnectionPool connectionPool = factory.factory.newConnectionPool(destination);
connectionPool.preCreateConnections(1).get();
return connectionPool;
}
catch (Exception x)
{
throw new RuntimeException(x);
}
});
long idleTimeout = 1000;
client.setIdleTimeout(idleTimeout);
// Trigger the creation of a destination, that will create the connection pool.
HttpDestination destination = client.resolveDestination(new Origin("http", "localhost", connector.getLocalPort()));
AbstractConnectionPool connectionPool = (AbstractConnectionPool)destination.getConnectionPool();
assertEquals(1, connectionPool.getConnectionCount());
// Wait for the pre-created connections to idle timeout.
Thread.sleep(idleTimeout + idleTimeout / 2);
assertEquals(0, connectionPool.getConnectionCount());
}
private static class ConnectionPoolFactory
{
private final String name;

View File

@ -796,7 +796,7 @@ public interface HttpFields extends Iterable<HttpField>
{
if (_size == 0)
throw new IllegalStateException();
Mutable.this.remove(_index - 1);
Mutable.this.remove(--_index);
}
};
}
@ -1059,7 +1059,7 @@ public interface HttpFields extends Iterable<HttpField>
}
// Remember and remove additional fields
found.add(f);
remove(i);
remove(i--);
}
}
@ -1088,7 +1088,7 @@ public interface HttpFields extends Iterable<HttpField>
{
HttpField f = _fields[i];
if (f.getHeader() == name)
remove(i);
remove(i--);
}
return this;
}
@ -1099,7 +1099,7 @@ public interface HttpFields extends Iterable<HttpField>
{
HttpField f = _fields[i];
if (fields.contains(f.getHeader()))
remove(i);
remove(i--);
}
return this;
}
@ -1116,7 +1116,7 @@ public interface HttpFields extends Iterable<HttpField>
{
HttpField f = _fields[i];
if (f.is(name))
remove(i);
remove(i--);
}
return this;
}

View File

@ -912,11 +912,12 @@ public class HttpFieldsTest
fields.add(new HttpField("After", "value"));
assertThat(fields.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Before: value", "Test: one", "After: value"));
fields.add(new HttpField("Test", "extra"));
assertThat(fields.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Before: value", "Test: one", "After: value", "Test: extra"));
fields.add(new HttpField("Test", "two"));
fields.add(new HttpField("Test", "three"));
assertThat(fields.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Before: value", "Test: one", "After: value", "Test: two", "Test: three"));
fields.computeField("Test", (n, f) -> new HttpField("TEST", "count=" + f.size()));
assertThat(fields.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Before: value", "TEST: count=2", "After: value"));
assertThat(fields.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Before: value", "TEST: count=3", "After: value"));
fields.computeField("TEST", (n, f) -> null);
assertThat(fields.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Before: value", "After: value"));

View File

@ -0,0 +1,114 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.io;
import java.util.AbstractSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Predicate;
import org.eclipse.jetty.util.IncludeExcludeSet;
public class IncludeExcludeConnectionStatistics extends ConnectionStatistics
{
private final IncludeExcludeSet<Class<? extends Connection>, Connection> _set = new IncludeExcludeSet<>(ConnectionSet.class);
public void include(String className) throws ClassNotFoundException
{
_set.include(connectionForName(className));
}
public void include(Class<? extends Connection> clazz)
{
_set.include(clazz);
}
public void exclude(String className) throws ClassNotFoundException
{
_set.exclude(connectionForName(className));
}
public void exclude(Class<? extends Connection> clazz)
{
_set.exclude(clazz);
}
private Class<? extends Connection> connectionForName(String className) throws ClassNotFoundException
{
Class<?> aClass = Class.forName(className);
if (!Connection.class.isAssignableFrom(aClass))
throw new IllegalArgumentException("Class is not a Connection");
@SuppressWarnings("unchecked")
Class<? extends Connection> connectionClass = (Class<? extends Connection>)aClass;
return connectionClass;
}
@Override
public void onOpened(Connection connection)
{
if (_set.test(connection))
super.onOpened(connection);
}
@Override
public void onClosed(Connection connection)
{
if (_set.test(connection))
super.onClosed(connection);
}
public static class ConnectionSet extends AbstractSet<Class<? extends Connection>> implements Predicate<Connection>
{
private final Set<Class<? extends Connection>> set = new HashSet<>();
@Override
public boolean add(Class<? extends Connection> aClass)
{
return set.add(aClass);
}
@Override
public boolean remove(Object o)
{
return set.remove(o);
}
@Override
public Iterator<Class<? extends Connection>> iterator()
{
return set.iterator();
}
@Override
public int size()
{
return set.size();
}
@Override
public boolean test(Connection connection)
{
if (connection == null)
return false;
return set.stream().anyMatch(c -> c.isAssignableFrom(connection.getClass()));
}
}
}

View File

@ -889,7 +889,11 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
{
this.channel = channel;
this.attachment = attachment;
this.timeout = ManagedSelector.this._selectorManager.getScheduler().schedule(this, ManagedSelector.this._selectorManager.getConnectTimeout(), TimeUnit.MILLISECONDS);
long timeout = ManagedSelector.this._selectorManager.getConnectTimeout();
if (timeout > 0)
this.timeout = ManagedSelector.this._selectorManager.getScheduler().schedule(this, timeout, TimeUnit.MILLISECONDS);
else
this.timeout = null;
}
@Override
@ -920,7 +924,8 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
{
if (failed.compareAndSet(false, true))
{
timeout.cancel();
if (timeout != null)
timeout.cancel();
IO.close(channel);
ManagedSelector.this._selectorManager.connectionFailed(channel, failure, attachment);
}

View File

@ -31,7 +31,6 @@
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>2.13.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>

View File

@ -18,19 +18,20 @@
package org.eclipse.jetty.websocket.javax.common;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.CloseReason;
import javax.websocket.Session;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.Graceful;
public class SessionTracker extends AbstractLifeCycle implements JavaxWebSocketSessionListener, Graceful
public class SessionTracker extends AbstractLifeCycle implements JavaxWebSocketSessionListener, Graceful, Dumpable
{
private final Set<JavaxWebSocketSession> sessions = Collections.newSetFromMap(new ConcurrentHashMap<>());
private boolean isShutdown = false;
@ -88,4 +89,16 @@ public class SessionTracker extends AbstractLifeCycle implements JavaxWebSocketS
{
return isShutdown;
}
@ManagedAttribute("Total number of active WebSocket Sessions")
public int getNumSessions()
{
return sessions.size();
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
Dumpable.dumpObjects(out, indent, this, sessions);
}
}

View File

@ -51,7 +51,7 @@
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
@{argLine} ${jetty.surefire.argLine} --add-exports org.eclipse.jetty.websocket.javax.server/org.eclipse.jetty.websocket.javax.server.examples=org.eclipse.jetty.websocket.javax.common --add-reads org.eclipse.jetty.websocket.javax.server=org.eclipse.jetty.security --add-reads org.eclipse.jetty.websocket.javax.common=org.eclipse.jetty.websocket.javax.server
@{argLine} ${jetty.surefire.argLine} --add-reads org.eclipse.jetty.websocket.javax.server=org.eclipse.jetty.security --add-reads org.eclipse.jetty.websocket.javax.common=org.eclipse.jetty.websocket.javax.server
</argLine>
</configuration>
</plugin>

View File

@ -16,7 +16,7 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.server.examples;
package org.eclipse.jetty.websocket.javax.tests.server.examples;
import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;

View File

@ -16,7 +16,7 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.server.examples;
package org.eclipse.jetty.websocket.javax.tests.server.examples;
import java.io.IOException;
import javax.servlet.http.HttpSession;

View File

@ -16,7 +16,7 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.server.examples;
package org.eclipse.jetty.websocket.javax.tests.server.examples;
import java.security.Principal;
import javax.websocket.HandshakeResponse;

View File

@ -16,7 +16,7 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.server.examples;
package org.eclipse.jetty.websocket.javax.tests.server.examples;
import javax.websocket.OnMessage;
import javax.websocket.server.ServerEndpoint;

View File

@ -16,7 +16,7 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.server.examples;
package org.eclipse.jetty.websocket.javax.tests.server.examples;
import java.io.IOException;
import java.io.Reader;

View File

@ -16,7 +16,7 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.server.examples;
package org.eclipse.jetty.websocket.javax.tests.server.examples;
import java.net.URI;
import java.util.concurrent.ArrayBlockingQueue;

View File

@ -18,22 +18,22 @@
package org.eclipse.jetty.websocket.common;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.Graceful;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.WebSocketSessionListener;
public class SessionTracker extends AbstractLifeCycle implements WebSocketSessionListener, Graceful
public class SessionTracker extends AbstractLifeCycle implements WebSocketSessionListener, Graceful, Dumpable
{
private final Set<Session> sessions = Collections.newSetFromMap(new ConcurrentHashMap<>());
private boolean isShutdown = false;
@ -91,4 +91,16 @@ public class SessionTracker extends AbstractLifeCycle implements WebSocketSessio
{
return isShutdown;
}
@ManagedAttribute("Total number of active WebSocket Sessions")
public int getNumSessions()
{
return sessions.size();
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
Dumpable.dumpObjects(out, indent, this, sessions);
}
}

View File

@ -73,6 +73,12 @@
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.websocket.tests;
import java.lang.management.ManagementFactory;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.concurrent.CompletableFuture;
@ -25,8 +26,8 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.ConnectionStatistics;
import org.eclipse.jetty.server.HttpConnection;
import org.eclipse.jetty.io.IncludeExcludeConnectionStatistics;
import org.eclipse.jetty.jmx.MBeanContainer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
@ -38,8 +39,6 @@ import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.core.internal.Generator;
import org.eclipse.jetty.websocket.core.internal.WebSocketConnection;
import org.eclipse.jetty.websocket.server.JettyWebSocketServlet;
import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@ -51,52 +50,50 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class WebSocketStatsTest
{
public static class MyWebSocketServlet extends JettyWebSocketServlet
{
@Override
public void configure(JettyWebSocketServletFactory factory)
{
factory.setAutoFragment(false);
factory.addMapping("/", (req, resp) -> new EchoSocket());
}
}
private final CountDownLatch wsConnectionClosed = new CountDownLatch(1);
private Server server;
private ServerConnector connector;
private WebSocketClient client;
private ConnectionStatistics statistics;
private final CountDownLatch wsUpgradeComplete = new CountDownLatch(1);
private final CountDownLatch wsConnectionClosed = new CountDownLatch(1);
private IncludeExcludeConnectionStatistics statistics;
@BeforeEach
public void start() throws Exception
{
statistics = new ConnectionStatistics()
statistics = new IncludeExcludeConnectionStatistics();
statistics.include(WebSocketConnection.class);
Connection.Listener.Adapter wsCloseListener = new Connection.Listener.Adapter()
{
@Override
public void onClosed(Connection connection)
{
super.onClosed(connection);
if (connection instanceof WebSocketConnection)
wsConnectionClosed.countDown();
else if (connection instanceof HttpConnection)
wsUpgradeComplete.countDown();
}
};
server = new Server();
connector = new ServerConnector(server);
connector.addBean(statistics);
connector.addBean(wsCloseListener);
server.addConnector(connector);
ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
contextHandler.setContextPath("/");
contextHandler.addServlet(MyWebSocketServlet.class, "/testPath");
server.setHandler(contextHandler);
contextHandler.setContextPath("/");
JettyWebSocketServletContainerInitializer.configure(contextHandler, (context, container) ->
{
container.setAutoFragment(false);
container.addMapping("/", EchoSocket.class);
});
JettyWebSocketServletContainerInitializer.configure(contextHandler, null);
client = new WebSocketClient();
client.setAutoFragment(false);
// Setup JMX.
MBeanContainer mbeanContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer());
server.addBean(mbeanContainer);
server.start();
client.start();
@ -105,8 +102,8 @@ public class WebSocketStatsTest
@AfterEach
public void stop() throws Exception
{
client.stop();
server.stop();
client.stop();
}
long getFrameByteSize(Frame frame)
@ -120,22 +117,14 @@ public class WebSocketStatsTest
@Test
public void echoStatsTest() throws Exception
{
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/testPath");
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/");
EventSocket socket = new EventSocket();
CompletableFuture<Session> connect = client.connect(socket, uri);
final long numMessages = 1000;
final String msgText = "hello world";
long upgradeSentBytes;
long upgradeReceivedBytes;
try (Session session = connect.get(5, TimeUnit.SECONDS))
{
wsUpgradeComplete.await(5, TimeUnit.SECONDS);
upgradeSentBytes = statistics.getSentBytes();
upgradeReceivedBytes = statistics.getReceivedBytes();
for (int i = 0; i < numMessages; i++)
{
session.getRemote().sendString(msgText);
@ -147,18 +136,18 @@ public class WebSocketStatsTest
assertThat(statistics.getConnectionsMax(), is(1L));
assertThat(statistics.getConnections(), is(0L));
assertThat(statistics.getSentMessages(), is(numMessages + 2L));
assertThat(statistics.getReceivedMessages(), is(numMessages + 2L));
// Sent and r eceived all of the echo messages + 1 for the close frame.
assertThat(statistics.getSentMessages(), is(numMessages + 1L));
assertThat(statistics.getReceivedMessages(), is(numMessages + 1L));
Frame textFrame = new Frame(OpCode.TEXT, msgText);
Frame closeFrame = CloseStatus.NORMAL_STATUS.toFrame();
final long textFrameSize = getFrameByteSize(textFrame);
final long closeFrameSize = getFrameByteSize(closeFrame);
final int maskSize = 4; // We use 4 byte mask for client frames in WSConnection
final long expectedSent = upgradeSentBytes + numMessages * textFrameSize + closeFrameSize;
final long expectedReceived = upgradeReceivedBytes + numMessages * (textFrameSize + maskSize) + closeFrameSize + maskSize;
final long expectedSent = numMessages * textFrameSize + closeFrameSize;
final long expectedReceived = numMessages * (textFrameSize + maskSize) + (closeFrameSize + maskSize);
assertThat(statistics.getSentBytes(), is(expectedSent));
assertThat(statistics.getReceivedBytes(), is(expectedReceived));

View File

@ -26,6 +26,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class InvokerUtils
{
public static class Arg
@ -135,6 +138,7 @@ public class InvokerUtils
}
public static final ParamIdentifier PARAM_IDENTITY = new ParamIdentity();
private static final Logger LOG = LoggerFactory.getLogger(InvokerUtils.class);
/**
* Bind optional arguments to provided method handle
@ -425,6 +429,8 @@ public class InvokerUtils
{
if (!throwOnFailure)
{
if (LOG.isDebugEnabled())
LOG.debug("Unable to obtain MethodHandle for " + method, e);
return null;
}

14
pom.xml
View File

@ -44,15 +44,15 @@
<jetty.surefire.argLine>-Dfile.encoding=UTF-8 -Duser.language=en -Duser.region=US -showversion -Xmx2g -Xms2g -Xlog:gc:stderr:time,level,tags</jetty.surefire.argLine>
<!-- some maven plugins versions -->
<maven.surefire.version>3.0.0-M4</maven.surefire.version>
<maven.surefire.version>3.0.0-M5</maven.surefire.version>
<maven.compiler.plugin.version>3.8.1</maven.compiler.plugin.version>
<maven.dependency.plugin.version>3.1.1</maven.dependency.plugin.version>
<maven.resources.plugin.version>3.1.0</maven.resources.plugin.version>
<maven.source.plugin.version>3.0.1</maven.source.plugin.version>
<maven.war.plugin.version>3.2.2</maven.war.plugin.version>
<maven.dependency.plugin.version>3.1.2</maven.dependency.plugin.version>
<maven.resources.plugin.version>3.2.0</maven.resources.plugin.version>
<maven.source.plugin.version>3.2.1</maven.source.plugin.version>
<maven.war.plugin.version>3.3.1</maven.war.plugin.version>
<maven.plugin-tools.version>3.5.2</maven.plugin-tools.version>
<maven.install.plugin.version>2.5.2</maven.install.plugin.version>
<maven.deploy.plugin.version>2.8.2</maven.deploy.plugin.version>
<maven.install.plugin.version>3.0.0-M1</maven.install.plugin.version>
<maven.deploy.plugin.version>3.0.0-M1</maven.deploy.plugin.version>
<!-- testing -->
<it.debug>false</it.debug>