Workflow service reference injector

Worklets are singleton object in the workflow framework
using service reference injector workflow framework will inject
Osgi services to worklets.

Change-Id: I5b87aa54589235e3584cd6e3cb2c3f427e3f9715
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DescriptionDataModel.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DescriptionDataModel.java
new file mode 100644
index 0000000..37dd420
--- /dev/null
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DescriptionDataModel.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.workflow.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface DescriptionDataModel {
+    String value() default "";
+}
+
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ServiceReference.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ServiceReference.java
new file mode 100644
index 0000000..306ffc7
--- /dev/null
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ServiceReference.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.workflow.api;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+
+/**
+ * Annotation for injecting service reference on work-let execution context.
+ */
+public @interface ServiceReference {
+
+}
+
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ServiceReferenceInjector.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ServiceReferenceInjector.java
new file mode 100644
index 0000000..f7bd833
--- /dev/null
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ServiceReferenceInjector.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2024-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.workflow.api;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Class for injecting service reference on the work-let execution context.
+ */
+public class ServiceReferenceInjector {
+
+    private static final Logger log = LoggerFactory.getLogger(ServiceReferenceInjector.class);
+
+    /**
+     * Injects service reference to work-let.
+     *
+     * @param worklet work-let to be injected
+     * @param context workflow context
+     * @throws WorkflowException workflow exception
+     */
+    public void inject(Worklet worklet, WorkflowContext context) throws WorkflowException {
+
+        handle(worklet, context, this::injectReference);
+    }
+
+
+    private void handle(Worklet worklet, WorkflowContext context, ReferenceFieldBehavior func)
+            throws WorkflowException {
+
+        if (Objects.isNull(worklet) || Objects.isNull(context)) {
+            log.error("Invalid worklet or context reference");
+            return;
+        }
+        Class cl = worklet.getClass();
+        List<Field> fields = getInheritedFields(cl);
+        if (Objects.isNull(fields)) {
+            log.error("Invalid fields on {}", cl);
+            return;
+        }
+
+        for (Field field : fields) {
+            Annotation[] annotations = field.getAnnotations();
+            if (Objects.isNull(annotations)) {
+                continue;
+            }
+            for (Annotation annotation : annotations) {
+                if (!(annotation instanceof ServiceReference)) {
+                    continue;
+                }
+
+                if (Modifier.isStatic(field.getModifiers())) {
+                    throw new WorkflowException("Static field(" + field + " ) cannot use @JsonDataModel in " + cl);
+                }
+
+                ServiceReference reference = (ServiceReference) annotation;
+                func.apply(worklet, context, field, reference);
+            }
+        }
+    }
+
+    private static List<Field> getInheritedFields(Class<?> type) {
+        List<Field> fields = new ArrayList<Field>();
+
+        Class<?> cl = type;
+        while (cl != null && cl != Object.class) {
+            for (Field field : cl.getDeclaredFields()) {
+                if (!field.isSynthetic()) {
+                    fields.add(field);
+                }
+            }
+            cl = cl.getSuperclass();
+        }
+        return fields;
+    }
+
+    /**
+     * Functional interface for service reference annotated field behavior.
+     */
+    @FunctionalInterface
+    public interface ReferenceFieldBehavior {
+        void apply(Worklet worklet, WorkflowContext context, Field field, ServiceReference reference)
+                throws WorkflowException;
+    }
+
+    /**
+     * Injects service reference on the filed of work-let.
+     *
+     * @param worklet work-let
+     * @param context workflow context
+     * @param field   the field of work-let
+     * @param reference   service reference for the field
+     * @throws WorkflowException workflow exception
+     */
+    private void injectReference(Worklet worklet, WorkflowContext context, Field field, ServiceReference reference)
+            throws WorkflowException {
+
+        Object obj = context.getService(field.getType());
+
+        if (Objects.isNull(obj)) {
+            throw new WorkflowException("Invalid reference " + field.getType() + " in "
+                    + worklet + " of " + context.name());
+        }
+
+        try {
+            field.setAccessible(true);
+            field.set(worklet, obj);
+        } catch (IllegalAccessException e) {
+            throw new WorkflowException(e);
+        }
+    }
+
+}
+