sruehl commented on a change in pull request #29: Opm plcentitymanager URL: https://github.com/apache/incubator-plc4x/pull/29#discussion_r228230930
########## File path: plc4j/utils/opm/src/main/java/org/apache/plc4x/java/opm/PlcEntityManager.java ########## @@ -0,0 +1,380 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +package org.apache.plc4x.java.opm; + +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.*; +import org.apache.plc4x.java.PlcDriverManager; +import org.apache.plc4x.java.api.connection.PlcConnection; +import org.apache.plc4x.java.api.connection.PlcReader; +import org.apache.plc4x.java.api.exceptions.PlcConnectionException; +import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException; +import org.apache.plc4x.java.api.exceptions.PlcRuntimeException; +import org.apache.plc4x.java.api.messages.PlcReadRequest; +import org.apache.plc4x.java.api.messages.PlcReadResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +/** + * Plc4x equivalent of Jpas EntityManager for implementing Object-Plc-Mapping. + * This means that calls to a plc can be done by using plain POJOs with Annotations. + * <p> + * 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>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)}. + * <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" + * 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 + * 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. + * Thus, all operations on fields that are annotated with {@link PlcField} are always done against the "live" values + * from the PLC. + * <p> + * // TODO Add detach method + */ +public class PlcEntityManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(PlcEntityManager.class); + + private final PlcDriverManager driverManager; + + public PlcEntityManager() { + this.driverManager = new PlcDriverManager(); + } + + public PlcEntityManager(PlcDriverManager driverManager) { + this.driverManager = driverManager; + } + + public <T> T read(Class<T> clazz) throws OPMException { + PlcEntity annotation = getPlcEntityAndCheckPreconditions(clazz); + String source = annotation.value(); + + try (PlcConnection connection = driverManager.getConnection(source)) { + + if (!connection.getReader().isPresent()) { + throw new OPMException("Unable to get Reader for connection with url '" + source + "'"); + } + + PlcReader reader = connection.getReader().get(); + + PlcReadRequest.Builder requestBuilder = reader.readRequestBuilder(); + + // Do the necessary queries for all fields + // HashMap<ReadRequestItem<?>, Field> requestItems = new HashMap<>(); + for (Field field : clazz.getDeclaredFields()) { + PlcField fieldAnnotation = field.getAnnotation(PlcField.class); + if (fieldAnnotation == null) { + // Ignore that field + continue; + } + // Create the suitable Request + String query = fieldAnnotation.value(); + requestBuilder.addItem(field.getName(), query); + } + + // Build the request + PlcReadRequest request; + try { + request = requestBuilder.build(); + } catch (PlcInvalidFieldException e) { + throw new OPMException("Unable to parse one field request", e); + } + + // Perform the request + PlcReadResponse<?> response; + try { + // TODO: make configurable. + response = reader.read(request).get(1_000, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException e) { + throw new OPMException("Request fetching not able", e); + } catch (TimeoutException e) { + throw new OPMException("Timeout during fetching values", e); + } + + // Construct the Object + T instance = clazz.getConstructor().newInstance(); + + // Fill all requested fields + for (String fieldName : response.getFieldNames()) { + setField(clazz, instance, response, fieldName); + } + return instance; + } catch (PlcConnectionException e) { + throw new OPMException("Unable to get connection with url '" + source + "'", e); + } catch (Exception e) { + throw new OPMException("Unable to fetch PlcEntity " + clazz.getName(), e); + } + } + + /** + * Returns a connected proxy. + * + * @param clazz clazz to be connected. + * @param <T> type of param {@code clazz}. + * @return a connected entity. + * @throws OPMException when proxy can't be build. + */ + public <T> T connect(Class<T> clazz) throws OPMException { Review comment: confuses me a bit. Is this the same as merge in jpa? ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org With regards, Apache Git Services