Package org.snmp4j.agent.agentx.subagent

Source Code of org.snmp4j.agent.agentx.subagent.AgentXSubagent$CleanupSetHandler

/*_############################################################################
  _##
  _##  SNMP4J-AgentX - AgentXSubagent.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.io.IOException;
import java.util.*;

import org.snmp4j.PDU;
import org.snmp4j.TransportMapping;
import org.snmp4j.agent.*;
import org.snmp4j.agent.agentx.*;
import org.snmp4j.agent.agentx.event.PingEvent;
import org.snmp4j.agent.agentx.event.PingListener;
import org.snmp4j.agent.mo.MOScalar;
import org.snmp4j.agent.mo.snmp.CoexistenceInfo;
import org.snmp4j.agent.request.*;
import org.snmp4j.log.LogAdapter;
import org.snmp4j.log.LogFactory;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.*;
import org.snmp4j.transport.ConnectionOrientedTransportMapping;
import org.snmp4j.transport.TransportMappings;
import org.snmp4j.util.ThreadPool;
import org.snmp4j.agent.mo.MOTableRow;
import org.snmp4j.agent.agentx.subagent.index.AnyNewIndexOID;
import org.snmp4j.agent.agentx.subagent.index.NewIndexOID;
import org.snmp4j.agent.mo.snmp.SysUpTime;
import java.util.Map.Entry;
import org.snmp4j.util.WorkerPool;
import org.snmp4j.util.WorkerTask;
import org.snmp4j.agent.mo.MOTable;

/**
* The <code>AgentXSubagent</code> class implements the AgentX communication
* for an AgentX subagent implementation.
*
* @author Frank Fock
* @version 1.1
*/
public class AgentXSubagent
    implements AgentXCommandListener, NotificationOriginator {

  private static final LogAdapter LOGGER =
      LogFactory.getLogger(AgentXSubagent.class);

  private ArrayList moServers = new ArrayList();
  private WorkerPool threadPool;
  private RequestFactory factory;
  private AgentX agentX;
  protected Map requestList;

  protected Map peers = new LinkedHashMap(2);
  protected Map sessions = new Hashtable(2);


  protected RequestHandler requestHandlerGet;
  protected RequestHandler requestHandlerGetNext;
  protected RequestHandler requestHandlerGetBulk;
  protected RequestHandler requestHandlerTestSet;
  protected RequestHandler requestHandlerCommitSet;
  protected RequestHandler requestHandlerUndoSet;
  protected RequestHandler requestHandlerCleanupSet;

  protected int nextTransactionID = 0;

  private OID subagentID;
  private OctetString subagentDescr;

  private long timeout = AgentXProtocol.DEFAULT_TIMEOUT_SECONDS * 1000;
  private byte defaultPriority = AgentXProtocol.DEFAULT_PRIORITY;

  private Timer pingTimer;
  private transient Vector pingListeners;


  public AgentXSubagent(AgentX agentX,
                        OID subagentID, OctetString subagentDescr) {
    this.requestList = Collections.synchronizedMap(new HashMap(10));
    this.agentX = agentX;
    this.subagentID = subagentID;
    this.subagentDescr = subagentDescr;
    this.factory = new DefaultAgentXRequestFactory();
    requestHandlerGet = new GetRequestHandler();
    requestHandlerCleanupSet = new CleanupSetHandler();
    requestHandlerCommitSet = new CommitSetHandler();
    requestHandlerTestSet = new TestSetHandler();
    requestHandlerUndoSet = new UndoSetHandler();
    requestHandlerGetNext = new GetNextHandler();
    requestHandlerGetBulk = new GetBulkHandler();
    agentX.addCommandResponder(this);
  }

  /**
   * Sets the ping delay in seconds. If greater than zero, for each session
   * a ping PDU is sent to the master to validate the session regularly with
   * the specified delay. To monitor the ping requests, it is necessary to
   * add a {@link PingListener} with {@link #addPingListener}.
   *
   * @param seconds
   *    the delay. If zero or a negative value is supplied, no pings are sent
   */
  public void setPingDelay(int seconds) {
    if (pingTimer != null) {
      pingTimer.cancel();
      pingTimer = null;
    }
    if (seconds > 0) {
      pingTimer = new Timer();
      pingTimer.schedule(new PingTask(), seconds * 1000, seconds * 1000);
    }
  }

  public void processCommand(AgentXCommandEvent event) {
    if (event.getCommand() != null) {
      event.setProcessed(true);
      Command command = new Command(event);
      if (threadPool != null) {
        threadPool.execute(command);
      }
      else {
        command.run();
      }
    }
  }

  protected synchronized int getNextTransactionID() {
    return nextTransactionID++;
  }

  protected int closeSession(int sessionID, byte reason) throws
      IOException {
    AgentXSession session;
    synchronized (this) {
      session = removeSession(sessionID);
      if ((session == null) || (session.isClosed())) {
        return AgentXProtocol.AGENTX_NOT_OPEN;
      }
      session.setClosed(true);
    }
    AgentXClosePDU closePDU =
        new AgentXClosePDU(AgentXProtocol.REASON_SHUTDOWN);
    closePDU.setSessionID(sessionID);
    AgentXTarget target = session.createAgentXTarget();
    AgentXResponseEvent resp =
        agentX.send(closePDU, target, session.getPeer().getTransport());
    if ((resp == null) || (resp.getResponse() == null)) {
      return AgentXProtocol.AGENTX_TIMEOUT;
    }
    return ((AgentXResponsePDU)resp.getResponse()).getErrorStatus();
  }

  protected int openSession(TransportMapping transport,
                            Address masterAddress,
                            AgentXSession session) throws IOException {
    AgentXOpenPDU openPDU = new AgentXOpenPDU(0, getNextTransactionID(),
                                              0, session.getTimeout(),
                                              subagentID, subagentDescr);
    AgentXResponseEvent responseEvent =
        agentX.send(openPDU, session.createAgentXTarget(), transport);
    if (responseEvent.getResponse() == null) {
      LOGGER.error("Timeout on connection to master "+masterAddress);
    }
    else if (responseEvent.getResponse() instanceof AgentXResponsePDU) {
      AgentXResponsePDU response = responseEvent.getResponse();
      if (response.getErrorStatus() == AgentXProtocol.AGENTX_SUCCESS) {
        session.setSessionID(response.getSessionID());
      }
      return response.getErrorStatus();
    }
    else {
      LOGGER.error("Received packet on open PDU is not a response AgentX PDU: "+
                   responseEvent);
    }
    return AgentXProtocol.AGENTX_TIMEOUT;
  }

  private static int getResponseStatus(AgentXResponseEvent responseEvent) {
    if (responseEvent.getResponse() == null) {
      LOGGER.error("Timeout on connection to master "+
                   responseEvent.getTarget());
      return AgentXProtocol.AGENTX_TIMEOUT;
    }
    else if (responseEvent.getResponse() instanceof AgentXResponsePDU) {
      AgentXResponsePDU response = responseEvent.getResponse();
      return response.getErrorStatus();
    }
    else {
      LOGGER.error("Received packet on open PDU is not a response AgentX PDU: "+
                   responseEvent);
    }
    return AgentXProtocol.AGENTX_ERROR;
  }

  public void disconnect(Address masterAddress) throws IOException {
    AgentXPeer peer = (AgentXPeer) peers.remove(masterAddress);
    if (peer != null) {
      TransportMapping transport = peer.getTransport();
      if (transport instanceof ConnectionOrientedTransportMapping) {
        ((ConnectionOrientedTransportMapping)transport).close(masterAddress);
      }
    }
  }

  public int connect(Address masterAddress, Address localAddress,
                     AgentXSession session) throws IOException {
    AgentXPeer peer = (AgentXPeer) peers.get(masterAddress);
    TransportMapping transport;
    if (peer == null) {
      transport = addMaster(localAddress);
      peer = new AgentXPeer(transport, masterAddress);
    }
    else {
      transport = peer.getTransport();
      agentX.removeTransportMapping(transport);
      agentX.addTransportMapping(transport);
      if (!transport.isListening()) {
        transport.listen();
      }
    }
    peer.setTimeout(session.getTimeout());
    session.setPeer(peer);
    int status = AgentXProtocol.AGENTX_TIMEOUT;
    try {
      status = openSession(transport, masterAddress, session);
      if (status != AgentXProtocol.AGENTX_TIMEOUT) {
        peers.put(masterAddress, peer);
        LOGGER.info("Added new peer address="+masterAddress+",peer="+peer);
      }
    }
    catch (IOException ex) {
      LOGGER.error(ex);
      removeMaster(transport);
      return AgentXProtocol.AGENTX_ERROR;
    }
    if (status == AgentXProtocol.AGENTX_SUCCESS) {
      sessions.put(new Integer(session.getSessionID()), session);
      LOGGER.info("Opened subagent session successfully: "+session);
    }
    else {
      removeMaster(transport);
    }
    return status;
  }

  public int close(AgentXSession session, byte reason) throws IOException {
    return closeSession(session.getSessionID(), reason);
  }

  private synchronized AgentXSession getSession(int sessionID) {
    return (AgentXSession) sessions.get(new Integer(sessionID));
  }

  private synchronized AgentXSession removeSession(int sessionID) {
    return (AgentXSession) sessions.remove(new Integer(sessionID));
  }

  public void setDefaultPriority(byte priority) {
    this.defaultPriority = priority;
  }

  public byte getDefaultPriority() {
    return defaultPriority;
  }

  /**
   * Gets the priority with which the supplied managed object and
   * region should be registered at the master agent. Overwrite
   * this method to use individual priorites depending on the registered
   * region/managed object. The default implementation returns
   * {@link #getDefaultPriority()}.
   *
   * @param mo ManagedObject
   *    a managed object instance that manages <code>region</code>.
   * @param region
   *    the region to be registered.
   * @return
   *    the priority between 0 and 255 (lower value results in higher priority).
   */
  protected byte getPriority(ManagedObject mo, AgentXRegion region) {
    return defaultPriority;
  }

  /**
   * Registers the subagent regions at the master agent.
   * @param session
   *    the session on whose behalf regions are registered.
   * @param context
   *    the context to use for registration.
   * @return
   *    a List of the managed objects which failed to register.
   * @deprecated
   *    Use the <code>registerRegions</code> method with
   *    {@link RegistrationCallback} callback parameter instead.
   */
  public List registerRegions(AgentXSession session, OctetString context) {
    return registerRegions(session, context, null);
  }

  /**
   * Registers the subagent regions at the master agent.
   * @param session
   *    the session on whose behalf regions are registered.
   * @param context
   *    the context to use for registration.
   * @param sysUpTime
   *    if not <code>null</code>, the master agent's notion of the sysUpTime
   *    for the registered context is returned. The input value is always
   *    ignored!
   * @return
   *    a List of the managed objects which failed to register.
   * @deprecated
   *    Use the <code>registerRegions</code> method with
   *    {@link RegistrationCallback} callback parameter instead.
   */
  public List registerRegions(AgentXSession session, OctetString context,
                              TimeTicks sysUpTime) {
    final List failures = new LinkedList();
    RegistrationCallback regCallback = new RegistrationCallback() {
      public void registrationEvent(OctetString context,
                                    ManagedObject mo, int result) {
        if (result != AgentXProtocol.AGENTX_SUCCESS) {
          failures.add(mo);
        }
      }

      public boolean tableRegistrationEvent(OctetString context,
                                            MOTable table, MOTableRow row,
                                            boolean indexAllocation, int result,
                                            int retryCount) {
        if (result != AgentXProtocol.AGENTX_SUCCESS) {
          failures.add(row);
        }
        return false;
      }

      public void unregistrationEvent(OctetString context,
                                      ManagedObject mo, int status) {
      }

      public void tableUnregistrationEvent(OctetString context,
                                           MOTable mo, MOTableRow row,
                                           boolean indexAllocation, int status) {
      }

    };
    return failures;
  }

  /**
   * Registers the subagent regions at the master agent.
   * @param session
   *    the session on whose behalf regions are registered.
   * @param context
   *    the context to use for registration.
   * @param sysUpTime
   *    if not <code>null</code>, the master agent's notion of the sysUpTime
   *    for the registered context is returned. The input value is always
   *    ignored!
   * @param registrationCallback
   *    a possibly <code>null</code> reference to a
   *    <code>RegistrationCallback</code> instance to handle registration
   *    events.
   */
  public void registerRegions(AgentXSession session, OctetString context,
                              TimeTicks sysUpTime,
                              RegistrationCallback registrationCallback) {
    MOServer server = getServer(context);
    if (server == null) {
      LOGGER.warn("No MOServer found for context '"+context+"'");
      return;
    }
    for (Iterator it = server.iterator(); it.hasNext();) {
      Entry e = (Entry) it.next();
      ManagedObject mo = (ManagedObject)e.getValue();
      MOScope scope = (MOScope) e.getKey();
      if (context != null) {
        if ((scope instanceof MOContextScope) &&
            (!context.equals(((MOContextScope) scope).getContext()))) {
          continue;
        }
      }
      if (mo instanceof AgentXSharedMOTable) {
        registerSharedTableRows(session, context,
                                (AgentXSharedMOTable)mo,
                                registrationCallback);
      }
      else {
        AgentXRegion region =
            new AgentXRegion(scope.getLowerBound(), scope.getUpperBound());
        if (mo instanceof MOScalar) {
          region.setSingleOID(true);
        }
        region.setUpperIncluded(scope.isUpperIncluded());
        try {
          int status = registerRegion(session, context, region,
                                      getPriority(mo, region), sysUpTime);
          if (status != AgentXProtocol.AGENTX_SUCCESS) {
            if (LOGGER.isWarnEnabled()) {
              LOGGER.warn("Failed to registered MO " + scope +
                          " with status = " +
                          status);
            }
          }
          else {
            if (LOGGER.isInfoEnabled()) {
              LOGGER.info("Registered MO " + scope + " successfully");
            }
          }
          if (registrationCallback != null) {
            registrationCallback.registrationEvent(context, mo, status);
          }
        }
        catch (IOException ex) {
          LOGGER.warn("Failed to register " + mo + " in context '" + context +
                      "' of session " + session);
          if (registrationCallback != null) {
            registrationCallback.registrationEvent(context, mo,
                AgentXProtocol.AGENTX_ERROR);
          }
        }
      }
    }
  }

  /**
   * Registers the indexes and (row) regions of a shared table. This method
   * is called on behalf of {@link #registerRegions(AgentXSession session,
   * OctetString context, TimeTicks sysUpTime)} and
   * {@link #registerRegions(AgentXSession session, OctetString context)}.
   *
   * @param session
   *    the session on whose behalf regions are registered.
   * @param context
   *    the context to use for registration.
   * @param mo
   *    the <code>AgentXSharedMOTable</code> instance to register.
   * @return List
   *    a possibly <code>null</code> reference to a
   *    <code>RegistrationCallback</code> instance to handle registration
   *    events.
   * @deprecated
   *    Use {@link #registerSharedTableRows(AgentXSession session,
   *    OctetString context, AgentXSharedMOTable mo, RegistrationCallback
   *    registrationCallback)} instead.
   */
  public List registerSharedTableRows(AgentXSession session,
                                      OctetString context,
                                      AgentXSharedMOTable mo) {
    final LinkedList failed = new LinkedList();
    RegistrationCallback regCallback = new RegistrationCallback() {
      public void registrationEvent(OctetString context,
                                    ManagedObject mo, int status) {
      }

      public boolean tableRegistrationEvent(OctetString context,
                                            MOTable mo, MOTableRow row,
                                            boolean indexAllocation, int status,
                                            int retryCount) {
        if (status != AgentXProtocol.AGENTX_SUCCESS) {
          failed.add(row);
        }
        return false;
      }

      public void unregistrationEvent(OctetString context,
                                      ManagedObject mo, int status) {
      }

      public void tableUnregistrationEvent(OctetString context,
                                           MOTable mo, MOTableRow row,
                                           boolean indexAllocation, int status) {
      }
    };
    return failed;
  }

  /**
   * Registers the indexes and (row) regions of a shared table. This method
   * is called on behalf of {@link #registerRegions(AgentXSession session,
   * OctetString context, TimeTicks sysUpTime)} and
   * {@link #registerRegions(AgentXSession session, OctetString context)}.
   *
   * @param session
   *    the session on whose behalf regions are registered.
   * @param context
   *    the context to use for registration.
   * @param mo
   *    the <code>AgentXSharedMOTable</code> instance to register.
   * @param registrationCallback
   *    if not <code>null</code> the callback is informed when registration
   *    of a row succeeded or failed.
   */
  public void registerSharedTableRows(AgentXSession session,
                                      OctetString context,
                                      AgentXSharedMOTable mo,
                                      RegistrationCallback registrationCallback) {
    AgentXSharedMOTableSupport sharedTableSupport =
        new AgentXSharedMOTableSupport(agentX, session, context);
    synchronized (mo) {
      if (mo instanceof AgentXSharedMutableMOTable) {
        ((AgentXSharedMutableMOTable)
         mo).setAgentXSharedMOTableSupport(sharedTableSupport);
      }
      // decouple iterator from table modifications (may still fail if table
      // is being modified while row list is copied - if such a concurrency is
      // needed a table model must be used that returns an table independent
      // iterator.
      ArrayList rows = new ArrayList(mo.getModel().getRowCount());
      for (Iterator it = mo.getModel().iterator(); it.hasNext();) {
        rows.add(it.next());
      }
      for (Iterator it = rows.iterator(); it.hasNext();) {
        boolean retry = true;
        MOTableRow row = (MOTableRow) it.next();
        int retries = 0;
        int status;
        OID newIndex;
        do {
          newIndex = (OID) row.getIndex().clone();
          status = sharedTableSupport.allocateIndex(context, mo.getIndexDef(),
              (byte) AgentXSharedMOTableSupport.INDEX_MODE_ALLOCATE,
              newIndex);
        }
        while (retry && (registrationCallback != null) &&
               registrationCallback.tableRegistrationEvent(context,
            mo, row, true, status, retries++));
        if (status == AgentXProtocol.AGENTX_SUCCESS) {
          if ((newIndex instanceof AnyNewIndexOID) ||
              (newIndex instanceof NewIndexOID)) {
            if (mo instanceof AgentXSharedMutableMOTable) {
              ((AgentXSharedMutableMOTable)mo).
                  changeRowIndex(row.getIndex(), newIndex);
            }
            retry = false;
          }
          status = sharedTableSupport.registerRow(mo, row);
          if (status != AgentXProtocol.AGENTX_SUCCESS) {
            sharedTableSupport.deallocateIndex(context, mo.getIndexDef(),
                                               row.getIndex());
            LOGGER.warn("Failed to register row with " + status + " for " +
                        row);
          }
          if (registrationCallback != null) {
            registrationCallback.tableRegistrationEvent(context,
                mo, row, false, status, retries++);
          }
        }
        else {
          LOGGER.warn("Failed to allocate index with "+status+" for row "+
                      row);
        }
      }
    }
  }

  protected int registerRegion(AgentXSession session,
                               OctetString context, AgentXRegion region,
                               byte priority,
                               TimeTicks sysUpTime) throws IOException {
    if ((session == null) || (session.isClosed())) {
      return AgentXProtocol.AGENTX_NOT_OPEN;
    }
    long t = (this.timeout == 0) ? session.getTimeout()*1000 : this.timeout;
    AgentXRegisterPDU pdu =
        new AgentXRegisterPDU(context, region.getLowerBound(), priority,
                              region.getRangeSubID(),
                              region.getUpperBoundSubID());
    pdu.setSessionAttributes(session);
    AgentXResponseEvent event =
        agentX.send(pdu, new AgentXTarget(session.getPeer().getAddress(), t),
                    session.getPeer().getTransport());
    if ((sysUpTime != null) && (event.getResponse() != null)) {
      sysUpTime.setValue(event.getResponse().getSysUpTime() & 0xFFFFFFFFL);
    }
    return getResponseStatus(event);
  }

  protected int unregisterRegion(AgentXSession session,
                                 OctetString context, AgentXRegion region,
                                 byte timeout) throws IOException {
    if ((session == null) || (session.isClosed())) {
      return AgentXProtocol.AGENTX_NOT_OPEN;
    }
    byte t = (timeout == 0) ? session.getTimeout() : timeout;
    AgentXUnregisterPDU pdu =
        new AgentXUnregisterPDU(context, region.getLowerBound(), t,
                                region.getRangeSubID(),
                                region.getUpperBoundSubID());
    pdu.setSessionAttributes(session);
    AgentXResponseEvent event =
        agentX.send(pdu, new AgentXTarget(session.getPeer().getAddress(),
                                          this.timeout),
                    session.getPeer().getTransport());
    return getResponseStatus(event);
  }



  protected TransportMapping addMaster(Address localAddress)
      throws IOException
  {
/*
    if (transport != null) {
      try {
        transport.close();
      }
      catch (IOException ex) {
        logger.error(ex);
      }
      agentX.removeTransportMapping(transport);
    }
*/
    TransportMapping transport =
          TransportMappings.getInstance().createTransportMapping(localAddress);
    if (transport instanceof ConnectionOrientedTransportMapping) {
      ConnectionOrientedTransportMapping tcpTransport =
          (ConnectionOrientedTransportMapping)transport;
      tcpTransport.setConnectionTimeout(0);
      tcpTransport.setMessageLengthDecoder(new AgentXProtocol());
    }
    agentX.addTransportMapping(transport);
    transport.listen();
    return transport;
  }

  protected void removeMaster(TransportMapping transport) {
    agentX.removeTransportMapping(transport);
    try {
      transport.close();
    }
    catch (IOException ex) {
      LOGGER.warn("Closing transport mapping "+transport+" failed with: "+
                  ex.getMessage());
    }
  }

  public synchronized void addMOServer(MOServer server) {
    moServers.add(server);
  }

  public synchronized void removeMOServer(MOServer server) {
    moServers.remove(server);
  }

  public synchronized MOServer getServer(OctetString context) {
    for (int i=0; i<moServers.size(); i++) {
      MOServer s = (MOServer)moServers.get(i);
      if (s.isContextSupported(context)) {
        return s;
      }
    }
    return null;
  }

  public synchronized Collection getContexts() {
    LinkedList allContexts = new LinkedList();
    for (int i=0; i<moServers.size(); i++) {
      MOServer s = (MOServer)moServers.get(i);
      OctetString[] contexts = s.getContexts();
      allContexts.addAll(Arrays.asList(contexts));
    }
    return allContexts;
  }

  public WorkerPool getWorkerPool() {
    return threadPool;
  }

  public void setThreadPool(ThreadPool threadPool) {
    this.threadPool = threadPool;
  }

  public void dispatchCommand(AgentXCommandEvent cmd) {
    boolean pendingSessionClose = false;
    if (cmd.getCommand().isConfirmedPDU()) {
      AgentXRequest request = null;
      MOServer server = null;
      int type = cmd.getCommand().getType();
      switch (type) {
        case AgentXPDU.AGENTX_GET_PDU: {
          request = (AgentXRequest) factory.createRequest(cmd, null);
          server = getServer(request.getContext());
          requestHandlerGet.processPdu(request, server);
          break;
        }
        case AgentXPDU.AGENTX_GETNEXT_PDU: {
          request = (AgentXRequest) factory.createRequest(cmd, null);
          server = getServer(request.getContext());
          requestHandlerGetNext.processPdu(request, server);
          break;
        }
        case AgentXPDU.AGENTX_GETBULK_PDU: {
          request = (AgentXRequest) factory.createRequest(cmd, null);
          server = getServer(request.getContext());
          requestHandlerGetBulk.processPdu(request, server);
          break;
        }
        case AgentXPDU.AGENTX_TESTSET_PDU: {
          request = (AgentXRequest) factory.createRequest(cmd, null);
          request.setPhase(Request.PHASE_2PC_PREPARE);
          server = getServer(request.getContext());
          requestHandlerTestSet.processPdu(request, server);
          requestList.put(createRequestID(cmd), request);
          break;
        }
        case AgentXPDU.AGENTX_COMMITSET_PDU:
        case AgentXPDU.AGENTX_UNDOSET_PDU:
        case AgentXPDU.AGENTX_CLEANUPSET_PDU: {
          RequestID reqID = createRequestID(cmd);
          request = (AgentXRequest) requestList.get(reqID);
          if (request == null) {
            LOGGER.error("Request with ID "+reqID+" not found in request list");
            request = new AgentXRequest(cmd);
            request.setErrorStatus(AgentXProtocol.AGENTX_PROCESSING_ERROR);
            break;
          }
          server = getServer(request.getContext());
          switch (type) {
            case AgentXPDU.AGENTX_COMMITSET_PDU:
              request.setPhase(Request.PHASE_2PC_COMMIT);
              requestHandlerCommitSet.processPdu(request, server);
              break;
            case AgentXPDU.AGENTX_UNDOSET_PDU:
              request.setPhase(Request.PHASE_2PC_UNDO);
              requestHandlerUndoSet.processPdu(request, server);
              break;
            case AgentXPDU.AGENTX_CLEANUPSET_PDU:
              request.setPhase(Request.PHASE_2PC_CLEANUP);
              requestHandlerCleanupSet.processPdu(request, server);
              break;
            default: {
              LOGGER.fatal("Internal error");
            }
          }
          if (cmd.getCommand().getType() != AgentXPDU.AGENTX_COMMITSET_PDU) {
            // remove request from request list
            requestList.remove(reqID);
          }
          break;
        }
        case AgentXPDU.AGENTX_CLOSE_PDU: {
          AgentXSession session =
              removeSession(cmd.getCommand().getSessionID());
          if (session != null) {
            session.setClosed(true);
            pendingSessionClose = true;
          }
          break;
        }
        default: {
          LOGGER.error("Unhandled PDU type: "+cmd.getCommand());
          request = new AgentXRequest(cmd);
          request.setErrorStatus(AgentXProtocol.AGENTX_PROCESSING_ERROR);
        }
      }
      if (request != null) {
        // Since this is an AgentX subagent it only processes a single phase at
        // once.
        if ((type != AgentXPDU.AGENTX_CLEANUPSET_PDU) &&
            request.isPhaseComplete()) {
          // send response
          sendResponse(cmd, request);
        }
        if (server != null) {
          release(server, request);
        }
      }
      if (pendingSessionClose) {
        try {
          disconnect(cmd.getPeerAddress());
        }
        catch (IOException ex) {
          LOGGER.error("Failed to disconnect from master at "+
                       cmd.getPeerAddress()+": "+ex.getMessage(), ex);
        }
      }
    }
    else {
      processResponse(cmd);
    }
  }

  protected void sendResponse(AgentXCommandEvent cmd, AgentXRequest request) {
    AgentXMessageDispatcher dispatcher = cmd.getDispatcher();
    AgentXResponsePDU response = request.getResponsePDU();
    if (response != null) {
      AgentXPDU rpdu = cmd.getCommand();
      response.setSessionID(rpdu.getSessionID());
      response.setTransactionID(rpdu.getTransactionID());
      response.setByteOrder(rpdu.getByteOrder());
      response.setPacketID(rpdu.getPacketID());
      // only send a response if required
      try {
        dispatcher.send(cmd.getPeerTransport(),
                        cmd.getPeerAddress(), response, null);
      }
      catch (IOException ex) {
        LOGGER.warn("Failed to send AgentX response to '"+
                    cmd.getPeerAddress()+"' with error: "+ex.getMessage());
      }
    }
  }

  protected void release(MOServer server, Request req) {
    for (Iterator it = req.iterator(); it.hasNext();) {
      SubRequest sreq = (SubRequest)it.next();
      if (sreq.getTargetMO() != null) {
        server.unlock(req, sreq.getTargetMO());
      }
    }
  }

  private static RequestID createRequestID(AgentXCommandEvent cmd) {
    return new RequestID(cmd.getPeerAddress(),
                         cmd.getCommand().getSessionID(),
                         cmd.getCommand().getTransactionID());

  }

  protected void processResponse(AgentXCommandEvent cmd) {
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Received response "+cmd);
    }
  }

  protected void processNextSubRequest(Request request, MOServer server,
                                       OctetString context,
                                       SubRequest sreq)
      throws NoSuchElementException
  {
    // We can be sure to have a default context scope here because
    // the inner class AgentXSubRequest creates it!
    DefaultMOContextScope scope =
        (DefaultMOContextScope)sreq.getScope();
    MOQuery query = sreq.getQuery();
    if (query == null) {
      query = new DefaultMOQuery(scope, false, request);
    }
    while (!sreq.getStatus().isProcessed()) {
      ManagedObject mo = server.lookup(query);
      if (mo == null) {
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("EndOfMibView at scope="+query.getScope()+
                       " and query "+query);
        }
        sreq.getVariableBinding().setVariable(Null.endOfMibView);
        sreq.getStatus().setPhaseComplete(true);
        break;
      }
      try {
        if (!mo.next(sreq)) {
          // We can be sure to have a default context scope here because
          // the inner class SnmpSubRequest creates it!
          // don't forget to update query:
          sreq.getVariableBinding().setVariable(Null.instance);
          scope.substractScope(mo.getScope());
          // query is updated automatically because scope is updated.
          query.substractScope(mo.getScope());
        }
      }
      catch (Exception moex) {
        if (LOGGER.isDebugEnabled()) {
          moex.printStackTrace();
        }
        LOGGER.warn(moex);
        if (sreq.getStatus().getErrorStatus() == PDU.noError) {
          sreq.getStatus().setErrorStatus(PDU.genErr);
        }
      }
    }
  }

  /**
   * Sends notifications (traps) to all appropriate notification targets
   * through the master agent.
   *
   * @param context the context name of the context on whose behalf this
   *   notification has been generated.
   * @param notificationID the object ID that uniquely identifies this
   *   notification. For SNMPv1 traps, the notification ID has to be build
   *   using the rules provided by RFC 2576.
   * @param vbs an array of <code>VariableBinding</code> instances
   *   representing the payload of the notification.
   * @return
   *    an {@link AgentXResponseEvent} instance or <code>null</code> if the
   *    notification request timed out.
   */
  public Object notify(OctetString context,
                       OID notificationID,
                       VariableBinding[] vbs) {
    return notify(context, notificationID, null, vbs);
  }

  public Object notify(OctetString context, OID notificationID,
                       TimeTicks sysUpTime, VariableBinding[] vbs) {
    AgentXSession session = firstSession();
    AgentXResponseEvent agentXResponse = null;
    try {
      agentXResponse =
          notify(session, context, notificationID, sysUpTime, vbs);
      if ((agentXResponse == null) || (agentXResponse.getResponse() == null)) {
        LOGGER.warn("Timeout on sending notification in context '"+context+
                   "' with ID '"+notificationID+"' and payload "+
                   Arrays.asList(vbs));
        return null;
      }
      return agentXResponse;
    }
    catch (IOException ex) {
      LOGGER.error("Failed to send notification in context '"+context+
                   "' with ID '"+notificationID+"' and payload "+
                   Arrays.asList(vbs)+", reason is: "+ex.getMessage());
      return null;
    }
  }

  /**
   * Returns the first session that have been opened by this subagent and is
   * still open. If no open session exists, <code>null</code> is returned.
   *
   * @return
   *    an <code>AgentXSession</code>.
   */
  public synchronized final AgentXSession firstSession() {
    if (sessions.size() > 0) {
      return (AgentXSession) sessions.values().iterator().next();
    }
    return null;
  }

  public AgentXResponseEvent notify(AgentXSession session,
                                    OctetString context,
                                    OID notificationID,
                                    TimeTicks sysUpTime,
                                    VariableBinding[] vbs) throws IOException {
    int offset = 1;
    if (sysUpTime != null) {
      offset = 2;
    }
    VariableBinding[] notifyVBs = new VariableBinding[vbs.length+offset];
    if (sysUpTime != null) {
      notifyVBs[0] = new VariableBinding(SnmpConstants.sysUpTime, sysUpTime);
    }
    notifyVBs[offset-1] =
        new VariableBinding(SnmpConstants.snmpTrapOID, notificationID);
    System.arraycopy(vbs, 0, notifyVBs, offset, vbs.length);
    AgentXNotifyPDU notifyPDU = new AgentXNotifyPDU(context, notifyVBs);
    notifyPDU.setSessionAttributes(session);
    notifyPDU.setTransactionID(getNextTransactionID());
    AgentXResponseEvent response =
        agentX.send(notifyPDU, session.createAgentXTarget(),
                    session.getPeer().getTransport());
    return response;
  }

  public int addAgentCaps(AgentXSession session,
                          OctetString context, OID id, OctetString descr) {
    AgentXAddAgentCapsPDU pdu = new AgentXAddAgentCapsPDU(context, id, descr);
    pdu.setSessionAttributes(session);
    try {
      AgentXResponseEvent resp = agentX.send(pdu, session.createAgentXTarget(),
                                             session.getPeer().getTransport());
      if (resp.getResponse() == null) {
        return AgentXProtocol.AGENTX_TIMEOUT;
      }
      return resp.getResponse().getErrorStatus();
    }
    catch (IOException ex) {
      LOGGER.error("Failed to send AgentX AddAgentCaps PDU "+pdu+
                   " because: "+ex.getMessage(), ex);
      return AgentXProtocol.AGENTX_NOT_OPEN;
    }
  }

  public int removeAgentCaps(AgentXSession session,
                             OctetString context, OID id) {
    AgentXRemoveAgentCapsPDU pdu = new AgentXRemoveAgentCapsPDU(context, id);
    pdu.setSessionAttributes(session);
    try {
      AgentXResponseEvent resp = agentX.send(pdu, session.createAgentXTarget(),
                                             session.getPeer().getTransport());
      return resp.getResponse().getErrorStatus();
    }
    catch (IOException ex) {
      LOGGER.error("Failed to send AgentX RemoveAgentCaps PDU "+pdu+
                   " because: "+ex.getMessage(), ex);
      return AgentXProtocol.AGENTX_NOT_OPEN;
    }
  }

  public void addPingListener(PingListener l) {
    if (pingListeners == null) {
      pingListeners = new Vector();
    }
    pingListeners.add(l);
  }

  public void removePingListener(PingListener l) {
    if (pingListeners != null) {
      synchronized (pingListeners) {
        pingListeners.remove(l);
      }
    }
  }

  protected void firePinged(PingEvent event) {
    if (pingListeners != null) {
      synchronized (pingListeners) {
        Vector listeners = pingListeners;
        int count = listeners.size();
        for (int i = 0; i < count; i++) {
          ((PingListener) listeners.elementAt(i)).pinged(event);
        }
      }
    }
  }

  private static void initRequestPhase(Request request) {
    if (request.getPhase() == Request.PHASE_INIT) {
      request.nextPhase();
    }
  }

  static class GetRequestHandler implements RequestHandler {

    public boolean isSupported(int pduType) {
      return pduType == AgentXPDU.AGENTX_GET_PDU;
    }

    public void processPdu(Request request, MOServer server) {
      initRequestPhase(request);
      try {
        SubRequestIterator it = (SubRequestIterator) request.iterator();
        while (it.hasNext()) {
          SubRequest sreq =  it.nextSubRequest();
          DefaultMOQuery query =
              new DefaultMOQuery((MOContextScope)sreq.getScope(),
                                 false, request);
          ManagedObject mo = server.lookup(query);
          if (mo == null) {
            sreq.getVariableBinding().setVariable(Null.noSuchObject);
            sreq.getStatus().setPhaseComplete(true);
            continue;
          }
          try {
            mo.get(sreq);
          }
          catch (Exception moex) {
            if (LOGGER.isDebugEnabled()) {
              moex.printStackTrace();
            }
            LOGGER.warn(moex);
            if (sreq.getStatus().getErrorStatus() == PDU.noError) {
              sreq.getStatus().setErrorStatus(PDU.genErr);
            }
          }
        }
      }
      catch (NoSuchElementException nsex) {
        if (LOGGER.isDebugEnabled()) {
          nsex.printStackTrace();
        }
        LOGGER.error("SubRequest not found");
        request.setErrorStatus(PDU.genErr);
      }
    }
  }

  class GetNextHandler implements RequestHandler {

    public void processPdu(Request request, MOServer server) {
      initRequestPhase(request);
      OctetString context = request.getContext();
      try {
        SubRequestIterator it = (SubRequestIterator) request.iterator();
        while (it.hasNext()) {
          SubRequest sreq =  it.nextSubRequest();
          processNextSubRequest(request, server, context, sreq);
        }
      }
      catch (NoSuchElementException nsex) {
        if (LOGGER.isDebugEnabled()) {
          nsex.printStackTrace();
        }
        LOGGER.error("SubRequest not found");
        request.setErrorStatus(PDU.genErr);
      }
    }


    public boolean isSupported(int pduType) {
      return (pduType == PDU.GETNEXT);
    }

  }

  class GetBulkHandler implements RequestHandler {

    public void processPdu(Request request, MOServer server) {
      initRequestPhase(request);
      OctetString context = request.getContext();
      AgentXRequest req = (AgentXRequest)request;
      int nonRep = req.getNonRepeaters();
      try {
        SubRequestIterator it = (SubRequestIterator) request.iterator();
        int i = 0;
        // non repeaters
        for (; ((i < nonRep) && it.hasNext()); i++) {
          SubRequest sreq =  it.nextSubRequest();
          processNextSubRequest(request, server, context, sreq);
        }
        // repetitions
        for (; it.hasNext(); i++) {
          SubRequest sreq =  it.nextSubRequest();
          processNextSubRequest(request, server, context, sreq);
        }
      }
      catch (NoSuchElementException nsex) {
        if (LOGGER.isDebugEnabled()) {
          nsex.printStackTrace();
        }
        LOGGER.error("SubRequest not found");
        request.setErrorStatus(PDU.genErr);
      }

    }

    public boolean isSupported(int pduType) {
      return (pduType == PDU.GETBULK);
    }

  }


  static class TestSetHandler implements RequestHandler {

    public void processPdu(Request request, MOServer server) {
      try {
        SubRequestIterator it = (SubRequestIterator) request.iterator();
        while ((!request.isPhaseComplete()) && (it.hasNext())) {
          SubRequest sreq =  it.nextSubRequest();
          if (sreq.isComplete()) {
            continue;
          }
          DefaultMOQuery query =
              new DefaultMOQuery((MOContextScope)sreq.getScope(), false,
                                 request);
          ManagedObject mo = server.lookup(query);
          if (mo == null) {
            sreq.getStatus().setErrorStatus(PDU.notWritable);
            break;
          }
          sreq.setTargetMO(mo);
          server.lock(sreq.getRequest(), mo);
          try {
            mo.prepare(sreq);
            sreq.getStatus().setPhaseComplete(true);
          }
          catch (Exception moex) {
            if (sreq.getStatus().getErrorStatus() == PDU.noError) {
              sreq.getStatus().setErrorStatus(PDU.genErr);
            }
            LOGGER.error("Exception occurred while preparing SET request, "+
                         "returning genErr: "+moex.getMessage(), moex);
          }
        }
      }
      catch (NoSuchElementException nsex) {
        if (LOGGER.isDebugEnabled()) {
          nsex.printStackTrace();
        }
        LOGGER.error("Cannot find sub-request: ", nsex);
        request.setErrorStatus(PDU.genErr);
      }
    }

    public boolean isSupported(int pduType) {
      return (pduType == AgentXPDU.AGENTX_TESTSET_PDU);
    }
  }

  class UndoSetHandler implements RequestHandler {

    public void processPdu(Request request, MOServer server) {
      try {
        SubRequestIterator it = (SubRequestIterator) request.iterator();
        while (it.hasNext()) {
          SubRequest sreq =  it.nextSubRequest();
          if (sreq.isComplete()) {
            continue;
          }
          ManagedObject mo = sreq.getTargetMO();
          if (mo == null) {
            DefaultMOQuery query =
                new DefaultMOQuery((MOContextScope)sreq.getScope(), true);
            mo = server.lookup(query);
          }
          if (mo == null) {
            sreq.getStatus().setErrorStatus(PDU.undoFailed);
            continue;
          }
          try {
            mo.undo(sreq);
            sreq.getStatus().setPhaseComplete(true);
          }
          catch (Exception moex) {
            if (LOGGER.isDebugEnabled()) {
              moex.printStackTrace();
            }
            LOGGER.error(moex);
            if (sreq.getStatus().getErrorStatus() == PDU.noError) {
              sreq.getStatus().setErrorStatus(PDU.undoFailed);
            }
          }
        }
      }
      catch (NoSuchElementException nsex) {
        if (LOGGER.isDebugEnabled()) {
          nsex.printStackTrace();
        }
        LOGGER.error("Cannot find sub-request: ", nsex);
        request.setErrorStatus(PDU.genErr);
      }
    }

    public boolean isSupported(int pduType) {
      return (pduType == AgentXPDU.AGENTX_UNDOSET_PDU);
    }
  }

  class CommitSetHandler implements RequestHandler {

    public void processPdu(Request request, MOServer server) {
      try {
        SubRequestIterator it = (SubRequestIterator) request.iterator();
        while ((!request.isPhaseComplete()) && (it.hasNext())) {
          SubRequest sreq =  it.nextSubRequest();
          if (sreq.isComplete()) {
            continue;
          }
          ManagedObject mo = sreq.getTargetMO();
          if (mo == null) {
            DefaultMOQuery query =
                new DefaultMOQuery((MOContextScope)sreq.getScope(), true);
            mo = server.lookup(query);
          }
          if (mo == null) {
            sreq.getStatus().setErrorStatus(PDU.commitFailed);
            continue;
          }
          try {
            mo.commit(sreq);
            sreq.getStatus().setPhaseComplete(true);
          }
          catch (Exception moex) {
            if (LOGGER.isDebugEnabled()) {
              moex.printStackTrace();
            }
            LOGGER.error(moex);
            if (sreq.getStatus().getErrorStatus() == PDU.noError) {
              sreq.getStatus().setErrorStatus(PDU.commitFailed);
            }
          }
        }
      }
      catch (NoSuchElementException nsex) {
        if (LOGGER.isDebugEnabled()) {
          nsex.printStackTrace();
        }
        LOGGER.error("Cannot find sub-request: ", nsex);
        request.setErrorStatus(PDU.genErr);
      }
    }

    public boolean isSupported(int pduType) {
      return (pduType == AgentXPDU.AGENTX_COMMITSET_PDU);
    }

  }

  class CleanupSetHandler implements RequestHandler {

    public void processPdu(Request request, MOServer server) {
      try {
        SubRequestIterator it = (SubRequestIterator) request.iterator();
        while (it.hasNext()) {
          SubRequest sreq =  it.nextSubRequest();
          if (sreq.isComplete()) {
            continue;
          }
          ManagedObject mo = sreq.getTargetMO();
          if (mo == null) {
            DefaultMOQuery query =
                new DefaultMOQuery((MOContextScope)sreq.getScope(), false);
            mo = server.lookup(query);
          }
          if (mo == null) {
            sreq.completed();
            continue;
          }
          server.unlock(sreq.getRequest(), mo);
          try {
            mo.cleanup(sreq);
            sreq.getStatus().setPhaseComplete(true);
          }
          catch (Exception moex) {
            if (LOGGER.isDebugEnabled()) {
              moex.printStackTrace();
            }
            LOGGER.error(moex);
          }
        }
      }
      catch (NoSuchElementException nsex) {
        if (LOGGER.isDebugEnabled()) {
          nsex.printStackTrace();
        }
        LOGGER.warn("Cannot find sub-request: "+ nsex.getMessage());
      }
    }

    public boolean isSupported(int pduType) {
      return (pduType == AgentXPDU.AGENTX_CLEANUPSET_PDU);
    }

  }


  static class DefaultAgentXRequestFactory implements RequestFactory {

    public Request createRequest(EventObject initiatingEvent,
                                 CoexistenceInfo cinfo) {
      Request request = new AgentXRequest((AgentXCommandEvent)initiatingEvent);
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Creating AgentX request "+request+
                     " from "+initiatingEvent);
      }
      return request;
    }

  }

  class Command implements WorkerTask {

    private AgentXCommandEvent request;

    public Command(AgentXCommandEvent event) {
      this.request = event;
    }

    public void run() {
      dispatchCommand(request);
    }

    public void terminate() {
    }

    public void join() throws InterruptedException {
    }

    public void interrupt() {
    }

  }


  static class RequestID implements Comparable {
    private Address masterAddress;
    private int sessionID;
    private int transactionID;

    public RequestID(Address masterAddress, int sessionID, int transactionID) {
      this.masterAddress = masterAddress;
      this.sessionID = sessionID;
      this.transactionID = transactionID;
    }

    public int compareTo(Object o) {
      RequestID other = (RequestID)o;
      int c = masterAddress.compareTo(other.masterAddress);
      if (c == 0) {
        c = sessionID - other.sessionID;
        if (c == 0) {
          c = transactionID - other.transactionID;
        }
      }
      return c;
    }

    public boolean equals(Object obj) {
      if (obj instanceof RequestID) {
        return (compareTo(obj) == 0);
      }
      return false;
    }

    public int hashCode() {
      return transactionID;
    }

  }

  class PingTask extends TimerTask {

    public void run() {
      List l;
      synchronized (sessions) {
        l = new LinkedList(sessions.values());
      }
      for (Iterator it = l.iterator(); it.hasNext();) {
        AgentXSession session = (AgentXSession) it.next();
        if (!session.isClosed()) {
          for (Iterator cit = getContexts().iterator(); cit.hasNext(); ) {
            OctetString context = (OctetString) cit.next();
            AgentXPingPDU ping = new AgentXPingPDU(context);
            ping.setSessionAttributes(session);
            ping.setTransactionID(getNextTransactionID());
            PingEvent pingEvent;
            try {
              AgentXResponseEvent resp =
                  agentX.send(ping, session.createAgentXTarget(),
                              session.getPeer().getTransport());
              pingEvent = new PingEvent(this, session,
                                        resp.getResponse());
            }
            catch (IOException ex) {
              pingEvent = new PingEvent(this, session, ex);
            }
            firePinged(pingEvent);
            if (LOGGER.isDebugEnabled()) {
              LOGGER.debug("Fired ping event " + pingEvent);
            }
            if (pingEvent.isCloseSession() || pingEvent.isResetSession()) {
              try {
                closeSession(session.getSessionID(),
                             AgentXProtocol.REASON_TIMEOUTS);
                if (pingEvent.isResetSession()) {
                  reopenSession(session);
                }
              }
              catch (IOException ex1) {
              }
            }
          }
        }
      }
    }

    /**
     * Reopens a closed session.
     *
     * @param session
     *    a closed AgentXSession instance.
     * @return
     *    {@link AgentXProtocol#AGENTX_SUCCESS} if the session could be opened
     *    sucessfully. Otherwise the AgentX error status is returned.
     * @throws IOException
     *    if the session cannot be reopened due to an IO exception.
     */
    public int reopenSession(AgentXSession session) throws IOException {
      return openSession(session.getPeer().getTransport(),
                         session.getPeer().getAddress(),
                         session);
    }

  }

}
TOP

Related Classes of org.snmp4j.agent.agentx.subagent.AgentXSubagent$CleanupSetHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.