/* * AbstractSkill.java * * Created on Jan 16, 2008, 10:57:22 AM * * Description: An abstract skill that provides behavior for a role. * * Copyright (C) Jan 16, 2008 Stephen L. Reed. * * This program is free software; you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.texai.ahcsSupport; import java.util.Objects; import net.jcip.annotations.ThreadSafe; import org.openrdf.model.URI; import org.texai.ahcsSupport.AHCSConstants.State; import org.texai.ahcsSupport.domainEntity.Node; import org.texai.ahcsSupport.domainEntity.Role; import org.texai.kb.persistence.RDFEntityManager; import org.texai.util.StringUtils; /** An abstract skill that provides behavior for a role. * * @author Stephen L. Reed */ @ThreadSafe public abstract class AbstractSkill { /** the role containing this skill */ private Role role; /** the skill state */ private State skillState = State.UNINITIALIZED; /** the session manager skill, populated only for managed session skills so that they can notify the manager of disconnected sessions */ private SessionManagerSkill sessionManagerSkill; /** Constructs a new Skill instance. */ public AbstractSkill() { } /** Gets the RDF entity manager. * * @return the RDF entity manager */ public RDFEntityManager getRDFEntityManager() { //Preconditions assert role != null : "role must not be null for " + this; return role.getNodeRuntime().getRdfEntityManager(); } /** Gets the node runtime. * * @return the role containing this skill */ public NodeRuntime getNodeRuntime() { //Preconditions assert role != null : "role must not be null for " + this; return role.getNodeRuntime(); } /** Gets the role containing this skill. * * @return the role containing this skill */ public Role getRole() { return role; } /** Sets the role containing this skill. * * @param role the role containing this skill */ public void setRole(final Role role) { //Preconditions assert role != null : "role must not be null for " + this; this.role = role; } /** Receives and attempts to process the given message. The skill is thread safe, given that any contained libraries are single threaded * with regard to the conversation. * * @param message the given message * @return whether the message was successfully processed */ public abstract boolean receiveMessage(final Message message); /** Synchronously processes the given message. The skill is thread safe, given that any contained libraries are single threaded * with regard to the conversation. * * @param message the given message * @return the response message or null if not applicable */ public abstract Message converseMessage(final Message message); /** Sends the given message via the node runtime. * * @param message the given message */ public void sendMessage(final Message message) { //Preconditions assert message != null : "message must not be null"; //Preconditions assert role != null : "role must not be null for " + this; role.sendMessage(message); } /** Sends the given message via the node runtime. * * @param message the given message */ public void sendMessageViaSeparateThread(final Message message) { //Preconditions assert message != null : "message must not be null"; assert getRole() != null : "role must not be null"; getNodeRuntime().getExecutor().execute(new MessageSendingRunnable( message, getRole())); } /** Returns the id of the containing role. * * @return the id of the containing role */ public URI getRoleId() { //Preconditions assert role != null : "role must not be null for " + this; return role.getId(); } /** Returns the id of the role having the given type contained in the node having the given nickname. * * @param nodeNickname the given nickname * @param roleTypeName the given role type * @return the id of the role having the given type contained in the node having the given nickname, or null if not found */ public URI getRoleId( final String nodeNickname, final String roleTypeName) { //Preconditions assert StringUtils.isNonEmptyString(nodeNickname) : "nodeNickname must be a non-empty string"; assert StringUtils.isNonEmptyString(roleTypeName) : "roleTypeName must be a non-empty string"; return getNodeRuntime().getRoleId(nodeNickname, roleTypeName); } /** Returns the class name of this skill. * * @return the class name of this skill */ public String getClassName() { return getClass().getName(); } /** Returns the containing node's nickname. * * @return the containing node's nickname */ public String getNodeNickname() { //Preconditions assert role != null : "role must not be null for " + this; return role.getNode().getNodeNickname(); } /** Returns the understood operations. * * @return the understood operations */ public abstract String[] getUnderstoodOperations(); /** Returns whether the given operation is understood. * * @param operation the given operation * @return whether the given operation is understood */ public boolean isOperationUnderstood(final String operation) { //Preconditions assert operation != null : "operation must not be null"; assert !operation.isEmpty() : "operation must not be empty"; for (final String operation1 : getUnderstoodOperations()) { if (operation.equals(operation1)) { return true; } } return false; } /** Returns a string representation of this object. * * @return a string representation of this object */ @Override public String toString() { final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append('['); if (role != null) { final Node node = role.getNode(); if (node != null) { stringBuilder.append(node.getNodeNickname()).append(':'); } stringBuilder.append(role.getRoleTypeName()).append(':'); } stringBuilder.append(getClass().getSimpleName()).append(']'); return stringBuilder.toString(); } /** Returns a qualified name for the given role id if local, otherwise returns the URI string. * * @param roleId the role id * @return a qualified name for the given role id if local, otherwise returns the URI string */ public String getRoleQualifiedName(final URI roleId) { //Preconditions assert roleId != null : "roleId must not be null"; final Role localRole = getRole().getNodeRuntime().getLocalRole(roleId); if (localRole == null) { return roleId.toString(); } else { return localRole.toString(); } } /** Propagates the given operation to the child roles, and to any service that understands the operation. * * @param operation the given operation */ public void propagateOperationToChildRoles(final String operation) { //Preconditions assert operation != null : "operation must not be null"; assert !operation.isEmpty() : "operation must not be empty"; assert role != null : "role must not be null for " + this; role.propagateOperationToChildRoles( operation, getClassName(), // senderService null); // service } /** Propagates the given operation to the child roles. * * @param service the recipient service, which if null indicates that any service that understands the operation will receive the message * @param operation the given operation */ public void propagateOperationToChildRoles( final String service, final String operation) { //Preconditions assert operation != null : "operation must not be null"; assert !operation.isEmpty() : "operation must not be empty"; assert role != null : "role must not be null for " + this; role.propagateOperationToChildRoles( operation, getClassName(), // senderService service); } /** Sends a not-understood message in response to the received message. * * @param receivedMessage the received message */ protected void sendDoNotUnderstandMessage(final Message receivedMessage) { //Preconditions assert receivedMessage != null : "receivedMessage must not be null"; sendMessage(notUnderstoodMessage(receivedMessage)); } /** Returns a not-understood message for replying to the sender of the given message. * * @param receivedMessage the given message * @return a not-understood message for replying to the sender of the given message */ protected Message notUnderstoodMessage(final Message receivedMessage) { //Preconditions assert receivedMessage != null : "receivedMessage must not be null"; assert role != null : "role must not be null"; final Message message = new Message( getRoleId(), // senderRoleId getClassName(), // senderService, receivedMessage.getSenderRoleId(), // recipientRoleId receivedMessage.getSenderService(), // service AHCSConstants.MESSAGE_NOT_UNDERSTOOD_INFO); // operation message.put(AHCSConstants.AHCS_ORIGINAL_MESSAGE, receivedMessage); return message; } /** Returns whether some other object equals this one. * * @param obj the other object * @return whether some other object equals this one */ @Override public boolean equals(final Object obj) { return this == obj; } /** Returns a hash code for this object. * * @return a hash code for this object */ @Override public int hashCode() { int hash = 5; hash = 53 * hash + Objects.hashCode(this.role); hash = 53 * hash + Objects.hashCode(this.skillState); return hash; } /** Gets the skill state. * * @return the skill state */ public State getSkillState() { return skillState; } /** Sets the skill state. * * @param skillState the skill state */ public void setSkillState(final State skillState) { //Preconditions assert skillState != null : "skillState must not be null"; this.skillState = skillState; } /** Returns a description of the given skill state for logging. * * @param skillState the given skill state * @return a description of the given skill state */ public static String stateDescription(final State skillState) { if (skillState.equals(State.INITIALIZED)) { return "initialized"; } else if (skillState.equals(State.READY)) { return "ready"; } else if (skillState.equals(State.SHUTDOWN)) { return "shutdown"; } else if (skillState.equals(State.UNINITIALIZED)) { return "uninitialized"; } else { assert false; return null; } } /** Gets the node state value associated with the given variable name. * * @param stateVariableName the given variable name * @return the state value associated with the given variable name */ public Object getNodeStateValue(final String stateVariableName) { //Preconditions assert stateVariableName != null : "stateVariableName must not be null"; assert !stateVariableName.isEmpty() : "stateVariableName must not be empty"; assert role != null : "role must not be null for " + this; return role.getNodeStateValue(stateVariableName); } /** Sets the node state value associated with the given variable name. * * @param variableName the given variable name * @param value the state value */ public void setNodeStateValue(final String variableName, final Object value) { //Preconditions assert variableName != null : "variableName must not be null"; assert !variableName.isEmpty() : "variableName must not be empty"; assert role != null : "role must not be null for " + this; role.setNodeStateValue(variableName, value); } /** Removes the node state value binding for the given variable. * * @param variableName the variable name */ public void removeNodeStateValueBinding(final String variableName) { //Preconditions assert variableName != null : "variableName must not be null"; assert !variableName.isEmpty() : "variableName must not be empty"; assert role != null : "role must not be null for " + this; role.removeNodeStateValueBinding(variableName); } /** Gets the role state value associated with the given variable name. * * @param stateVariableName the given variable name * @return the state value associated with the given variable name */ public Object getRoleStateValue(final String stateVariableName) { //Preconditions assert stateVariableName != null : "stateVariableName must not be null"; assert !stateVariableName.isEmpty() : "stateVariableName must not be empty"; assert role != null : "role must not be null for " + this; return role.getRoleStateValue(stateVariableName); } /** Sets the role state value associated with the given variable name. * * @param variableName the given variable name * @param value the state value */ public void setRoleStateValue(final String variableName, final Object value) { //Preconditions assert variableName != null : "variableName must not be null"; assert !variableName.isEmpty() : "variableName must not be empty"; assert role != null : "role must not be null for " + this; role.setRoleStateValue(variableName, value); } /** Removes the role state value binding for the given variable. * * @param variableName the variable name */ public void removeRoleStateValueBinding(final String variableName) { //Preconditions assert variableName != null : "variableName must not be null"; assert !variableName.isEmpty() : "variableName must not be empty"; assert role != null : "role must not be null for " + this; role.removeRoleStateValueBinding(variableName); } /** Gets the session manager skill. * * @return the session manager skill */ public SessionManagerSkill getSessionManagerSkill() { return sessionManagerSkill; } /** Sets the parent session manager skill for a managed session. * * @param sessionManagerSkill the session manager skill */ public void setSessionManagerSkill(final SessionManagerSkill sessionManagerSkill) { //Preconditions assert sessionManagerSkill != null : "sessionManagerSkill must not be null"; this.sessionManagerSkill = sessionManagerSkill; } /** Finds or creates a sharable subskill instance. * * @param subSkillClassName the class name of the sharable subskill * @return a sharable subskill instance */ public AbstractSubSkill findOrCreateSubSkill(final String subSkillClassName) { //Preconditions assert StringUtils.isNonEmptyString(subSkillClassName) : "subSkillClassName must be a non-empty string"; assert role != null : "role must not be null for " + this; return getRole().findOrCreateSubSkill(subSkillClassName); } /** Finds a sharable subskill instance. * * @param subSkillClassName the class name of the sharable subskill * @return a sharable subskill instance, or null if not found */ public AbstractSubSkill findSubSkill(final String subSkillClassName) { //Preconditions assert StringUtils.isNonEmptyString(subSkillClassName) : "subSkillClassName must be a non-empty string"; return getRole().findSubSkill(subSkillClassName); } /** Provides a message sending runnable. */ public static class MessageSendingRunnable implements Runnable { /** the message to send */ final Message message; /** the sender role */ final Role role; /** Constructs a new MessageSendingRunnable instance. * * @param message the message to send * @param role the sender role */ public MessageSendingRunnable( final Message message, final Role role) { //Preconditions assert message != null : "message must not be null"; assert role != null : "role must not be null"; this.message = message; this.role = role; } /** Sends the message. */ @Override public void run() { role.sendMessage(message); } } }