/********************************************************************************
*
* JOrocos Library
*
* Copyright (c) 2011
* All rights reserved.
*
* Luca Gherardi
* University of Bergamo
* Dept. of Information Technology and Mathematics
*
* -------------------------------------------------------------------------------
*
* File: AbstractOrocosService.java
* Created: Jul 27, 2011
*
* Author: <A HREF="mailto:luca.gherardi@unibg.it">Luca Gherardi</A>
*
* Supervised by: <A HREF="mailto:brugali@unibg.it">Davide Brugali</A>
*
* In cooperation with: <A HREF="mailto:herman.bruyninckx@mech.kuleuven.be">Herman Bruyninckx</A>
*
* -------------------------------------------------------------------------------
*
* This software is published under a dual-license: GNU Lesser General Public
* License LGPL 2.1 and BSD license. The dual-license implies that users of this
* code may choose which terms they prefer.
*
* -------------------------------------------------------------------------------
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - Neither the name of the University of Bergamo nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License LGPL as
* published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version or the BSD license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License LGPL and the BSD license for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License LGPL and BSD license along with this program.
*
*******************************************************************************/
package it.unibg.robotics.jorocos.core;
import it.unibg.robotics.jorocos.exceptions.OrocosPropertyNotExistException;
import it.unibg.robotics.jorocos.exceptions.OrocosServiceNotExistException;
import it.unibg.robotics.jorocos.interfaces.CallbackListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import org.apache.log4j.Logger;
import RTT.corba.CArgumentDescription;
import RTT.corba.CNoSuchNameException;
import RTT.corba.CService;
import RTT.corba.CConfigurationInterfacePackage.CProperty;
/**
* The Class AbstractOrocosService defines a proxy to an Orocos service and offers
* the functionality for introspecting and invoking its operations and introspecting,
* reading and writing its properties.
*
* @author <A HREF="mailto:luca.gherardi@unibg.it">Luca Gherardi</A>
* @version 1.0
* @since August 2011
*/
public abstract class AbstractOrocosService {
/** The name of the service. */
private String name;
/** The description of the service. */
private String description;
/** The list of the operations provided by this service. */
private ArrayList<OrocosOperation> operations;
/** A pointer to the service modeled by this class. */
private CService cService;
/** The list of services provided by this service. */
private ArrayList<AbstractOrocosService> services;
/** The logger. */
protected Logger logger;
/** The list of properties contained in this service. */
private ArrayList<OrocosProperty> properties;
/** The list of the names of the properties contained in this service. */
private ArrayList<String> propertiesNames;
/**
* Instantiates a new abstract Orocos service.
*
* @param service a pointer to the service that has to be modeled
* @param introspect if true the constructor will introspect the service and store
* all the information about its services, operation and properties.
* This information can be retrieved by using the methods {@link #getServices()},
* {@link #getOperations()} and {@link #getProperties()}.
*/
public AbstractOrocosService(CService service, boolean introspect) {
super();
this.cService = service;
name = service.getName();
description = service.getServiceDescription();
operations = new ArrayList<OrocosOperation>();
properties = new ArrayList<OrocosProperty>();
propertiesNames = new ArrayList<String>();
services = new ArrayList<AbstractOrocosService>();
logger = Logger.getLogger(AbstractOrocosService.class);
if(introspect){
introspectOperations();
introspectServices();
introspectProperties();
}
}
/**
* Returns the service name.
*
* @return the service name
*/
public String getName() {
return name;
}
/**
* Returns the service description.
*
* @return the service description
*/
public String getDescription(){
return description;
}
/**
* Introspect all the operations provided by this service and store them.
* The operations can be retrieved by using the method {@link #getOperations()}
*/
public void introspectOperations() {
String[] operationsNames = cService.getOperations();
logger.info(" Introspection of the service '" + name +"' operations: ");
for (int i = 0; i < operationsNames.length; i++) {
String operationName = operationsNames[i];
logger.info(" Opearation-" + (i+1) + ": " + operationName);
this.operations.add(introspectOperation(operationName));
}
}
/**
* Returns the required operation provided by this service.
*
* @param operationName the name of the requested operation
* @return the operation
*/
public OrocosOperation getOperation(String operationName){
if(operations.size()>0){
OrocosOperation operation;
for (Iterator<OrocosOperation> iterator = operations.iterator(); iterator.hasNext();) {
operation = iterator.next();
if(operation.getName().equals(operationName)){
return operation;
}
}
}
return introspectOperation(operationName);
}
/**
* Returns all the operations provided by this service.
* This method should be called after the method {@link #introspectOperations()}
*
* @return the all operations
*/
public ArrayList<OrocosOperation> getOperations() {
return operations;
}
/**
* Returns the names of all the operations provided by this service.
* This method should be called after the method {@link #introspectOperations()}
*
* @return the name of all the operations
*/
public ArrayList<String> getOperationsNames(){
ArrayList<String> result = new ArrayList<String>();
Collections.addAll(result, cService.getOperations());
return result;
}
/**
* Call the requested operation of this service in a synchronously way.
*
* @param operationName the name of the operation
* @param arguments the arguments of the operation
* @return the result of the operation execution
*/
abstract public Object callOperationSynchronously(String operationName, Object... arguments);
/**
* Call the requested operation of this service in a asynchronously way.
*
* @param operationName operationName the name of the operation
* @param listener the client that calls the operation, usually should be "this".
* The client must be an instance of a class that implement the interface {@link CallbackListener}.
* When the execution of the operation will be completed the client will be notified by means of its method
* {@link CallbackListener#callback(String, String, Object)}.
* @param periodMs defines the frequency with which the availability
* of the operation result will be checked (it is expressed as period in milliseconds).
* @param arguments the arguments of the operation
*/
abstract public void callOperationAsynchronously(String operationName, CallbackListener listener, int periodMs, Object... arguments);
/**
* Introspect a specific operation provided by this service.
*
* @param operationName the operation name
* @return the operation
*/
private OrocosOperation introspectOperation(String operationName){
try {
String description = cService.getDescription(operationName);
String resultType = cService.getResultType(operationName);
CArgumentDescription[] argsDescriptor;
ArrayList<OrocosArgument> arguments = new ArrayList<OrocosArgument>();
argsDescriptor = cService.getArguments(operationName);
for (int j = 0; j < argsDescriptor.length; j++) {
CArgumentDescription arg = argsDescriptor[j];
logger.info(" Attribute-" + (j+1) + ": " + arg.name + " (type: " + arg.type + ", description: " + arg.description + ")");
arguments.add(new OrocosArgument(arg.name, arg.type, arg.description));
}
return new OrocosOperation(operationName, description, resultType, arguments);
} catch (CNoSuchNameException e) {
logger.error("The required Orocos operation '" + operationName + "' does not exist.");
e.printStackTrace();
return null;
}
}
/**
* Introspect all the services provided by this service and store them.
* The services can be retrieved by using the method {@link #getServices()}.
*/
public void introspectServices() {
String[] servicesNames = cService.getProviderNames();
for (int i = 0; i < servicesNames.length; i++) {
try {
services.add(introspectService(servicesNames[i], true));
} catch (OrocosServiceNotExistException e) {
// should not happen
e.printStackTrace();
}
}
}
/**
* Returns the requested service provided by this service.
*
* @param serviceName the service name
* @param introspect if true, in the case the request service has not been yet introspected,
* the method will introspect the service and store all the information about its services,
* operation and properties.
* @return the service
* @throws OrocosServiceNotExistException the Orocos service not exist exception
*/
public AbstractOrocosService getService(String serviceName, boolean introspect) throws OrocosServiceNotExistException {
if(services.size()>0){
AbstractOrocosService service;
for (Iterator<AbstractOrocosService> iterator = services.iterator(); iterator.hasNext();) {
service = iterator.next();
if(service.getName().equals(serviceName)){
return service;
}
}
}
return introspectService(serviceName, introspect);
}
/**
* Returns the all services provided by this service.
* This method should be called after the method {@link #introspectServices()}.
*
* @return the all services
*/
public ArrayList<AbstractOrocosService> getServices() {
return services;
}
/**
* Returns the names of all the services provided by this service.
* This method should be called after the method {@link #introspectServices()}.
*
* @return the name of all the services
*/
public ArrayList<String> getServicesNames(){
ArrayList<String> result = new ArrayList<String>();
Collections.addAll(result, cService.getProviderNames());
return result;
}
/**
* Introspects a specific service provided by this service.
*
* @param name the service name
* @param introspect if true the method will introspect the service and store all the information
* about its services, operation and properties.
* @return the service
* @throws OrocosServiceNotExistException the Orocos service not exist exception
*/
abstract protected AbstractOrocosService introspectService(String name, boolean introspect) throws OrocosServiceNotExistException;
/**
* Returns a pointer to the service modeled by this class.
*
* @return a pointer to the service modeled by this class
*/
public CService getCService() {
return cService;
}
/**
* Introspect all the properties contained in this service and store them.
* The services can be retrieved by using the method {@link #getProperties()}.
*/
public void introspectProperties(){
properties = new ArrayList<OrocosProperty>();
propertiesNames = new ArrayList<String>();
CProperty[] cProperties = cService.getPropertyList();
logger.info(" Introspection of the service '" + name +"' properties: ");
for (int i = 0; i < cProperties.length; i++) {
String name = cProperties[i].name;
String description = cProperties[i].description;
String dataType = cService.getPropertyType(name);
properties.add(new OrocosProperty(name, description, dataType));
propertiesNames.add(name);
logger.info(" Property-" + (i+1) + ": name = " + name + ", description = " + description + ", dataType = " + dataType);
}
}
/**
* Returns the requested property contained in this service.
* @param propertyName the name of the requested property
* @return the property
* @throws OrocosPropertyNotExistException
*/
public OrocosProperty getProperty(String propertyName) throws OrocosPropertyNotExistException{
OrocosProperty property = getPropertyFromArrayList(propertyName);
if(property == null)
introspectProperties();
property = getPropertyFromArrayList(propertyName);
if(property == null)
throw new OrocosPropertyNotExistException(propertyName);
return property;
}
/**
* Returns the requested property by looking into the set of propertied stored
* during the last execution of the method {@link #introspectProperties()}.
* @param propertyName the name of the requested property
* @return the property if it exist, null otherwise
*/
private OrocosProperty getPropertyFromArrayList(String propertyName){
OrocosProperty property = null;
if(propertiesNames.contains(propertyName)){
for (Iterator<OrocosProperty> iterator = properties.iterator(); iterator.hasNext();) {
property = (OrocosProperty) iterator.next();
if(property.getName().equals(propertyName))
return property;
}
}
return property;
}
/**
* Returns the all properties contained in this service.
* This method should be called after the method {@link #introspectProperties()}.
*
* @return the all properties
*/
public ArrayList<OrocosProperty> getProperties(){
return properties;
}
/**
* Returns the names of all the properties contained in this service.
* This method should be called after the method {@link #introspectProperties()}.
*
* @return the name of all the properties
*/
public ArrayList<String> getPropertiesNames(){
return propertiesNames;
}
/**
* Returns the value of the requested property.
*
* @param propertyName the name of the property
* @return the value of the requested property
* @throws OrocosPropertyNotExistException
*/
abstract public Object getPropertyValue(String propertyName) throws OrocosPropertyNotExistException;
/**
* Sets the value of the requested property.
* @param propertyName the name of the property
* @param value the new value for the property
* @throws OrocosPropertyNotExistException
*/
abstract public void setPropertyValue(String propertyName, Object value) throws OrocosPropertyNotExistException;
}