You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by jf...@apache.org on 2018/10/27 06:55:32 UTC
[incubator-plc4x] branch master updated: [OPM] Moved connection
string from Entity to the connect / read methods.
This is an automated email from the ASF dual-hosted git repository.
jfeinauer pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-plc4x.git
The following commit(s) were added to refs/heads/master by this push:
new 5b80c1b [OPM] Moved connection string from Entity to the connect / read methods.
5b80c1b is described below
commit 5b80c1bad969f95e1ff7deae274d45e617cc8535
Author: Julian Feinauer <j....@pragmaticminds.de>
AuthorDate: Sat Oct 27 08:53:44 2018 +0200
[OPM] Moved connection string from Entity to the connect / read methods.
---
.../java/org/apache/plc4x/java/opm/PlcEntity.java | 1 -
.../apache/plc4x/java/opm/PlcEntityManager.java | 76 ++++++++++++++--------
.../plc4x/java/opm/PlcEntityManagerTest.java | 18 ++---
3 files changed, 58 insertions(+), 37 deletions(-)
diff --git a/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntity.java b/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntity.java
index c8cfef4..479f0a5 100644
--- a/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntity.java
+++ b/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntity.java
@@ -30,5 +30,4 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface PlcEntity {
- String value();
}
diff --git a/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntityManager.java b/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntityManager.java
index ccb8e3d..487a479 100644
--- a/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntityManager.java
+++ b/plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntityManager.java
@@ -20,6 +20,7 @@
package org.apache.plc4x.java.opm;
import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.*;
import org.apache.commons.configuration2.Configuration;
@@ -59,22 +60,22 @@ import static net.bytebuddy.matcher.ElementMatchers.any;
* First, the necessary annotations are {@link PlcEntity} and {@link PlcField}.
* For a class to be usable as PlcEntity it needs
* <ul>
- * <li>be non-final (as proxiing has to be used in case of {@link #connect(Class)}</li>
+ * <li>be non-final (as proxiing has to be used in case of {@link #connect(Class, String)}</li>
* <li>a public no args constructor for instanciation</li>
* <li>Needs to be annotated with {@link PlcEntity} and has a valid value which is the connection string</li>
* </ul>
* <p>
- * Basically, the {@link PlcEntityManager} has to operation "modes" represented by the methods {@link #read(Class)} and
- * {@link #connect(Class)}.
+ * Basically, the {@link PlcEntityManager} has to operation "modes" represented by the methods {@link #read(Class,String)} and
+ * {@link #connect(Class,String)}.
* <p>
* For a field to get Values from the Plc Injected it needs to be annotated with the {@link PlcField} annotation.
* The value has to be the plc fields string (which is inserted in the {@link PlcReadRequest}).
* The connection string is taken from the value of the {@link PlcEntity} annotation on the class.
* <p>
- * The {@link #read(Class)} method has no direkt equivalent in JPA (as far as I know) as it only returns a "detached"
+ * The {@link #read(Class,String)} method has no direkt equivalent in JPA (as far as I know) as it only returns a "detached"
* entity. This means it fetches all values from the plc that are annotated wiht the {@link PlcField} annotations.
* <p>
- * The {@link #connect(Class)} method is more JPA-like as it returns a "connected" entity. This means, that each
+ * The {@link #connect(Class,String)} method is more JPA-like as it returns a "connected" entity. This means, that each
* time one of the getters on the returned entity is called a call is made to the plc (and the field value is changed
* for this specific field).
* Furthermore, if a method which is no getter is called, then all {@link PlcField}s are refreshed before doing the call.
@@ -89,6 +90,7 @@ public class PlcEntityManager {
private static final Configuration CONF = new SystemConfiguration();
private static final long READ_TIMEOUT = CONF.getLong("org.apache.plc4x.java.opm.entity_manager.read_timeout", 1_000);
+ public static final String PLC_ADDRESS_FIELD_NAME = "_plcAddress";
private final PlcDriverManager driverManager;
@@ -100,13 +102,12 @@ public class PlcEntityManager {
this.driverManager = driverManager;
}
- public <T> T read(Class<T> clazz) throws OPMException {
+ public <T> T read(Class<T> clazz, String address) throws OPMException {
PlcEntity annotation = getPlcEntityAndCheckPreconditions(clazz);
- String source = annotation.value();
- try (PlcConnection connection = driverManager.getConnection(source)) {
+ try (PlcConnection connection = driverManager.getConnection(address)) {
if (!connection.getMetadata().canRead()) {
- throw new OPMException("Unable to get Reader for connection with url '" + source + "'");
+ throw new OPMException("Unable to get Reader for connection with url '" + address + "'");
}
PlcReadRequest.Builder requestBuilder = connection.readRequestBuilder();
@@ -138,7 +139,7 @@ public class PlcEntityManager {
} catch (PlcInvalidFieldException e) {
throw new OPMException("Unable to parse one field request", e);
} catch (PlcConnectionException e) {
- throw new OPMException("Unable to get connection with url '" + source + "'", e);
+ throw new OPMException("Unable to get connection with url '" + address + "'", e);
} catch (InstantiationException | InvocationTargetException | NoSuchMethodException | NoSuchFieldException | IllegalAccessException e) {
throw new OPMException("Unable to fetch PlcEntity " + clazz.getName(), e);
} catch (Exception e) {
@@ -154,21 +155,29 @@ public class PlcEntityManager {
* @return a connected entity.
* @throws OPMException when proxy can't be build.
*/
- public <T> T connect(Class<T> clazz) throws OPMException {
+ public <T> T connect(Class<T> clazz, String address) throws OPMException {
getPlcEntityAndCheckPreconditions(clazz);
try {
// Use Byte Buddy to generate a subclassed proxy that delegates all PlcField Methods
// to the intercept method
- return new ByteBuddy()
+ T instance = new ByteBuddy()
.subclass(clazz)
+ .defineField(PLC_ADDRESS_FIELD_NAME, String.class, Visibility.PRIVATE)
.method(any()).intercept(MethodDelegation.to(this))
.make()
.load(Thread.currentThread().getContextClassLoader())
.getLoaded()
.getConstructor()
.newInstance();
+ // Set connection value into the private field
+ Field plcAddress = instance.getClass().getDeclaredField(PLC_ADDRESS_FIELD_NAME);
+ plcAddress.setAccessible(true);
+ plcAddress.set(instance, address);
+ return instance;
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new OPMException("Unable to instantiate Proxy", e);
+ } catch (NoSuchFieldException e) {
+ throw new IllegalStateException("Problem with field injection during proxy generation", e);
}
}
@@ -195,9 +204,9 @@ public class PlcEntityManager {
/**
* Basic Intersector for all methods on the proxy object.
* It checks if the invoked method is a getter and if so, only retrieves the requested field, forwarding to
- * the {@link #fetchValueForGetter(Object, Method)} method.
+ * the {@link #fetchValueForGetter(Method, String)} method.
* <p>
- * If the field is no getter, then all fields are refreshed by calling {@link #refetchAllFields(Object)}
+ * If the field is no getter, then all fields are refreshed by calling {@link #refetchAllFields(Object,String)}
* and then, the method is invoked.
*
* @param proxy Object to intercept
@@ -212,6 +221,9 @@ public class PlcEntityManager {
public Object intercept(@This Object proxy, @Origin Method method, @SuperCall Callable<?> callable, @Super Object entity) throws OPMException {
LOGGER.trace("Invoked method {} on connected PlcEntity {}", method.getName(), entity);
+ // Fetch connection from internal variable
+ String address = extractAddress(proxy);
+
if (method.getName().startsWith("get")) {
if (method.getParameterCount() > 0) {
throw new OPMException("Only getter with no arguments are supported");
@@ -219,7 +231,7 @@ public class PlcEntityManager {
// Fetch single value
LOGGER.trace("Invoked method {} is getter, trying to find annotated field and return requested value",
method.getName());
- return fetchValueForGetter(entity, method);
+ return fetchValueForGetter(method, address);
}
if (method.getName().startsWith("is") && (method.getReturnType() == boolean.class || method.getReturnType() == Boolean.class)) {
@@ -229,13 +241,13 @@ public class PlcEntityManager {
// Fetch single value
LOGGER.trace("Invoked method {} is boolean flag method, trying to find annotated field and return requested value",
method.getName());
- return fetchValueForIsGetter(entity, method);
+ return fetchValueForIsGetter(method, address);
}
// Fetch all values, than invoke method
try {
LOGGER.trace("Invoked method is no getter, refetch all fields and invoke method {} then", method.getName());
- refetchAllFields(proxy);
+ refetchAllFields(proxy, address);
return callable.call();
} catch (Exception e) {
throw new OPMException("Unable to forward invocation " + method.getName() + " on connected PlcEntity", e);
@@ -248,7 +260,7 @@ public class PlcEntityManager {
* @param proxy Object to refresh the fields on.
* @throws OPMException on various errors.
*/
- private void refetchAllFields(Object proxy) throws OPMException {
+ private void refetchAllFields(Object proxy, String address) throws OPMException {
// Don't log o here as this would cause a second request against a plc so don't touch it, or if you log be aware of that
Class<?> entityClass = proxy.getClass().getSuperclass();
PlcEntity plcEntity = entityClass.getAnnotation(PlcEntity.class);
@@ -256,7 +268,7 @@ public class PlcEntityManager {
throw new OPMException("Non PlcEntity supplied");
}
- try (PlcConnection connection = driverManager.getConnection(plcEntity.value())) {
+ try (PlcConnection connection = driverManager.getConnection(address)) {
// Catch the exception, if no reader present (see below)
// Build the query
PlcReadRequest.Builder requestBuilder = connection.readRequestBuilder();
@@ -291,17 +303,28 @@ public class PlcEntityManager {
}
}
+ private static String extractAddress(Object proxy) throws OPMException {
+ String address;
+ try {
+ Field field = proxy.getClass().getDeclaredField(PLC_ADDRESS_FIELD_NAME);
+ field.setAccessible(true);
+ address = (String) field.get(proxy);
+ } catch (IllegalAccessException | NoSuchFieldException e) {
+ throw new OPMException("Problem with accessing internal plc address", e);
+ }
+ return address;
+ }
+
- private Object fetchValueForIsGetter(Object o, Method m) throws OPMException {
- return fetchValueForGetter(o, m, 2);
+ private Object fetchValueForIsGetter(Method m, String address) throws OPMException {
+ return fetchValueForGetter(m, 2, address);
}
- private Object fetchValueForGetter(Object o, Method m) throws OPMException {
- return fetchValueForGetter(o, m, 3);
+ private Object fetchValueForGetter(Method m, String address) throws OPMException {
+ return fetchValueForGetter(m, 3, address);
}
- // TODO: why isn't o used?
- private Object fetchValueForGetter(Object o, Method m, int prefixLength) throws OPMException {
+ private Object fetchValueForGetter(Method m, int prefixLength, String address) throws OPMException {
String s = m.getName().substring(prefixLength);
// First char to lower
String variable = s.substring(0, 1).toLowerCase().concat(s.substring(1));
@@ -312,8 +335,7 @@ public class PlcEntityManager {
} catch (NoSuchFieldException e) {
throw new OPMException("Unable to identify field annotated field for call to " + m.getName(), e);
}
- PlcEntity plcEntity = m.getDeclaringClass().getAnnotation(PlcEntity.class);
- try (PlcConnection connection = driverManager.getConnection(plcEntity.value())) {
+ try (PlcConnection connection = driverManager.getConnection(address)) {
// Catch the exception, if no reader present (see below)
// Assume to do the query here...
diff --git a/plc4j/utils/opm/src/test/java/org/apache/plc4x/java/opm/PlcEntityManagerTest.java b/plc4j/utils/opm/src/test/java/org/apache/plc4x/java/opm/PlcEntityManagerTest.java
index d43604d..8017d9c 100644
--- a/plc4j/utils/opm/src/test/java/org/apache/plc4x/java/opm/PlcEntityManagerTest.java
+++ b/plc4j/utils/opm/src/test/java/org/apache/plc4x/java/opm/PlcEntityManagerTest.java
@@ -62,14 +62,14 @@ public class PlcEntityManagerTest {
public void noEntity_throws() throws OPMException {
PlcEntityManager manager = new PlcEntityManager();
- manager.read(NoEntity.class);
+ manager.read(NoEntity.class, "s7://localhost:5555/0/0");
}
@Test(expected = IllegalArgumentException.class)
public void noValidConstructor_throws() throws OPMException {
PlcEntityManager manager = new PlcEntityManager();
- manager.read(EntityWithBadConstructor.class);
+ manager.read(EntityWithBadConstructor.class, "s7://localhost:5555/0/0");
}
@Test
@@ -80,7 +80,7 @@ public class PlcEntityManagerTest {
results.put(prefix + "counter2", new DefaultLongFieldItem(1l));
PlcEntityManager manager = getPlcEntityManager(results);
- MyEntity myEntity = manager.read(MyEntity.class);
+ MyEntity myEntity = manager.read(MyEntity.class, "s7://localhost:5555/0/0");
assertEquals(1, (long) myEntity.getCounter());
assertEquals(1, myEntity.getCounter2());
@@ -112,7 +112,7 @@ public class PlcEntityManagerTest {
map.put(prefix + "stringVar", new DefaultStringFieldItem("Hallo"));
PlcEntityManager manager = getPlcEntityManager(map);
- ConnectedEntity connect = manager.read(ConnectedEntity.class);
+ ConnectedEntity connect = manager.read(ConnectedEntity.class, "s7://localhost:5555/0/0");
Assert.assertNotNull(connect);
@@ -148,7 +148,7 @@ public class PlcEntityManagerTest {
map.put(prefix + "stringVar", new DefaultStringFieldItem("Hallo"));
PlcEntityManager manager = getPlcEntityManager(map);
- ConnectedEntity connect = manager.connect(ConnectedEntity.class);
+ ConnectedEntity connect = manager.connect(ConnectedEntity.class, "s7://localhost:5555/0/0");
Assert.assertNotNull(connect);
@@ -166,7 +166,7 @@ public class PlcEntityManagerTest {
map.put("isBoolVar", new DefaultBooleanFieldItem(true));
PlcEntityManager manager = getPlcEntityManager(map);
- ConnectedEntity connect = manager.connect(ConnectedEntity.class);
+ ConnectedEntity connect = manager.connect(ConnectedEntity.class, "s7://localhost:5555/0/0");
Assert.assertNotNull(connect);
@@ -298,7 +298,7 @@ public class PlcEntityManagerTest {
}
- @PlcEntity("source")
+ @PlcEntity()
public static class EntityWithBadConstructor {
@PlcField("asdf")
@@ -313,7 +313,7 @@ public class PlcEntityManagerTest {
}
}
- @PlcEntity("s7://localhost:5555/0/0")
+ @PlcEntity()
public static class MyEntity {
@PlcField("%DB3.DBW500")
@@ -332,7 +332,7 @@ public class PlcEntityManagerTest {
}
- @PlcEntity("s7://localhost:5555/0/0")
+ @PlcEntity()
public static class ConnectedEntity {
@PlcField("%DB1.DW111:BOOL")