blob: c128fc6bd3951783216b77a4db1cb9b411dcf567 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017 Huawei Technologies Co., Ltd (http://www.huawei.com)
* Huawei Base, Bantian,Longgang District ,Shenzhen ,Guangdong ,China
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Initial Contributors:
* Seven Ganlu : Developer
*
* New contributors :
*******************************************************************************/
package org.eclipse.om2m.ipe.dal;
import java.awt.EventQueue;
import java.math.BigInteger;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.dal.Function;
import org.osgi.service.dal.FunctionEvent;
import org.osgi.service.dal.PropertyMetadata;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.osgi.util.tracker.ServiceTracker;
import org.eclipse.om2m.commons.constants.MimeMediaType;
import org.eclipse.om2m.commons.constants.ResourceType;
import org.eclipse.om2m.commons.constants.ResponseStatusCode;
import org.eclipse.om2m.commons.entities.ContainerEntity;
import org.eclipse.om2m.commons.obix.Contract;
import org.eclipse.om2m.commons.obix.Obj;
import org.eclipse.om2m.commons.obix.Op;
import org.eclipse.om2m.commons.obix.Str;
import org.eclipse.om2m.commons.obix.Uri;
import org.eclipse.om2m.commons.obix.io.ObixEncoder;
import org.eclipse.om2m.commons.resource.Container;
import org.eclipse.om2m.commons.resource.ContentInstance;
import org.eclipse.om2m.commons.resource.ResponsePrimitive;
import org.eclipse.om2m.persistence.service.DBTransaction;
/**
* Device function service tracker.
*/
public class FunctionServiceTracker extends ServiceTracker<Object, Object> {
/** Logger */
private static Log LOGGER = LogFactory.getLog(FunctionServiceTracker.class);
/** Resource Id of the device this function tracker belongs to */
private String deviceResourceName = "";
public static final String DESC = "DESCRIPTOR";
public static final String LATEST = "la";
/**
* Constructor
*
* @param deviceUid
* - UID of the device that this function belongs to
* deviceResourceId - resource Id of the device
* @return
*/
public FunctionServiceTracker(String deviceUid, String deviceResourceName)
throws InvalidSyntaxException {
super(Activator.getContext(),
Activator.getContext().createFilter(
"(&(" + Constants.OBJECTCLASS + "="
+ Function.class.getName().toString() + ")("
+ Function.SERVICE_DEVICE_UID + "=" + deviceUid
+ "))"), null);
this.deviceResourceName = deviceResourceName;
}
/**
* Call back method when function service is unregistered
*
* @param reference
* - service reference
* @return
*/
@Override
public void removedService(ServiceReference<Object> reference,
Object service) {
LOGGER.info("Function service removed");
}
/**
* Call back method when function service is registered. Create container
* resource asynchronously
*
* @param reference
* - service reference
* @return Object
*/
@Override
public Object addingService(ServiceReference<Object> reference) {
LOGGER.info("Function service discovered");
final Function func = (Function) this.context.getService(reference);
EventQueue.invokeLater(new Runnable() {
public void run() {
createFunctionResource(func);
}
});
return func;
}
/**
* Create container resource asynchronously
*
* @param func
* - function service object
* @return
*/
private void createFunctionResource(Function func) {
// Set the function resource name to the function UID, replace the
// illegal character
String funcUid = (String) func.getServiceProperty(Function.SERVICE_UID);
String resourceName = funcUid.replace(':', '_');
String resourceId = "";
// Construct the container resource
Container container = new Container();
container.setMaxNrOfInstances(BigInteger.valueOf(10));
container.setName(resourceName);
ResponsePrimitive response = InterworkingServiceImpl.createResource(
InterworkingServiceImpl.CSE_PREFIX + "/" + deviceResourceName,
container, ResourceType.CONTAINER);
if (response.getResponseStatusCode().equals(ResponseStatusCode.CREATED)) {
resourceId = response.getLocation();
}
// Query the container resource ID from database if it is already
// created
else if (response.getResponseStatusCode().equals(
ResponseStatusCode.CONFLICT)) {
DBTransaction dbTransaction = Activator.getDBService()
.getDbTransaction();
dbTransaction.open();
ContainerEntity dbContainer = Activator.getDBService()
.getDAOFactory().getContainerByResourceNameDAO()
.find(dbTransaction, resourceName);
dbTransaction.close();
resourceId = dbContainer.getResourceID();
}
if (!resourceId.isEmpty()) {
// Store the function to the map, so it can handle the oneM2M
// request
InterworkingServiceImpl.addFunction(resourceName, func);
// Create the DESCRIPTOR container resource for the function, to
// describe the function information
container = new Container();
container.setMaxNrOfInstances(BigInteger.valueOf(10));
container.setName(DESC);
response = InterworkingServiceImpl.createResource(resourceId,
container, ResourceType.CONTAINER);
String descResourceId = "";
if (response.getResponseStatusCode().equals(
ResponseStatusCode.CREATED)) {
descResourceId = response.getLocation();
}
// Query from the database by function resource ID
else if (response.getResponseStatusCode().equals(
ResponseStatusCode.CONFLICT)) {
DBTransaction dbTransaction = Activator.getDBService()
.getDbTransaction();
dbTransaction.open();
ContainerEntity dbContainer = Activator.getDBService()
.getDAOFactory().getDescContainerByParentDAO()
.find(dbTransaction, resourceId);
dbTransaction.close();
descResourceId = dbContainer.getResourceID();
}
if (!descResourceId.isEmpty()) {
// Create DESCRIPTOR content instance resource
ContentInstance contentInstance = new ContentInstance();
contentInstance.setContent(getFunctionDescContent(func,
resourceName, resourceId));
contentInstance.setContentInfo(MimeMediaType.OBIX);
contentInstance.setName(DESC);
InterworkingServiceImpl.createResource(descResourceId,
contentInstance, ResourceType.CONTENT_INSTANCE);
}
// Register this function's event handler
Dictionary<String, Object> dict = new Hashtable<String, Object>();
dict.put(EventConstants.EVENT_TOPIC,
FunctionEvent.TOPIC_PROPERTY_CHANGED);
dict.put(EventConstants.EVENT_FILTER, "(" + Function.SERVICE_UID
+ "=" + funcUid + ")");
FunctionEventHandler newFuncEventHandler = new FunctionEventHandler(
funcUid, resourceId);
ServiceRegistration<?> register = Activator.getContext()
.registerService(EventHandler.class.getName(),
newFuncEventHandler, dict);
if (register == null) {
LOGGER.error(String.format(
"Event handler (%s) register failed!", funcUid));
}
}
}
/**
* Return the specific function DESCRIPTOR content string
*
* @param func
* - function service object resourceName - resource name of this
* function
* @return
*/
private String getFunctionDescContent(Function func, String resourceName,
String resourceId) {
// Basic function parameter
Obj obj = new Obj();
obj.add(new Str(Function.SERVICE_UID, (String) func
.getServiceProperty(Function.SERVICE_UID)));
obj.add(new Str(Function.SERVICE_TYPE, (String) func
.getServiceProperty(Function.SERVICE_TYPE)));
obj.add(new Str(Function.SERVICE_VERSION, (String) func
.getServiceProperty(Function.SERVICE_VERSION)));
obj.add(new Str(Function.SERVICE_DESCRIPTION, (String) func
.getServiceProperty(Function.SERVICE_DESCRIPTION)));
// Get resource state from oneM2M side
Op opLa = new Op();
opLa.setName("getState");
opLa.setHref(new Uri(InterworkingServiceImpl.CSE_PREFIX + "/"
+ deviceResourceName + "/" + resourceName + "/" + LATEST));
// opLa.setHref(new Uri(resourceId));
opLa.setIs(new Contract("retrieve"));
opLa.setIn(new Contract("obix:Nil"));
opLa.setOut(new Contract("obix:Nil"));
obj.add(opLa);
// Function properties
String[] propNames = (String[]) func
.getServiceProperty(Function.SERVICE_PROPERTY_NAMES);
if (propNames != null) {
for (String propName : propNames) {
// Get property access policy
Map<?, ?> propMetadata = func.getPropertyMetadata(propName)
.getMetadata(null);
Integer access = (Integer) propMetadata
.get(PropertyMetadata.ACCESS);
// Create get operation when this property is readable
if ((access.intValue() & PropertyMetadata.ACCESS_READABLE) != 0) {
String getPropOpName = "get"
+ propName.replace(propName.substring(0, 1),
propName.substring(0, 1).toUpperCase());
Op getPropOp = new Op();
getPropOp.setName(getPropOpName);
getPropOp.setHref(new Uri(
InterworkingServiceImpl.CSE_PREFIX + "/"
+ deviceResourceName + "?function="
+ resourceName + "&op=" + getPropOpName));
getPropOp.setIs(new Contract("execute"));
getPropOp.setIn(new Contract("obix:Nil"));
getPropOp.setOut(new Contract("obix:Nil"));
obj.add(getPropOp);
}
// Create set operation when this property is writable
if ((access.intValue() & PropertyMetadata.ACCESS_WRITABLE) != 0) {
String setPropOpName = "set"
+ propName.replace(propName.substring(0, 1),
propName.substring(0, 1).toUpperCase());
Op setPropOp = new Op();
setPropOp.setName(setPropOpName);
setPropOp.setHref(new Uri(
InterworkingServiceImpl.CSE_PREFIX + "/"
+ deviceResourceName + "?function="
+ resourceName + "&op=" + setPropOpName));
setPropOp.setIs(new Contract("execute"));
setPropOp.setIn(new Contract("obix:Nil"));
setPropOp.setOut(new Contract("obix:Nil"));
setPropOp.setWritable(true);
obj.add(setPropOp);
}
}
}
// Function operations
String[] operNames = (String[]) func
.getServiceProperty(Function.SERVICE_OPERATION_NAMES);
if (operNames != null) {
for (String operName : operNames) {
Op opState = new Op();
opState.setName(operName);
opState.setHref(new Uri(InterworkingServiceImpl.CSE_PREFIX
+ "/" + deviceResourceName + "?function="
+ resourceName + "&op=" + operName));
opState.setIs(new Contract("execute"));
opState.setIn(new Contract("obix:Nil"));
opState.setOut(new Contract("obix:Nil"));
obj.add(opState);
}
}
return ObixEncoder.toString(obj);
}
}