Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.5 changeable ping interval #43

Merged
merged 8 commits into from
Apr 17, 2018
12 changes: 10 additions & 2 deletions ocpp-common/src/main/java/eu/chargetime/ocpp/Communicator.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,15 @@ public void accept(CommunicatorEvents events) {
synchronized public void sendCall(String uniqueId, String action, Request request) {
Object call = makeCall(uniqueId, action, packPayload(request));
try {
if (request.transactionRelated() && transactionQueue.size() > 0) {
if(radio.isClosed()) {
if (request.transactionRelated()) {
logger.warn("Not connected: storing request to queue: {}", request);
transactionQueue.add(call);
} else {
logger.warn("Not connected: can't send request: {}", request);
events.onError(uniqueId, "Not connected", "The request can't be sent due to the lack of connection", request);
}
} else if (request.transactionRelated() && transactionQueue.size() > 0) {
transactionQueue.add(call);
processTransactionQueue();
} else {
Expand All @@ -167,7 +175,7 @@ synchronized public void sendCall(String uniqueId, String action, Request reques
if (request.transactionRelated()) {
transactionQueue.add(call);
} else {
events.onError(uniqueId, "Not connected", "The request couldn't be send due to the lack of connection", request);
events.onError(uniqueId, "Not connected", "The request can't be sent due to the lack of connection", request);
}
}
}
Expand Down
42 changes: 40 additions & 2 deletions ocpp-v1_6/src/main/java/eu/chargetime/ocpp/JSONClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import eu.chargetime.ocpp.feature.profile.Profile;
import eu.chargetime.ocpp.model.Confirmation;
import eu.chargetime.ocpp.model.Request;

import eu.chargetime.ocpp.wss.BaseWssSocketBuilder;
import eu.chargetime.ocpp.wss.WssSocketBuilder;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.util.concurrent.CompletionStage;
Expand Down Expand Up @@ -67,8 +68,45 @@ public JSONClient(ClientCoreProfile coreProfile, String identity) {
featureRepository.addFeatureProfile(coreProfile);
}

/**
* Application composite root for a json client.
* The core feature profile is required as a minimum.
*
* @param coreProfile implementation of the core feature profile.
* @param identity if set, will append identity to url.
* @param wssSocketBuilder to build {@link java.net.Socket} to support wss://.
*/
public JSONClient(ClientCoreProfile coreProfile, String identity, WssSocketBuilder wssSocketBuilder) {
this(coreProfile, identity);
enableWSS(wssSocketBuilder);
}

// To ensure the exposed API is backward compatible
public void enableWSS(SSLContext sslContext) throws IOException {
transmitter.enableWSS(sslContext);
WssSocketBuilder wssSocketBuilder =
BaseWssSocketBuilder.builder().sslSocketFactory(sslContext.getSocketFactory());
enableWSS(wssSocketBuilder);
}

/**
* Enables WSS connection to the endpoint.
* The {@code wssSocketBuilder} must be initialized at that step
* (as required parameters set might vary depending on implementation the {@link eu.chargetime.ocpp.wss.WssSocketBuilder#verify()} is used to ensure initialization).
*
* @param wssSocketBuilder builder to provide SSL socket
* @return instance of {@link JSONClient}
* @throws IllegalStateException in case if the client is already connected
* @throws IllegalStateException in case {@code wssSocketBuilder} not initialized properly
*/
public JSONClient enableWSS(WssSocketBuilder wssSocketBuilder) {
wssSocketBuilder.verify();
transmitter.enableWSS(wssSocketBuilder);
return this;
}

public void setPingInterval(int interval) {
// Set ping interval in seconds
transmitter.setPingInterval(interval);
}

@Override
Expand Down
43 changes: 41 additions & 2 deletions ocpp-v1_6/src/main/java/eu/chargetime/ocpp/JSONServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ of this software and associated documentation files (the "Software"), to deal
import eu.chargetime.ocpp.feature.profile.ServerCoreProfile;
import eu.chargetime.ocpp.model.Confirmation;
import eu.chargetime.ocpp.model.Request;
import eu.chargetime.ocpp.wss.BaseWssFactoryBuilder;
import eu.chargetime.ocpp.wss.WssFactoryBuilder;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.CompletionStage;

Expand All @@ -42,6 +45,7 @@ public class JSONServer implements IServerAPI {

/**
* The core feature profile is required as a minimum.
* The constructor creates WS-ready server.
*
* @param coreProfile implementation of the core feature profile.
*/
Expand All @@ -53,8 +57,43 @@ public JSONServer(ServerCoreProfile coreProfile) {
featureRepository.addFeatureProfile(coreProfile);
}

public void enableWSS(SSLContext sslContext) {
listener.enableWSS(sslContext);
/**
* The core feature profile is required as a minimum.
* The constructor creates WSS-ready server.
*
* @param coreProfile implementation of the core feature profile.
* @param wssFactoryBuilder to build {@link org.java_websocket.WebSocketServerFactory} to support wss://.
*/
public JSONServer(ServerCoreProfile coreProfile, WssFactoryBuilder wssFactoryBuilder) {
this(coreProfile);
enableWSS(wssFactoryBuilder);
}

// To ensure the exposed API is backward compatible
public void enableWSS(SSLContext sslContext) throws IOException {
WssFactoryBuilder builder = BaseWssFactoryBuilder.builder().sslContext(sslContext);
enableWSS(builder);
}

/**
* Enables server to accept WSS connections.
* The {@code wssFactoryBuilder} must be initialized at that step
* (as required parameters set might vary depending on implementation the {@link eu.chargetime.ocpp.wss.WssFactoryBuilder#verify()} is used to ensure initialization).
*
* @param wssFactoryBuilder builder to provide WebSocketServerFactory
* @return instance of {@link JSONServer}
* @throws IllegalStateException in case if the server is already connected
* @throws IllegalStateException in case {@code wssFactoryBuilder} not initialized properly
*/
public JSONServer enableWSS(WssFactoryBuilder wssFactoryBuilder) {
wssFactoryBuilder.verify();
listener.enableWSS(wssFactoryBuilder);
return this;
}

public void setPingInterval(int interval) {
// Set ping interval in seconds
listener.setPingInterval(interval);
}

@Override
Expand Down
33 changes: 29 additions & 4 deletions ocpp-v1_6/src/main/java/eu/chargetime/ocpp/WebSocketListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ of this software and associated documentation files (the "Software"), to deal
*/

import eu.chargetime.ocpp.model.SessionInformation;

import eu.chargetime.ocpp.wss.WssFactoryBuilder;
import org.java_websocket.WebSocket;
import org.java_websocket.drafts.Draft;
import org.java_websocket.drafts.Draft_6455;
Expand All @@ -48,7 +48,11 @@ public class WebSocketListener implements Listener {
private static final Logger logger = LoggerFactory.getLogger(WebSocketListener.class);
private final IServerSessionFactory sessionFactory;

// In seconds
private int pingInterval = 60;

private WebSocketServer server;
private WssFactoryBuilder wssFactoryBuilder;
private HashMap<WebSocket, WebSocketReceiver> sockets;
private volatile boolean closed = true;
private boolean handleRequestAsync;
Expand Down Expand Up @@ -107,12 +111,31 @@ public void onStart() {

}
};

if(wssFactoryBuilder != null) {
server.setWebSocketFactory(wssFactoryBuilder.build());
}

server.setConnectionLostTimeout(pingInterval);

server.start();
closed = false;
}

public void enableWSS(SSLContext sslContext) {
server.setWebSocketFactory(new DefaultSSLWebSocketServerFactory(sslContext));
public void enableWSS(WssFactoryBuilder wssFactoryBuilder) {
if(server != null) {
throw new IllegalStateException("Cannot enable WSS on already running server");
}

this.wssFactoryBuilder = wssFactoryBuilder;
}

public void setPingInterval(int interval) {
this.pingInterval = pingInterval;

if(server != null) {
server.setConnectionLostTimeout(interval);
}
}

@Override
Expand All @@ -127,7 +150,9 @@ public void close() {
server.stop(1);

} catch (InterruptedException e) {
logger.info("close() failed", e);
logger.error("Failed to close listener", e);
} finally {
server = null;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package eu.chargetime.ocpp;/*
package eu.chargetime.ocpp;
/*
ChargeTime.eu - Java-OCA-OCPP

MIT License
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ of this software and associated documentation files (the "Software"), to deal
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

*/

import eu.chargetime.ocpp.wss.WssSocketBuilder;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.exceptions.WebsocketNotConnectedException;
Expand All @@ -54,16 +54,21 @@ public class WebSocketTransmitter implements Transmitter
{
private static final Logger logger = LoggerFactory.getLogger(WebSocketTransmitter.class);

public static final String WSS_SCHEME = "wss";
// In seconds
private int pingInterval = 60;
private volatile boolean closed = true;
private WebSocketClient client;
private WssSocketBuilder wssSocketBuilder;

public WebSocketTransmitter() {
}

@Override
public void connect(String uri, RadioEvents events) {
Draft_6455 draft = new Draft_6455(Collections.<IExtension>emptyList(), Collections.<IProtocol>singletonList(new Protocol("ocpp1.6")));
client = new WebSocketClient(URI.create(uri), draft) {
final URI resource = URI.create(uri);
Draft_6455 draft = new Draft_6455(Collections.<IExtension>emptyList(), Collections.<IProtocol>singletonList(new Protocol("ocpp1.6")));
client = new WebSocketClient(resource, draft) {
@Override
public void onOpen(ServerHandshake serverHandshake)
{
Expand Down Expand Up @@ -93,6 +98,24 @@ public void onError(Exception ex)
}
}
};

if(WSS_SCHEME.equals(resource.getScheme())) {

if(wssSocketBuilder == null) {
throw new IllegalStateException("wssSocketBuilder must be set to support " + WSS_SCHEME + " scheme");
}

try {
client.setSocket(wssSocketBuilder
.uri(resource)
.build());
} catch (IOException ex) {
logger.error("SSL socket creation failed", ex);
}
}

client.setConnectionLostTimeout(pingInterval);

try {
client.connectBlocking();
closed = false;
Expand All @@ -101,19 +124,35 @@ public void onError(Exception ex)
}
}

public void enableWSS(SSLContext sslContext) throws IOException {
SSLSocketFactory factory = sslContext.getSocketFactory();
client.setSocket(factory.createSocket());
public void enableWSS(WssSocketBuilder wssSocketBuilder) {

if(client != null) {
throw new IllegalStateException("Cannot enable WSS on already connected client");
}

this.wssSocketBuilder = wssSocketBuilder;
}

public void setPingInterval(int interval) {
this.pingInterval = pingInterval;

if(client != null) {
client.setConnectionLostTimeout(interval);
}
}

@Override
public void disconnect()
{
if(client == null) {
return;
}
try {
client.closeBlocking();
} catch (Exception ex) {
logger.info("client.closeBlocking() failed", ex);
} finally {
client = null;
closed = true;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package eu.chargetime.ocpp.wss;

/*
ubitricity.com - Java-OCA-OCPP

MIT License

Copyright (C) 2018 Evgeny Pakhomov <eugene.pakhomov@ubitricity.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/


import org.java_websocket.WebSocketServerFactory;
import org.java_websocket.server.DefaultSSLWebSocketServerFactory;

import javax.net.ssl.SSLContext;
import java.util.List;

/**
* Base implementation of WssFactoryBuilder.
*/
public class BaseWssFactoryBuilder implements WssFactoryBuilder {

private SSLContext sslContext;
private List<String> ciphers;

private BaseWssFactoryBuilder() {}

public static BaseWssFactoryBuilder builder() {
return new BaseWssFactoryBuilder();
}

public BaseWssFactoryBuilder ciphers(List<String> ciphers) {
this.ciphers = ciphers;
return this;
}

public BaseWssFactoryBuilder sslContext(SSLContext sslContext) {
this.sslContext = sslContext;
return this;
}

@Override
public WebSocketServerFactory build() {
return ciphers == null
? new DefaultSSLWebSocketServerFactory(sslContext)
: new CustomSSLWebSocketServerFactory(sslContext, ciphers);
}

@Override
public void verify() {
if(sslContext == null) {
throw new IllegalStateException("sslContext must be set");
}
}
}
Loading