blob: 9374d8cd1a5c11adf80c7c0f1e2b883ba0aa482e [file] [log] [blame]
/*
* Copyright (c) 2020 Kentyou.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Kentyou - initial API and implementation
*/
package org.eclipse.sensinact.gateway.generic;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.sensinact.gateway.common.bundle.Mediator;
import org.eclipse.sensinact.gateway.core.InvalidServiceProviderException;
import org.eclipse.sensinact.gateway.core.method.Parameter;
import org.eclipse.sensinact.gateway.generic.Task.CommandType;
import org.eclipse.sensinact.gateway.generic.packet.InvalidPacketException;
import org.eclipse.sensinact.gateway.generic.packet.Packet;
import org.eclipse.sensinact.gateway.generic.parser.Commands;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* @author <a href="mailto:christophe.munilla@cea.fr">Christophe Munilla</a>
*/
public abstract class ProtocolStackEndpoint<P extends Packet> implements TaskTranslator {
/**
* the {@link Mediator} that will be used by the ProtocolStackConnector to instantiate
* to interact with the OSGi host environment
*/
protected final Mediator mediator;
/**
* the {@link Connector} connected to this ProtocolStackConnector instance
*/
protected Connector<P> connector;
/**
* the set of available commands for the
* connected {@link Connector}
*/
protected Commands commands;
/**
* Map of the subscription identifiers to the subscriber ones
*/
protected Map<String,String> subscriptions;
/**
* {@link SubscriptionHandlerDelegate} in charge of providing the appropriate
* and extended {@link AbstractSubscribeTaskWrapper} and {@link AbstractUnsubscribeTaskWrapper} types
*/
protected SubscriptionHandlerDelegate subscriptionHandlerDelegate;
/**
* Constructor
*
* @param mediator the {@link Mediator} that will be used by the ProtocolStackConnector
* to instantiate to interact with the OSGi host environment
*/
public ProtocolStackEndpoint(Mediator mediator) {
this.mediator = mediator;
this.subscriptions = new HashMap<>();
}
/**
* Connects this ProtocolStackConnector to the {@link ExtModelConfiguration} passed
* as parameter.
*
* @param manager the {@link ExtModelConfiguration} to connect to
*
* @throws InvalidProtocolStackException
*/
public void connect(ExtModelConfiguration<P> manager) throws InvalidProtocolStackException {
this.commands = manager.getCommands();
if ((this.connector = manager.connect(this)) != null) {
Iterator<Map.Entry<String, String>> iterator = manager.getFixedProviders().entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
try {
this.connector.addModelInstance(entry.getValue(), entry.getKey());
} catch (InvalidServiceProviderException e) {
throw new InvalidProtocolStackException(e);
}
}
}
}
/**
* Defines the {@link SubscriptionHandlerDelegate} in charge of providing wrapper types for subscribe and
* unsubscribe tasks
*
* @param subscriptionHandlerDelegate the {@link SubscriptionHandlerDelegate} to be used
*/
public void setSubscriptionHandlerDelegate(SubscriptionHandlerDelegate subscriptionHandlerDelegate) {
this.subscriptionHandlerDelegate = subscriptionHandlerDelegate;
}
/**
* Defines the String subscription identifier of the {@link AbstractUnsubscribeTaskWrapper}
* passed as parameter
*
* @param task the {@link AbstractUnsubscribeTaskWrapper} to define the subscription identifier
* of
*/
public void setSubsciptionIdentifier(UnsubscribeTaskWrapper task) {
task.setSubscriptionId(this.subscriptions.get(task.getSubscriberId()));
}
/**
* Returns the {@link AbstractUnsubscribeTaskWrapper} type to be used to wrapped unsubscribe
* {@link Task}
*
* @return the {@link AbstractUnsubscribeTaskWrapper} type to be used
*/
protected Class<? extends UnsubscribeTaskWrapper> getUnsubscribeTaskWrapperType(){
if(this.subscriptionHandlerDelegate!=null)
return this.subscriptionHandlerDelegate.getUnsubscribeTaskWrapperType();
return null;
}
/**
* Maps the String subscription identifier to the String subscriber one, both provided
* by the {@link AbstractSubscribeTaskWrapper} wrapping a subscribe {@link Task}
*
* @param task the subscribe {@link Task} wrapped into an {@link AbstractSubscribeTaskWrapper}
*/
public void registerSubsciptionIdentifier(SubscribeTaskWrapper task) {
this.subscriptions.put(task.getSubscriberId(), task.getSubscriptionId());
}
/**
* Returns the {@link AbstractSubscribeTaskWrapper} type to be used to wrapped subscribe
* {@link Task}
*
* @return the {@link AbstractSubscribeTaskWrapper} type to be used
*/
protected Class<? extends SubscribeTaskWrapper> getSubscribeTaskWrapperType(){
if(this.subscriptionHandlerDelegate!=null)
return this.subscriptionHandlerDelegate.getSubscribeTaskWrapperType();
return null;
}
/**
* Returns the {@link Task} passed as parameter wrapped into the appropriate
* {@link TaskWrapper} if it holds an SUBSCRIBE or UNSUBSCRIBE {@link CommandType}
* and if it is not already an {@link TaskWrapper} instance
*
* @param <T> the handled {@link Task} type
*
* @param type the handled {@link Task} type
* @param task the {@link Task} to be wrapped
*
* @return the {@link Task} passed as parameter wrapped into the appropriate
* {@link TaskWrapper}
*/
protected <T extends Task> T wrap(Class<T> type , T task) {
if(task == null)
return null;
if(task instanceof TaskWrapper)
return task;
T _task = null;
if(task.getCommand().equals(CommandType.SUBSCRIBE)) {
Class<? extends SubscribeTaskWrapper> wrapperType = this.getSubscribeTaskWrapperType();
if(wrapperType != null) {
try {
_task = (T) wrapperType.getDeclaredConstructor(new Class<?>[]{Mediator.class, type, ProtocolStackEndpoint.class}
).newInstance(new Object[] {mediator,task,this});
} catch (Exception e) {
e.printStackTrace();
_task = null;
}
}
} else if(task.getCommand().equals(CommandType.UNSUBSCRIBE)) {
Class<? extends UnsubscribeTaskWrapper> wrapperType = this.getUnsubscribeTaskWrapperType();
if(wrapperType != null) {
try {
_task = (T) wrapperType.getDeclaredConstructor(new Class<?>[]{Mediator.class, type, ProtocolStackEndpoint.class}
).newInstance(new Object[] {mediator,task,this});
} catch (Exception e) {
e.printStackTrace();
_task = null;
}
}
}
if(_task==null)
_task = task;
return _task;
}
/**
* Processes the {@link Packet} passed as parameter
*
* @param packet the {@link Packet} to process
*
* @throws InvalidPacketException
*/
public void process(P packet) throws InvalidPacketException {
if (connector == null) {
this.mediator.debug("No processor connected");
return;
}
connector.process(packet);
}
/**
* Returns the bytes array command for the {@link CommandType} passed as parameter
*
* @param commandType the {@link CommandType} for which to retrieve the bytes array command
*
* @return the bytes array command for the specified {@link CommandType}
*/
public byte[] getCommand(CommandType commandType) {
if (this.commands == null) {
return new byte[0];
}
return this.commands.getCommand(commandType);
}
/**
* Joins each bytes array contained by the arrays argument into a single one delimiting
* each others by the delimiter argument bytes array
*
* @param arrays the array of bytes arrays to join
* @param delimiter the delimiters bytes array
*
* @return the bytes array resulting of the junction of the bytes arrays contained by the
* arrays argument
*/
public static byte[] join(byte[][] arrays, byte[] delimiter) {
byte[] joined = new byte[0];
int index = 0;
int length = 0;
int delimiterLength = delimiter == null ? 0 : delimiter.length;
for (; index < arrays.length; index++) {
if (arrays[index] != null && arrays[index].length > 0) {
if (delimiterLength > 0 && length > 0) {
joined = Arrays.copyOf(joined, (length + delimiterLength));
System.arraycopy(delimiter, 0, joined, length, delimiterLength);
length += delimiterLength;
}
joined = Arrays.copyOf(joined, (length + arrays[index].length));
System.arraycopy(arrays[index], 0, joined, length, arrays[index].length);
length += arrays[index].length;
}
}
return joined;
}
/**
* Converts the parameter object argument into a bytes array and returns
* it. If the parameter object is of an {@link Array} type, its distinct
* elements are separated using the delimiter bytes array argument
*
* @param parameter the parameter object to convert into a bytes array
* @param delimiter the array of byte delimiting the distinct elements
* of the parameter argument if this last one is of an {@link Array} type
*
* @return the parameter argument object converted into a bytes array
*/
public static byte[] formatParameter(Object parameter, byte[] delimiter) {
if (parameter == null) {
return null;
}
byte[] valueBytes = new byte[0];
if (String.class.isAssignableFrom(parameter.getClass())) {
valueBytes = ((String) parameter).getBytes();
} else if (JSONObject.class.isAssignableFrom(parameter.getClass())) {
valueBytes = ((JSONObject) parameter).toString().getBytes();
} else if (JSONArray.class.isAssignableFrom(parameter.getClass())) {
valueBytes = ((JSONArray) parameter).toString().getBytes();
} else if (Parameter.class.isAssignableFrom(parameter.getClass())) {
valueBytes = formatParameter(((Parameter) parameter).getValue(), delimiter);
} else if (parameter.getClass().isArray()) {
for (int j = 0; j < Array.getLength(parameter); j++) {
Object param = Array.get(parameter, j);
byte[] formated = formatParameter(param, delimiter);
if (formated != null && formated.length > 0) {
int length = valueBytes.length;
if (valueBytes.length > 0 && delimiter != null && delimiter.length > 0) {
valueBytes = Arrays.copyOfRange(valueBytes, 0, length + delimiter.length);
System.arraycopy(delimiter, 0, valueBytes, length, delimiter.length);
length += delimiter.length;
}
valueBytes = Arrays.copyOfRange(valueBytes, 0, length + formated.length);
System.arraycopy(formated, 0, valueBytes, length, formated.length);
}
}
} else if (byte.class.equals(parameter.getClass()) || Byte.class.equals(parameter.getClass())) {
valueBytes = new byte[]{((Byte) parameter).byteValue()};
} else {
valueBytes = String.valueOf(parameter).getBytes();
}
return valueBytes;
}
/**
* Stops this ProtocolStackEndpoint and its associated {@link Connector}
*/
public void stop() {
if (this.connector != null)
this.connector.stop();
else
this.mediator.debug("No processor connected");
}
}