/*_############################################################################
_##
_## SNMP4J-AgentX - AgentXRequest.java
_##
_## Copyright (C) 2005-2009 Frank Fock (SNMP4J.org)
_##
_## This program is free software; you can redistribute it and/or modify
_## it under the terms of the GNU General Public License version 2 as
_## published by the Free Software Foundation.
_##
_## 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., 51 Franklin Street, Fifth Floor, Boston,
_## MA 02110-1301 USA
_##
_##########################################################################*/
package org.snmp4j.agent.agentx.subagent;
import java.util.*;
import org.snmp4j.*;
import org.snmp4j.mp.*;
import org.snmp4j.smi.*;
import org.snmp4j.agent.DefaultMOContextScope;
import org.snmp4j.agent.MOScope;
import org.snmp4j.agent.ManagedObject;
import org.snmp4j.log.LogAdapter;
import org.snmp4j.log.LogFactory;
import org.snmp4j.agent.request.AbstractRequest;
import org.snmp4j.agent.request.SubRequest;
import org.snmp4j.agent.request.SubRequestIterator;
import org.snmp4j.agent.request.RequestStatusListener;
import org.snmp4j.agent.request.RequestStatus;
import org.snmp4j.agent.request.RequestStatusEvent;
import org.snmp4j.agent.agentx.AgentXResponsePDU;
import org.snmp4j.agent.agentx.AgentXGetBulkPDU;
import org.snmp4j.agent.agentx.AgentXRequestPDU;
import org.snmp4j.agent.agentx.AgentXVariableBindingPDU;
import org.snmp4j.agent.agentx.AgentXPDU;
import org.snmp4j.agent.agentx.AgentXContextPDU;
import org.snmp4j.agent.agentx.AgentXCommandEvent;
import org.snmp4j.agent.MOQuery;
import org.snmp4j.agent.request.SubRequestIteratorSupport;
import org.snmp4j.agent.request.Request;
/**
* The <code>AgentXRequest</code> class represents AgentX sub-agent requests.
* AgentX sub-agent requests are similar to SNMP requests but especially for
* SET request processing and query scopes differences apply.
*
* @author Frank Fock
* @version 1.0
*/
public class AgentXRequest extends AbstractRequest {
private static final LogAdapter logger =
LogFactory.getLogger(AgentXRequest.class);
public static final OctetString DEFAULT_CONTEXT = new OctetString();
private AgentXCommandEvent requestEvent;
private AgentXResponsePDU response;
protected Map processingUserObjects;
private static int nextTransactionID = 0;
public AgentXRequest(AgentXCommandEvent request) {
this.requestEvent = request;
correctRequestValues();
this.transactionID = nextTransactionID();
}
public static int nextTransactionID() {
return nextTransactionID++;
}
public int size() {
if (requestEvent.getCommand() instanceof AgentXRequestPDU) {
return ((AgentXRequestPDU)requestEvent.getCommand()).size();
}
else if (requestEvent.getCommand() instanceof AgentXVariableBindingPDU) {
return ((AgentXVariableBindingPDU)requestEvent.getCommand()).size();
}
return 0;
}
public boolean isBulkRequest() {
return requestEvent.getCommand().getType() == AgentXPDU.AGENTX_GETBULK_PDU;
}
private void correctRequestValues() {
AgentXPDU request = requestEvent.getCommand();
if (request instanceof AgentXGetBulkPDU) {
repeaterStartIndex = getNonRepeaters();
repeaterRowSize =
Math.max(size() - repeaterStartIndex, 0);
}
else {
repeaterStartIndex = 0;
repeaterRowSize = size();
}
}
protected void setupSubRequests() {
int capacity = size();
int totalRepetitions = 0;
if (requestEvent.getCommand() instanceof AgentXGetBulkPDU) {
totalRepetitions = repeaterRowSize * getMaxRepetitions();
}
subrequests = new ArrayList(capacity + totalRepetitions);
if (response == null) {
response = createResponse();
}
if (requestEvent.getCommand() instanceof AgentXRequestPDU) {
AgentXRequestPDU rangeRequest =
(AgentXRequestPDU)requestEvent.getCommand();
MOScope[] ranges = rangeRequest.getRanges();
for (int i = 0; i < ranges.length; i++) {
AgentXSubRequest subReq =
new AgentXSubRequest(
new DefaultMOContextScope(getContext(), ranges[i]), i);
addSubRequest(subReq);
}
}
else if (requestEvent.getCommand() instanceof AgentXVariableBindingPDU) {
AgentXVariableBindingPDU vbRequest =
(AgentXVariableBindingPDU)requestEvent.getCommand();
VariableBinding[] vbs = vbRequest.getVariableBindings();
for (int i = 0; i < vbs.length; i++) {
AgentXSubRequest subReq = new AgentXSubRequest(vbs[i], i);
addSubRequest(subReq);
}
}
if (logger.isDebugEnabled()) {
logger.debug("AgentXSubRequests initialized: "+subrequests);
}
}
public int getMaxRepetitions() {
if (requestEvent.getCommand() instanceof AgentXGetBulkPDU) {
return ((AgentXGetBulkPDU)requestEvent.getCommand()).getMaxRepetitions()
& 0xFFFF;
}
return 0;
}
public int getNonRepeaters() {
if (requestEvent.getCommand() instanceof AgentXGetBulkPDU) {
return ((AgentXGetBulkPDU)requestEvent.getCommand()).getNonRepeaters()
& 0xFFFF;
}
return 0;
}
private void addSubRequest(SubRequest subReq) {
subrequests.add(subReq);
response.add(subReq.getVariableBinding());
}
protected int getMaxPhase() {
return (is2PC()) ? PHASE_2PC_CLEANUP : PHASE_1PC;
}
public Object getSource() {
return requestEvent;
}
public void setRequestEvent(AgentXCommandEvent requestEvent) {
this.requestEvent = requestEvent;
}
protected void assignErrorStatus2Response() {
int errStatus = getErrorStatus();
response.setErrorStatus((short)errStatus);
response.setErrorIndex((short)getErrorIndex());
}
private AgentXResponsePDU createResponse() {
AgentXResponsePDU resp =
new AgentXResponsePDU(0, (short)0, (short)0);
resp.setTransactionID(transactionID);
return resp;
}
public AgentXResponsePDU getResponsePDU() {
return (AgentXResponsePDU) getResponse();
}
public Object getResponse() {
if (response == null) {
response = createResponse();
assignErrorStatus2Response();
}
else {
assignErrorStatus2Response();
}
if (is2PC()) {
response.clear();
if ((requestEvent.getCommand().getType() == AgentXPDU.AGENTX_CLEANUPSET_PDU) ||
(requestEvent.getCommand().getType() == AgentXPDU.AGENTX_UNDOSET_PDU)) {
return null;
}
}
return response;
}
public Iterator iterator() {
initSubRequests();
return new AgentXSubRequestIterator();
}
protected boolean is2PC() {
return ((requestEvent.getCommand().getType() >= AgentXPDU.AGENTX_TESTSET_PDU) &&
(requestEvent.getCommand().getType() <= AgentXPDU.AGENTX_CLEANUPSET_PDU));
}
public OctetString getContext() {
if (requestEvent.getCommand() instanceof AgentXContextPDU) {
return ((AgentXContextPDU)requestEvent.getCommand()).getContext();
}
return DEFAULT_CONTEXT;
}
public OctetString getViewName() {
throw new UnsupportedOperationException();
}
public void setViewName(OctetString viewName) {
throw new UnsupportedOperationException();
}
public int getSecurityLevel() {
throw new UnsupportedOperationException();
}
public int getSecurityModel() {
throw new UnsupportedOperationException();
}
public OctetString getSecurityName() {
throw new UnsupportedOperationException();
}
public int getViewType() {
throw new UnsupportedOperationException();
}
protected synchronized void addRepeaterSubRequest() {
int predecessorIndex = subrequests.size() - repeaterRowSize;
AgentXSubRequest sreq =
new AgentXSubRequest((AgentXSubRequest)subrequests.get(predecessorIndex),
subrequests.size());
addSubRequest(sreq);
}
/**
* Returns the last repetition row that is complete (regarding the number
* of elements in the row).
* @return
* a sub list of the sub-requests list that contains the row's elements.
* If no such row exists <code>null</code> is returned.
*/
private List lastRow() {
if (repeaterRowSize == 0) {
return null;
}
int rows = (subrequests.size() - repeaterStartIndex) / repeaterRowSize;
int startIndex = repeaterStartIndex + (repeaterRowSize*(rows-1));
int endIndex = repeaterStartIndex + (repeaterRowSize*rows);
return subrequests.subList(startIndex, endIndex);
}
public int getMessageProcessingModel() {
throw new UnsupportedOperationException();
}
public String toString() {
return getClass().getName()+"[subrequests="+subrequests+",phase="+phase+
",requestEvent="+requestEvent+"]";
}
public boolean isPhaseComplete() {
if (errorStatus == SnmpConstants.SNMP_ERROR_SUCCESS) {
initSubRequests();
for (Iterator it = subrequests.iterator(); it.hasNext(); ) {
SubRequest subreq = (SubRequest) it.next();
RequestStatus status = subreq.getStatus();
if (status.getErrorStatus() != SnmpConstants.SNMP_ERROR_SUCCESS) {
return true;
}
else if (!status.isPhaseComplete()) {
return false;
}
}
}
return true;
}
public synchronized Object getProcessingUserObject(Object key) {
if (processingUserObjects != null) {
return processingUserObjects.get(key);
}
return null;
}
public synchronized Object setProcessingUserObject(Object key, Object value) {
if (processingUserObjects == null) {
processingUserObjects = new HashMap(5);
}
return processingUserObjects.put(key, value);
}
/**
* The AgentXSubRequestIterator iterates over the subrequests in a AgentX
* request. In case of bulk operations, it also may physically append
* new sub-request instances while iterating, until the bulk operations
* limits are reached.
*
* @author Frank Fock
* @version 1.0
*/
public class AgentXSubRequestIterator implements SubRequestIterator {
private int cursor = 0;
private int increment = 1;
protected AgentXSubRequestIterator() {
}
protected AgentXSubRequestIterator(int offset, int increment) {
this.cursor = offset;
this.increment = increment;
}
private int getRepeaterCount() {
AgentXPDU pdu = requestEvent.getCommand();
if (pdu instanceof AgentXGetBulkPDU) {
AgentXGetBulkPDU bulkPDU = (AgentXGetBulkPDU)pdu;
return Math.max(bulkPDU.size() - bulkPDU.getNonRepeaters(), 0);
}
return 0;
}
public boolean hasNext() {
AgentXPDU reqPDU = requestEvent.getCommand();
if (reqPDU.getType() == AgentXPDU.AGENTX_GETBULK_PDU) {
AgentXGetBulkPDU bulkPDU = (AgentXGetBulkPDU)reqPDU;
if (cursor < Math.min(bulkPDU.size(), bulkPDU.getNonRepeaters())) {
return true;
}
else {
if (cursor < bulkPDU.getNonRepeaters() +
bulkPDU.getMaxRepetitions() * getRepeaterCount()) {
List lastRow = lastRow();
if (lastRow != null) {
boolean allEndOfMibView = true;
for (Iterator it = lastRow.iterator(); it.hasNext();) {
SubRequest sreq = (SubRequest) it.next();
if (sreq.getVariableBinding().getSyntax() !=
SMIConstants.EXCEPTION_END_OF_MIB_VIEW) {
allEndOfMibView = false;
break;
}
}
if (allEndOfMibView) {
return false;
}
}
return true;
}
}
return false;
}
return (cursor < size());
}
public SubRequest nextSubRequest() {
if (!hasNext()) {
throw new NoSuchElementException();
}
if ((isBulkRequest()) &&
(cursor >= subrequests.size())) {
while (cursor >= subrequests.size()) {
addRepeaterSubRequest();
}
}
SubRequest sreq = (SubRequest) subrequests.get(cursor);
cursor += increment;
return sreq;
}
public void remove() {
throw new UnsupportedOperationException("Remove is not supported "+
"on sub-requests");
}
public Object next() {
return nextSubRequest();
}
public boolean equals(Object other) {
if (other instanceof AgentXRequest) {
return ((AgentXRequest)other).getTransactionID() == getTransactionID();
}
return false;
}
public int hashCode() {
return getTransactionID();
}
}
/**
* The <code>AgentXSubRequest</code> implements the <code>SubRequest</code>
* interface for AgentX sub-requests.
*
* @author Frank Fock
* @version 1.0
*/
public class AgentXSubRequest implements SubRequest, RequestStatusListener {
private RequestStatus status;
private VariableBinding vb;
private Object undoValue;
private MOScope scope;
private MOQuery query;
private ManagedObject targetMO;
private int index;
private volatile Object userObject;
private AgentXSubRequest(int index) {
this.index = index;
status = new RequestStatus();
status.addRequestStatusListener(this);
}
protected AgentXSubRequest(MOScope searchRange, int index) {
this(index);
this.scope = searchRange;
this.vb = new VariableBinding(searchRange.getLowerBound());
}
protected AgentXSubRequest(VariableBinding subrequest, int index) {
this(index);
this.vb = subrequest;
OID oid = this.vb.getOid();
this.scope = new DefaultMOContextScope(getContext(),
oid, true, oid, true);
}
protected AgentXSubRequest(AgentXSubRequest predecessor, int index) {
this(index);
this.vb = new VariableBinding(predecessor.getVariableBinding().getOid());
switch (requestEvent.getCommand().getType()) {
case AgentXPDU.AGENTX_GETBULK_PDU:
case AgentXPDU.AGENTX_GETNEXT_PDU: {
this.scope =
new DefaultMOContextScope(getContext(),
predecessor.getVariableBinding().getOid(),
false,
predecessor.getScope().getUpperBound(),
predecessor.getScope().isUpperIncluded());
break;
}
default: {
this.scope = new DefaultMOContextScope(getContext(),
predecessor.getScope());
}
}
// Do not copy queries because they need to be updated externally only!
// this.query = predecessor.getQuery();
}
public Request getRequest() {
return AgentXRequest.this;
}
public RequestStatus getStatus() {
return status;
}
public VariableBinding getVariableBinding() {
return vb;
}
public void setStatus(RequestStatus status) {
this.status = status;
}
public Object getUndoValue() {
return undoValue;
}
public void setUndoValue(Object undoInformation) {
this.undoValue = undoInformation;
}
public void requestStatusChanged(RequestStatusEvent event) {
int newStatus = event.getStatus().getErrorStatus();
AgentXRequest.this.setErrorStatus(newStatus);
if (logger.isDebugEnabled() &&
(newStatus != SnmpConstants.SNMP_ERROR_SUCCESS)) {
new Exception("Error "+event.getStatus().getErrorStatus()+
" generated at: "+vb).printStackTrace();
}
}
public MOScope getScope() {
return scope;
}
public void completed() {
status.setPhaseComplete(true);
}
public boolean hasError() {
return getStatus().getErrorStatus() != SnmpConstants.SNMP_ERROR_SUCCESS;
}
public boolean isComplete() {
return status.isPhaseComplete();
}
public void setTargetMO(ManagedObject managedObject) {
this.targetMO = managedObject;
}
public ManagedObject getTargetMO() {
return targetMO;
}
public int getIndex() {
return index;
}
public void setQuery(MOQuery query) {
this.query = query;
}
public MOQuery getQuery() {
return query;
}
public String toString() {
return getClass().getName()+"[scope="+scope+
",vb="+vb+",status="+status+",query="+query+",index="+index+
",targetMO="+targetMO+"]";
}
public SubRequestIterator repetitions() {
initSubRequests();
if (requestEvent.getCommand().getType() == AgentXPDU.AGENTX_GETBULK_PDU) {
AgentXGetBulkPDU getBulk = (AgentXGetBulkPDU) requestEvent.getCommand();
int repeaters = getBulk.size() - getBulk.getNonRepeaters();
return new AgentXSubRequestIterator(getIndex(), repeaters);
}
return new SubRequestIteratorSupport(Collections.EMPTY_LIST.iterator());
}
public void updateNextRepetition() {
SubRequestIterator repetitions = repetitions();
// skip this one
repetitions.next();
if (repetitions.hasNext()) {
if ((getStatus().getErrorStatus() == PDU.noError) &&
(!this.vb.isException())) {
AgentXSubRequest nsreq =
(AgentXSubRequest) repetitions.nextSubRequest();
nsreq.query = null;
nsreq.scope =
new DefaultMOContextScope(getContext(),
this.getVariableBinding().getOid(),
false,
this.getScope().getUpperBound(),
this.getScope().isUpperIncluded());
}
else if (this.vb.isException()) {
while (repetitions.hasNext()) {
AgentXSubRequest nsreq =
(AgentXSubRequest) repetitions.nextSubRequest();
nsreq.query = null;
nsreq.getVariableBinding().setOid(this.vb.getOid());
nsreq.getVariableBinding().setVariable(this.vb.getVariable());
nsreq.getStatus().setPhaseComplete(true);
}
}
}
}
public final void setErrorStatus(int errorStatus) {
getStatus().setErrorStatus(errorStatus);
}
public final int getErrorStatus() {
return getStatus().getErrorStatus();
}
public Object getUserObject() {
return userObject;
}
public void setUserObject(Object userObject) {
this.userObject = userObject;
}
}
}