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

Naive implementation of UpgradeProtocol #38

Open
wants to merge 12 commits into
base: jtango-9-lts
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,10 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
</dependencies>
</project>
2 changes: 2 additions & 0 deletions common/src/main/java/fr/esrf/Tango/factory/ITangoFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
package fr.esrf.Tango.factory;

import fr.esrf.TangoApi.*;
import org.tango.transport.Transport;

public interface ITangoFactory {

Expand All @@ -70,4 +71,5 @@ public interface ITangoFactory {

public String getFactoryName();

Transport newTransport(String targetProtocol);
}
5 changes: 5 additions & 0 deletions common/src/main/java/fr/esrf/Tango/factory/TangoFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
package fr.esrf.Tango.factory;

import fr.esrf.TangoApi.*;
import org.tango.transport.Transport;

import java.io.BufferedInputStream;
import java.io.InputStream;
Expand Down Expand Up @@ -203,4 +204,8 @@ public boolean isDefaultFactory() {
public void setDefaultFactory(final boolean isDefaultFactory) {
this.isDefaultFactory = isDefaultFactory;
}

public Transport newTransport(String targetProtocol) {
return tangoFactory.newTransport(targetProtocol);
}
}
72 changes: 68 additions & 4 deletions common/src/main/java/fr/esrf/TangoApi/DeviceProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,15 @@
import fr.esrf.TangoDs.Except;
import fr.esrf.TangoDs.TangoConst;
import org.omg.CORBA.Request;
import org.tango.network.NetworkUtils;
import org.tango.transport.DefaultTransport;
import org.tango.transport.GsonTangoMessage;
import org.tango.transport.TangoMessage;
import org.tango.transport.Transport;
import org.tango.utils.DevFailedUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.io.IOException;
import java.util.*;

/**
* Class Description: This class manage device connection for Tango objects. It
Expand Down Expand Up @@ -80,6 +84,8 @@ public class DeviceProxy extends Connection implements ApiDefs {

private IDeviceProxyDAO deviceProxyDAO = null;

private final GsonTangoMessage marshaller = new GsonTangoMessage();

static final private boolean check_idl = false;

/**
Expand Down Expand Up @@ -782,7 +788,26 @@ public void set_attribute_config(AttributeInfo[] attr) throws DevFailed {
deviceProxyDAO.set_attribute_info(this, attr);
}

private Transport transport = new DefaultTransport();

// ==========================================================================

public double readAttributeDouble(String name) throws DevFailed {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lacking documentation

if (transport.isConnected()) {
try {
TangoMessage<Double> message = new TangoMessage<>("read", this.get_name(), name, TangoConst.Tango_DEV_DOUBLE, 0D);

message = marshaller.unmarshal(transport.send(marshaller.marshal(message)));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug log entry here?


return message.value;
} catch (IOException e) {
throw DevFailedUtils.newDevFailed(e);
}
} else {
return read_attribute(name).extractDouble();
}
}

/**
* Read the attribute value for the specified device.
*
Expand Down Expand Up @@ -818,6 +843,21 @@ public DeviceAttribute[] read_attribute(String[] attnames) throws DevFailed {
return deviceProxyDAO.read_attribute(this, attnames);
}

//TODO extract and decorate
public void writeAttribute(String name, double value) throws DevFailed {
if (transport.isConnected()) {
try {
TangoMessage<Double> message = new TangoMessage<>("write", this.get_name(), name, TangoConst.Tango_DEV_DOUBLE, value);

transport.send(marshaller.marshal(message));
} catch (IOException e) {
throw DevFailedUtils.newDevFailed(e);
}
}
DeviceAttribute deviceAttribute = new DeviceAttribute(name, value);
write_attribute(deviceAttribute);
}

// ==========================================================================
/**
* Write the attribute value for the specified device.
Expand Down Expand Up @@ -1825,6 +1865,26 @@ public int subscribe_event(int event, int max_size, boolean stateless) throws De
return deviceProxyDAO.subscribe_event(this, event, max_size, stateless);
}

public void upgradeProtocol(String targetProtocol) throws DevFailed {
DeviceData argin = new DeviceData();
argin.insert(targetProtocol);

DeviceData response = this.get_adm_dev().command_inout("UpgradeProtocol", argin);

String[] array = response.extractStringArray();

try {
String endpoint = Arrays.stream(array)
.filter(NetworkUtils.getInstance()::checkEndpoint)
.findFirst()
.orElseThrow(() -> new IOException("No reachable connection points were found"));
transport = TangoFactory.getSingleton().newTransport(targetProtocol);
transport.connect(endpoint);
} catch (IOException e) {
throw DevFailedUtils.newDevFailed(e);
}
}

// ==========================================================================
// ==========================================================================
public void setEventQueue(EventQueue eq) {
Expand Down Expand Up @@ -2020,6 +2080,10 @@ private static void checkDuplication(String[] list, String orig) throws DevFaile
*/
//==========================================================================
protected void finalize() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finalize is deprecated in Java 9 and is discouraged even before. Should use AutoCloseable and implement close mrthod.

try {
transport.close();
} catch (IOException ignored) {
}
if (proxy_lock_cnt>0) {
try {
unlock();
Expand Down
94 changes: 94 additions & 0 deletions common/src/main/java/org/tango/network/NetworkUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package org.tango.network;

import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest remove Guava if possible. Using mix of Guava and Java 8 is confusing. Most Guava functions have analogues in Java 8.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.*;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
* @author Igor Khokhriakov <[email protected]>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Short documentation required

* @since 18.02.2020
*/
public class NetworkUtils {
private static final NetworkUtils INSTANCE = new NetworkUtils();
private final Logger logger = LoggerFactory.getLogger(NetworkUtils.class);

private NetworkUtils() {
}

public static NetworkUtils getInstance() {
return INSTANCE;
}

public int getRandomPort() {
//TODO check if available
return new Random().ints(5000, 65535).findFirst().getAsInt();
}

public List<String> getIp4Addresses() {
Iterable<NetworkInterface> networkInterfaces = null;
try {
networkInterfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
} catch (SocketException e) {
logger.error("Failed to get NICs due to " + e.getMessage(), e);
return Collections.emptyList();
}

java.util.function.Predicate<NetworkInterface> filter = networkInterface -> {
try {
return !networkInterface.isLoopback() && !networkInterface.isVirtual() && networkInterface.isUp();
} catch (SocketException e) {
logger.warn("Ignoring NetworkInterface({}) due to an exception: {}", networkInterface.getName(), e);
return false;
}
};

Function<InterfaceAddress, String> interfaceAddressToString = interfaceAddress -> interfaceAddress.getAddress().getHostAddress();

Iterable<NetworkInterface> filteredNICs = Iterables.filter(networkInterfaces, filter::test);

List<String> result = Lists.newArrayList();
//TODO #17
for (NetworkInterface nic : filteredNICs) {
result.addAll(
Collections2.filter(
nic.getInterfaceAddresses()
.stream()
.map(interfaceAddressToString::apply)
.collect(Collectors.toList()),
s -> s.split("\\.").length == 4)
);
}
return result;
}

public boolean checkEndpoint(String endpoint) {
logger.debug("Check endpoint: {}", endpoint);
URI uri = null;
try {
uri = new URI(endpoint);
} catch (URISyntaxException e) {
logger.debug("Bad endpoint: " + endpoint, e);
return false;
}

// Try to connect
InetSocketAddress ip = new InetSocketAddress(uri.getHost(), uri.getPort());
try (Socket socket = new Socket()) {
socket.connect(ip, 10);
return true;
} catch (IOException e) {
logger.debug("Failed to connect to " + ip, e);
return false;
}
}
}
34 changes: 34 additions & 0 deletions common/src/main/java/org/tango/transport/DefaultTransport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.tango.transport;

import java.io.IOException;

/**
* @author Igor Khokhriakov <[email protected]>
* @since 21.02.2020
*/
public class DefaultTransport implements Transport {
@Override
public boolean isConnected() {
return false;
}

@Override
public void connect(String endpoint) throws IOException {
throw new UnsupportedOperationException("This method is not supported in " + this.getClass());
}

@Override
public void disconnect(String endpoint) throws IOException {
throw new UnsupportedOperationException("This method is not supported in " + this.getClass());
}

@Override
public byte[] send(byte[] data) throws IOException {
throw new UnsupportedOperationException("This method is not supported in " + this.getClass());
}

@Override
public void close() throws IOException {
throw new UnsupportedOperationException("This method is not supported in " + this.getClass());
}
}
40 changes: 40 additions & 0 deletions common/src/main/java/org/tango/transport/GsonTangoMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.tango.transport;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

/**
* @author Igor Khokhriakov <[email protected]>
* @since 21.02.2020
*/
public class GsonTangoMessage implements TangoMessageMarshaller, TangoMessageUnmarshaller {
private final Gson gson = new GsonBuilder()
.serializeNulls()
.create();


@Override
public byte[] marshal(TangoMessage tangoMessage) {
return gson.toJson(tangoMessage).getBytes(StandardCharsets.UTF_8);
}

@Override
public TangoMessage unmarshal(InputStream stream) {
return gson.fromJson(new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)), TangoMessage.class);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential bug here. If there are subsequent reads from the same stream, BufferedReader will read its buffer size and the next reader will start in the wrong place. If the stream is exhausted after read, it should be closed.

}

@Override
public TangoMessage unmarshal(byte[] stream) {
return unmarshal(new String(stream, StandardCharsets.UTF_8));
}

@Override
public TangoMessage unmarshal(String stream) {
return gson.fromJson(stream, TangoMessage.class);
}
}
68 changes: 68 additions & 0 deletions common/src/main/java/org/tango/transport/StringTangoMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.tango.transport;

import fr.esrf.TangoDs.TangoConst;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
* @author Igor Khokhriakov <[email protected]>
* @since 21.02.2020
*/
public class StringTangoMessage implements TangoMessageMarshaller, TangoMessageUnmarshaller {
public static final String PATTERN_STR = "(read|write|exec|response);([\\w\\W]+)/([\\w\\W]+);(\\d+):([\\w.]+)";
public static final Pattern PATTERN = Pattern.compile(PATTERN_STR);

@Override
public byte[] marshal(TangoMessage tangoMessage) {
return toString(tangoMessage).getBytes(StandardCharsets.UTF_8);
}

@Override
public TangoMessage unmarshal(InputStream stream) {
BufferedReader br = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same as above

return fromString(br.lines().collect(Collectors.joining(System.lineSeparator())));
}

@Override
public TangoMessage unmarshal(byte[] stream) {
return unmarshal(new ByteArrayInputStream(stream));
}

@Override
public TangoMessage unmarshal(String stream) {
return fromString(stream);
}

private String toString(TangoMessage message) {
return String.format(
"%s;%s/%s;%d:%s", message.action, message.device, message.target, message.dataType, String.valueOf(message.value)
);
}

private TangoMessage fromString(String str) {
Matcher matcher = PATTERN.matcher(str);

if (matcher.matches()) {
int dataType = Integer.parseInt(matcher.group(4));
return new TangoMessage(matcher.group(1), matcher.group(2), matcher.group(3), dataType, parseValue(dataType, matcher.group(5)));
} else
throw new IllegalArgumentException("Unrecognized message: " + str);
}

private Object parseValue(int dataType, String value) {
switch (dataType) {
case TangoConst
.Tango_DEV_DOUBLE:
return Double.valueOf(value);
default:
return value;
}
}
}
Loading