/* * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jmx.snmp.agent; // java imports // import java.util.Enumeration; import java.util.Iterator; // jmx imports // import javax.management.AttributeList; import javax.management.Attribute; import javax.management.MBeanException; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.ReflectionException; import javax.management.InstanceNotFoundException; import javax.management.InvalidAttributeValueException; import javax.management.InstanceAlreadyExistsException; import javax.management.MBeanRegistrationException; import javax.management.NotCompliantMBeanException; import javax.management.RuntimeOperationsException; import com.sun.jmx.snmp.SnmpOid; import com.sun.jmx.snmp.SnmpValue; import com.sun.jmx.snmp.SnmpVarBind; import com.sun.jmx.snmp.SnmpStatusException; /** * <p> * This class is a utility class that transforms SNMP GET / SET requests * into standard JMX getAttributes() setAttributes() requests. * </p> * * <p> * The transformation relies on the metadata information provided by the * {@link com.sun.jmx.snmp.agent.SnmpGenericMetaServer} object which is * passed as the first parameter to every method. This SnmpGenericMetaServer * object is usually a Metadata object generated by <code>mibgen</code>. * </p> * * <p><b><i> * This class is used internally by mibgen generated metadata objects and * you should never need to use it directly. * </b></i></p> * <p><b>This API is a Sun Microsystems internal API and is subject * to change without notice.</b></p> **/ public class SnmpGenericObjectServer { // ---------------------------------------------------------------------- // // Protected variables // // ---------------------------------------------------------------------- /** * The MBean server through which the MBeans will be accessed. **/ protected final MBeanServer server; // ---------------------------------------------------------------------- // // Constructors // // ---------------------------------------------------------------------- /** * Builds a new SnmpGenericObjectServer. Usually there will be a single * object of this type per MIB. * * @param server The MBeanServer in which the MBean accessed by this * MIB are registered. **/ public SnmpGenericObjectServer(MBeanServer server) { this.server = server; } /** * Execute an SNMP GET request. * * <p> * This method first builds the list of attributes that need to be * retrieved from the MBean and then calls getAttributes() on the * MBean server. Then it updates the SnmpMibSubRequest with the values * retrieved from the MBean. * </p> * * <p> * The SNMP metadata information is obtained through the given * <code>meta</code> object, which usually is an instance of a * <code>mibgen</code> generated class. * </p> * * <p><b><i> * This method is called internally by <code>mibgen</code> generated * objects and you should never need to call it directly. * </i></b></p> * * @param meta The metadata object impacted by the subrequest * @param name The ObjectName of the MBean impacted by this subrequest * @param req The SNMP subrequest to execute on the MBean * @param depth The depth of the SNMP object in the OID tree. * * @exception SnmpStatusException whenever an SNMP exception must be * raised. Raising an exception will abort the request.<br> * Exceptions should never be raised directly, but only by means of * <code> * req.registerGetException(<i>VariableId</i>,<i>SnmpStatusException</i>) * </code> **/ public void get(SnmpGenericMetaServer meta, ObjectName name, SnmpMibSubRequest req, int depth) throws SnmpStatusException { // java.lang.System.out.println(">>>>>>>>> GET " + name); final int size = req.getSize(); final Object data = req.getUserData(); final String[] nameList = new String[size]; final SnmpVarBind[] varList = new SnmpVarBind[size]; final long[] idList = new long[size]; int i = 0; for (Enumeration<SnmpVarBind> e=req.getElements(); e.hasMoreElements();) { final SnmpVarBind var= e.nextElement(); try { final long id = var.oid.getOidArc(depth); nameList[i] = meta.getAttributeName(id); varList[i] = var; idList[i] = id; // Check the access rights according to the MIB. // The MBean might be less restrictive (have a getter // while the MIB defines the variable as AFN) // meta.checkGetAccess(id,data); //java.lang.System.out.println(nameList[i] + " added."); i++; } catch(SnmpStatusException x) { //java.lang.System.out.println("exception for " + nameList[i]); //x.printStackTrace(); req.registerGetException(var,x); } } AttributeList result = null; int errorCode = SnmpStatusException.noSuchInstance; try { result = server.getAttributes(name,nameList); } catch (InstanceNotFoundException f) { //java.lang.System.out.println(name + ": instance not found."); //f.printStackTrace(); result = new AttributeList(); } catch (ReflectionException r) { //java.lang.System.out.println(name + ": reflexion error."); //r.printStackTrace(); result = new AttributeList(); } catch (Exception x) { result = new AttributeList(); } final Iterator<?> it = result.iterator(); for (int j=0; j < i; j++) { if (!it.hasNext()) { //java.lang.System.out.println(name + "variable[" + j + // "] absent"); final SnmpStatusException x = new SnmpStatusException(errorCode); req.registerGetException(varList[j],x); continue; } final Attribute att = (Attribute) it.next(); while ((j < i) && (! nameList[j].equals(att.getName()))) { //java.lang.System.out.println(name + "variable[" +j + // "] not found"); final SnmpStatusException x = new SnmpStatusException(errorCode); req.registerGetException(varList[j],x); j++; } if ( j == i) break; try { varList[j].value = meta.buildSnmpValue(idList[j],att.getValue()); } catch (SnmpStatusException x) { req.registerGetException(varList[j],x); } //java.lang.System.out.println(att.getName() + " retrieved."); } //java.lang.System.out.println(">>>>>>>>> END GET"); } /** * Get the value of an SNMP variable. * * <p><b><i> * You should never need to use this method directly. * </i></b></p> * * @param meta The impacted metadata object * @param name The ObjectName of the impacted MBean * @param id The OID arc identifying the variable we're trying to set. * @param data User contextual data allocated through the * {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory} * * @return The value of the variable. * * @exception SnmpStatusException whenever an SNMP exception must be * raised. Raising an exception will abort the request. <br> * Exceptions should never be raised directly, but only by means of * <code> * req.registerGetException(<i>VariableId</i>,<i>SnmpStatusException</i>) * </code> **/ public SnmpValue get(SnmpGenericMetaServer meta, ObjectName name, long id, Object data) throws SnmpStatusException { final String attname = meta.getAttributeName(id); Object result = null; try { result = server.getAttribute(name,attname); } catch (MBeanException m) { Exception t = m.getTargetException(); if (t instanceof SnmpStatusException) throw (SnmpStatusException) t; throw new SnmpStatusException(SnmpStatusException.noSuchInstance); } catch (Exception e) { throw new SnmpStatusException(SnmpStatusException.noSuchInstance); } return meta.buildSnmpValue(id,result); } /** * Execute an SNMP SET request. * * <p> * This method first builds the list of attributes that need to be * set on the MBean and then calls setAttributes() on the * MBean server. Then it updates the SnmpMibSubRequest with the new * values retrieved from the MBean. * </p> * * <p> * The SNMP metadata information is obtained through the given * <code>meta</code> object, which usually is an instance of a * <code>mibgen</code> generated class. * </p> * * <p><b><i> * This method is called internally by <code>mibgen</code> generated * objects and you should never need to call it directly. * </i></b></p> * * @param meta The metadata object impacted by the subrequest * @param name The ObjectName of the MBean impacted by this subrequest * @param req The SNMP subrequest to execute on the MBean * @param depth The depth of the SNMP object in the OID tree. * * @exception SnmpStatusException whenever an SNMP exception must be * raised. Raising an exception will abort the request. <br> * Exceptions should never be raised directly, but only by means of * <code> * req.registerGetException(<i>VariableId</i>,<i>SnmpStatusException</i>) * </code> **/ public void set(SnmpGenericMetaServer meta, ObjectName name, SnmpMibSubRequest req, int depth) throws SnmpStatusException { final int size = req.getSize(); final AttributeList attList = new AttributeList(size); final String[] nameList = new String[size]; final SnmpVarBind[] varList = new SnmpVarBind[size]; final long[] idList = new long[size]; int i = 0; for (Enumeration<SnmpVarBind> e=req.getElements(); e.hasMoreElements();) { final SnmpVarBind var= e.nextElement(); try { final long id = var.oid.getOidArc(depth); final String attname = meta.getAttributeName(id); final Object attvalue= meta.buildAttributeValue(id,var.value); final Attribute att = new Attribute(attname,attvalue); attList.add(att); nameList[i] = attname; varList[i] = var; idList[i] = id; i++; } catch(SnmpStatusException x) { req.registerSetException(var,x); } } AttributeList result; int errorCode = SnmpStatusException.noAccess; try { result = server.setAttributes(name,attList); } catch (InstanceNotFoundException f) { result = new AttributeList(); errorCode = SnmpStatusException.snmpRspInconsistentName; } catch (ReflectionException r) { errorCode = SnmpStatusException.snmpRspInconsistentName; result = new AttributeList(); } catch (Exception x) { result = new AttributeList(); } final Iterator<?> it = result.iterator(); for (int j=0; j < i; j++) { if (!it.hasNext()) { final SnmpStatusException x = new SnmpStatusException(errorCode); req.registerSetException(varList[j],x); continue; } final Attribute att = (Attribute) it.next(); while ((j < i) && (! nameList[j].equals(att.getName()))) { final SnmpStatusException x = new SnmpStatusException(SnmpStatusException.noAccess); req.registerSetException(varList[j],x); j++; } if ( j == i) break; try { varList[j].value = meta.buildSnmpValue(idList[j],att.getValue()); } catch (SnmpStatusException x) { req.registerSetException(varList[j],x); } } } /** * Set the value of an SNMP variable. * * <p><b><i> * You should never need to use this method directly. * </i></b></p> * * @param meta The impacted metadata object * @param name The ObjectName of the impacted MBean * @param x The new requested SnmpValue * @param id The OID arc identifying the variable we're trying to set. * @param data User contextual data allocated through the * {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory} * * @return The new value of the variable after the operation. * * @exception SnmpStatusException whenever an SNMP exception must be * raised. Raising an exception will abort the request. <br> * Exceptions should never be raised directly, but only by means of * <code> * req.registerSetException(<i>VariableId</i>,<i>SnmpStatusException</i>) * </code> **/ public SnmpValue set(SnmpGenericMetaServer meta, ObjectName name, SnmpValue x, long id, Object data) throws SnmpStatusException { final String attname = meta.getAttributeName(id); final Object attvalue= meta.buildAttributeValue(id,x); final Attribute att = new Attribute(attname,attvalue); Object result = null; try { server.setAttribute(name,att); result = server.getAttribute(name,attname); } catch(InvalidAttributeValueException iv) { throw new SnmpStatusException(SnmpStatusException.snmpRspWrongValue); } catch (InstanceNotFoundException f) { throw new SnmpStatusException(SnmpStatusException.snmpRspInconsistentName); } catch (ReflectionException r) { throw new SnmpStatusException(SnmpStatusException.snmpRspInconsistentName); } catch (MBeanException m) { Exception t = m.getTargetException(); if (t instanceof SnmpStatusException) throw (SnmpStatusException) t; throw new SnmpStatusException(SnmpStatusException.noAccess); } catch (Exception e) { throw new SnmpStatusException(SnmpStatusException.noAccess); } return meta.buildSnmpValue(id,result); } /** * Checks whether an SNMP SET request can be successfully performed. * * <p> * For each variable in the subrequest, this method calls * checkSetAccess() on the meta object, and then tries to invoke the * check<i>AttributeName</i>() method on the MBean. If this method * is not defined then it is assumed that the SET won't fail. * </p> * * <p><b><i> * This method is called internally by <code>mibgen</code> generated * objects and you should never need to call it directly. * </i></b></p> * * @param meta The metadata object impacted by the subrequest * @param name The ObjectName of the MBean impacted by this subrequest * @param req The SNMP subrequest to execute on the MBean * @param depth The depth of the SNMP object in the OID tree. * * @exception SnmpStatusException if the requested SET operation must * be rejected. Raising an exception will abort the request. <br> * Exceptions should never be raised directly, but only by means of * <code> * req.registerCheckException(<i>VariableId</i>,<i>SnmpStatusException</i>) * </code> * **/ public void check(SnmpGenericMetaServer meta, ObjectName name, SnmpMibSubRequest req, int depth) throws SnmpStatusException { final Object data = req.getUserData(); for (Enumeration<SnmpVarBind> e=req.getElements(); e.hasMoreElements();) { final SnmpVarBind var= e.nextElement(); try { final long id = var.oid.getOidArc(depth); // call meta.check() here, and meta.check will call check() check(meta,name,var.value,id,data); } catch(SnmpStatusException x) { req.registerCheckException(var,x); } } } /** * Checks whether a SET operation can be performed on a given SNMP * variable. * * @param meta The impacted metadata object * @param name The ObjectName of the impacted MBean * @param x The new requested SnmpValue * @param id The OID arc identifying the variable we're trying to set. * @param data User contextual data allocated through the * {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory} * * <p> * This method calls checkSetAccess() on the meta object, and then * tries to invoke the check<i>AttributeName</i>() method on the MBean. * If this method is not defined then it is assumed that the SET * won't fail. * </p> * * <p><b><i> * This method is called internally by <code>mibgen</code> generated * objects and you should never need to call it directly. * </i></b></p> * * @exception SnmpStatusException if the requested SET operation must * be rejected. Raising an exception will abort the request. <br> * Exceptions should never be raised directly, but only by means of * <code> * req.registerCheckException(<i>VariableId</i>,<i>SnmpStatusException</i>) * </code> * **/ // XXX xxx ZZZ zzz Maybe we should go through the MBeanInfo here? public void check(SnmpGenericMetaServer meta, ObjectName name, SnmpValue x, long id, Object data) throws SnmpStatusException { meta.checkSetAccess(x,id,data); try { final String attname = meta.getAttributeName(id); final Object attvalue= meta.buildAttributeValue(id,x); final Object[] params = new Object[1]; final String[] signature = new String[1]; params[0] = attvalue; signature[0] = attvalue.getClass().getName(); server.invoke(name,"check"+attname,params,signature); } catch( SnmpStatusException e) { throw e; } catch (InstanceNotFoundException i) { throw new SnmpStatusException(SnmpStatusException.snmpRspInconsistentName); } catch (ReflectionException r) { // checkXXXX() not defined => do nothing } catch (MBeanException m) { Exception t = m.getTargetException(); if (t instanceof SnmpStatusException) throw (SnmpStatusException) t; throw new SnmpStatusException(SnmpStatusException.noAccess); } catch (Exception e) { throw new SnmpStatusException(SnmpStatusException.noAccess); } } public void registerTableEntry(SnmpMibTable meta, SnmpOid rowOid, ObjectName objname, Object entry) throws SnmpStatusException { if (objname == null) throw new SnmpStatusException(SnmpStatusException.snmpRspInconsistentName); try { if (entry != null && !server.isRegistered(objname)) server.registerMBean(entry, objname); } catch (InstanceAlreadyExistsException e) { throw new SnmpStatusException(SnmpStatusException.snmpRspInconsistentName); } catch (MBeanRegistrationException e) { throw new SnmpStatusException(SnmpStatusException.snmpRspNoAccess); } catch (NotCompliantMBeanException e) { throw new SnmpStatusException(SnmpStatusException.snmpRspGenErr); } catch (RuntimeOperationsException e) { throw new SnmpStatusException(SnmpStatusException.snmpRspGenErr); } catch(Exception e) { throw new SnmpStatusException(SnmpStatusException.snmpRspGenErr); } } }