001: /*_############################################################################
002: _##
003: _## SNMP4J - AbstractVariable.java
004: _##
005: _## Copyright (C) 2003-2008 Frank Fock and Jochen Katz (SNMP4J.org)
006: _##
007: _## Licensed under the Apache License, Version 2.0 (the "License");
008: _## you may not use this file except in compliance with the License.
009: _## You may obtain a copy of the License at
010: _##
011: _## http://www.apache.org/licenses/LICENSE-2.0
012: _##
013: _## Unless required by applicable law or agreed to in writing, software
014: _## distributed under the License is distributed on an "AS IS" BASIS,
015: _## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: _## See the License for the specific language governing permissions and
017: _## limitations under the License.
018: _##
019: _##########################################################################*/
020:
021: package org.snmp4j.smi;
022:
023: import java.io.*;
024: import java.util.*;
025: import org.snmp4j.log.*;
026: import org.snmp4j.asn1.*;
027: import org.snmp4j.SNMP4JSettings; // For JavaDoc:
028: import org.snmp4j.PDU;
029:
030: /**
031: * The <code>Variable</code> abstract class is the base class for all SNMP
032: * variables.
033: * <p>
034: * All derived classes need to be registered with their SMI BER type in the
035: * <code>smisyntaxes.properties</code>so that the
036: * {@link #createFromBER(BERInputStream inputStream)} method
037: * is able to decode a variable from a BER encoded stream.
038: * <p>
039: * To register additional syntaxes, set the system property
040: * {@link #SMISYNTAXES_PROPERTIES} before decoding a Variable for the first
041: * time. The path of the property file must be accessible from the classpath
042: * and it has to be specified relative to the <code>Variable</code> class.
043: *
044: * @author Jochen Katz & Frank Fock
045: * @version 1.8
046: * @since 1.8
047: */
048: public abstract class AbstractVariable implements Variable,
049: Serializable {
050:
051: private static final long serialVersionUID = 1395840752909725320L;
052:
053: public static final String SMISYNTAXES_PROPERTIES = "org.snmp4j.smisyntaxes";
054: private static final String SMISYNTAXES_PROPERTIES_DEFAULT = "smisyntaxes.properties";
055:
056: private static final Object[][] SYNTAX_NAME_MAPPING = {
057: { "Integer32", new Integer(BER.INTEGER32) },
058: { "BIT STRING", new Integer(BER.BITSTRING) },
059: { "OCTET STRING", new Integer(BER.OCTETSTRING) },
060: { "OBJECT IDENTIFIER", new Integer(BER.OID) },
061: { "TimeTicks", new Integer(BER.TIMETICKS) },
062: { "Counter", new Integer(BER.COUNTER) },
063: { "Counter64", new Integer(BER.COUNTER64) },
064: { "EndOfMibView", new Integer(BER.ENDOFMIBVIEW) },
065: { "Gauge", new Integer(BER.GAUGE32) },
066: { "IpAddress", new Integer(BER.IPADDRESS) },
067: { "NoSuchInstance", new Integer(BER.NOSUCHINSTANCE) },
068: { "NoSuchObject", new Integer(BER.NOSUCHOBJECT) },
069: { "Null", new Integer(BER.NULL) },
070: { "Opaque", new Integer(BER.OPAQUE) } };
071:
072: private static Hashtable registeredSyntaxes = null;
073:
074: private static final LogAdapter logger = LogFactory
075: .getLogger(AbstractVariable.class);
076:
077: /**
078: * The abstract <code>Variable</code> class serves as the base class for all
079: * specific SNMP syntax types.
080: */
081: public AbstractVariable() {
082: }
083:
084: public abstract boolean equals(Object o);
085:
086: public abstract int compareTo(Object o);
087:
088: public abstract int hashCode();
089:
090: /**
091: * Returns the length of this <code>Variable</code> in bytes when encoded
092: * according to the Basic Encoding Rules (BER).
093: * @return
094: * the BER encoded length of this variable.
095: */
096: public abstract int getBERLength();
097:
098: public int getBERPayloadLength() {
099: return getBERLength();
100: }
101:
102: /**
103: * Decodes a <code>Variable</code> from an <code>InputStream</code>.
104: * @param inputStream
105: * an <code>InputStream</code> containing a BER encoded byte stream.
106: * @throws IOException
107: * if the stream could not be decoded by using BER rules.
108: */
109: public abstract void decodeBER(BERInputStream inputStream)
110: throws IOException;
111:
112: /**
113: * Encodes a <code>Variable</code> to an <code>OutputStream</code>.
114: * @param outputStream
115: * an <code>OutputStream</code>.
116: * @throws IOException
117: * if an error occurs while writing to the stream.
118: */
119: public abstract void encodeBER(OutputStream outputStream)
120: throws IOException;
121:
122: /**
123: * Creates a <code>Variable</code> from a BER encoded <code>InputStream</code>.
124: * Subclasses of <code>Variable</code> are registered using the properties file
125: * <code>smisyntaxes.properties</code> in this package. The properties are
126: * read when this method is called first.
127: *
128: * @param inputStream
129: * an <code>BERInputStream</code> containing a BER encoded byte stream.
130: * @return
131: * an instance of a subclass of <code>Variable</code>.
132: * @throws IOException
133: */
134: public static Variable createFromBER(BERInputStream inputStream)
135: throws IOException {
136: if (!inputStream.markSupported()) {
137: throw new IOException(
138: "InputStream for decoding a Variable must support marks");
139: }
140: if (SNMP4JSettings.isExtensibilityEnabled()
141: && (registeredSyntaxes == null)) {
142: registerSyntaxes();
143: }
144: inputStream.mark(2);
145: int type = inputStream.read();
146: Variable variable;
147: if (SNMP4JSettings.isExtensibilityEnabled()) {
148: Class c = (Class) registeredSyntaxes.get(new Integer(type));
149: if (c == null) {
150: throw new IOException(
151: "Encountered unsupported variable syntax: "
152: + type);
153: }
154: try {
155: variable = (Variable) c.newInstance();
156: } catch (IllegalAccessException aex) {
157: throw new IOException(
158: "Could not access variable syntax class for: "
159: + c.getName());
160: } catch (InstantiationException iex) {
161: throw new IOException(
162: "Could not instantiate variable syntax class for: "
163: + c.getName());
164: }
165: } else {
166: variable = createVariable(type);
167: }
168: inputStream.reset();
169: variable.decodeBER(inputStream);
170: return variable;
171: }
172:
173: private static Variable createVariable(int smiSyntax) {
174: switch (smiSyntax) {
175: case SMIConstants.SYNTAX_OBJECT_IDENTIFIER: {
176: return new OID();
177: }
178: case SMIConstants.SYNTAX_INTEGER: {
179: return new Integer32();
180: }
181: case SMIConstants.SYNTAX_OCTET_STRING: {
182: return new OctetString();
183: }
184: case SMIConstants.SYNTAX_GAUGE32: {
185: return new Gauge32();
186: }
187: case SMIConstants.SYNTAX_COUNTER32: {
188: return new Counter32();
189: }
190: case SMIConstants.SYNTAX_COUNTER64: {
191: return new Counter64();
192: }
193: case SMIConstants.SYNTAX_NULL: {
194: return new Null();
195: }
196: case SMIConstants.SYNTAX_TIMETICKS: {
197: return new TimeTicks();
198: }
199: case SMIConstants.EXCEPTION_END_OF_MIB_VIEW: {
200: return new Null(SMIConstants.EXCEPTION_END_OF_MIB_VIEW);
201: }
202: case SMIConstants.EXCEPTION_NO_SUCH_INSTANCE: {
203: return new Null(SMIConstants.EXCEPTION_NO_SUCH_INSTANCE);
204: }
205: case SMIConstants.EXCEPTION_NO_SUCH_OBJECT: {
206: return new Null(SMIConstants.EXCEPTION_NO_SUCH_OBJECT);
207: }
208: case SMIConstants.SYNTAX_OPAQUE: {
209: return new Opaque();
210: }
211: case SMIConstants.SYNTAX_IPADDRESS: {
212: return new IpAddress();
213: }
214: default: {
215: throw new IllegalArgumentException(
216: "Unsupported variable syntax: " + smiSyntax);
217: }
218: }
219: }
220:
221: /**
222: * Creates a <code>Variable</code> from the supplied SMI syntax identifier.
223: * Subclasses of <code>Variable</code> are registered using the properties
224: * file <code>smisyntaxes.properties</code> in this package. The properties
225: * are read when this method is called for the first time.
226: *
227: * @param smiSyntax
228: * an SMI syntax identifier of the registered types, which is typically
229: * defined by {@link SMIConstants}.
230: * @return
231: * a <code>Variable</code> variable instance of the supplied SMI syntax.
232: */
233: public static Variable createFromSyntax(int smiSyntax) {
234: if (!SNMP4JSettings.isExtensibilityEnabled()) {
235: return createVariable(smiSyntax);
236: }
237: if (registeredSyntaxes == null) {
238: registerSyntaxes();
239: }
240: Class c = (Class) registeredSyntaxes
241: .get(new Integer(smiSyntax));
242: if (c == null) {
243: throw new IllegalArgumentException(
244: "Unsupported variable syntax: " + smiSyntax);
245: }
246: try {
247: Variable variable = (Variable) c.newInstance();
248: return variable;
249: } catch (IllegalAccessException aex) {
250: throw new RuntimeException(
251: "Could not access variable syntax class for: "
252: + c.getName());
253: } catch (InstantiationException iex) {
254: throw new RuntimeException(
255: "Could not instantiate variable syntax class for: "
256: + c.getName());
257: }
258: }
259:
260: /**
261: * Register SNMP syntax classes from a properties file. The registered
262: * syntaxes are used by the {@link createFromBER} method to type-safe
263: * instantiate sub-classes from <code>Variable</code> from an BER encoded
264: * <code>InputStream</code>.
265: */
266: private synchronized static void registerSyntaxes() {
267: String syntaxes = System.getProperty(SMISYNTAXES_PROPERTIES,
268: SMISYNTAXES_PROPERTIES_DEFAULT);
269: InputStream is = Variable.class.getResourceAsStream(syntaxes);
270: if (is == null) {
271: throw new InternalError("Could not read '" + syntaxes
272: + "' from classpath!");
273: }
274: Properties props = new Properties();
275: try {
276: props.load(is);
277: Hashtable regSyntaxes = new Hashtable(props.size());
278: for (Enumeration en = props.propertyNames(); en
279: .hasMoreElements();) {
280: String id = (String) en.nextElement();
281: String className = props.getProperty(id);
282: try {
283: Class c = Class.forName(className);
284: regSyntaxes.put(new Integer(id), c);
285: } catch (ClassNotFoundException cnfe) {
286: logger.error(cnfe);
287: }
288: }
289: // atomic syntax registration
290: registeredSyntaxes = regSyntaxes;
291: } catch (IOException iox) {
292: String txt = "Could not read '" + syntaxes + "': "
293: + iox.getMessage();
294: logger.error(txt);
295: throw new InternalError(txt);
296: } finally {
297: try {
298: is.close();
299: } catch (IOException ex) {
300: logger.warn(ex);
301: }
302: }
303: }
304:
305: /**
306: * Gets the ASN.1 syntax identifier value of this SNMP variable.
307: * @return
308: * an integer value < 128 for regular SMI objects and a value >= 128
309: * for exception values like noSuchObject, noSuchInstance, and
310: * endOfMibView.
311: */
312: public abstract int getSyntax();
313:
314: /**
315: * Checks whether this variable represents an exception like
316: * noSuchObject, noSuchInstance, and endOfMibView.
317: * @return
318: * <code>true</code> if the syntax of this variable is an instance of
319: * <code>Null</code> and its syntax equals one of the following:
320: * <UL>
321: * <LI>{@link SMIConstants#EXCEPTION_NO_SUCH_OBJECT}</LI>
322: * <LI>{@link SMIConstants#EXCEPTION_NO_SUCH_INSTANCE}</LI>
323: * <LI>{@link SMIConstants#EXCEPTION_END_OF_MIB_VIEW}</LI>
324: * </UL>
325: */
326: public boolean isException() {
327: return Null.isExceptionSyntax(getSyntax());
328: }
329:
330: /**
331: * Gets a string representation of the variable.
332: * @return
333: * a string representation of the variable's value.
334: */
335: public abstract String toString();
336:
337: /**
338: * Returns an integer representation of this variable if
339: * such a representation exists.
340: * @return
341: * an integer value (if the native representation of this variable
342: * would be a long, then the long value will be casted to int).
343: * @throws UnsupportedOperationException if an integer representation
344: * does not exists for this Variable.
345: * @since 1.7
346: */
347: public abstract int toInt();
348:
349: /**
350: * Returns a long representation of this variable if
351: * such a representation exists.
352: * @return
353: * a long value.
354: * @throws UnsupportedOperationException if a long representation
355: * does not exists for this Variable.
356: * @since 1.7
357: */
358: public abstract long toLong();
359:
360: public abstract Object clone();
361:
362: /**
363: * Gets a textual description of the supplied syntax type.
364: * @param syntax
365: * the BER code of the syntax.
366: * @return
367: * a textual description like 'Integer32' for <code>syntax</code>
368: * as used in the Structure of Management Information (SMI) modules.
369: * '?' is returned if the supplied syntax is unknown.
370: */
371: public static String getSyntaxString(int syntax) {
372: switch (syntax) {
373: case BER.INTEGER:
374: return "Integer32";
375: case BER.BITSTRING:
376: return "BIT STRING";
377: case BER.OCTETSTRING:
378: return "OCTET STRING";
379: case BER.OID:
380: return "OBJECT IDENTIFIER";
381: case BER.TIMETICKS:
382: return "TimeTicks";
383: case BER.COUNTER:
384: return "Counter";
385: case BER.COUNTER64:
386: return "Counter64";
387: case BER.ENDOFMIBVIEW:
388: return "EndOfMibView";
389: case BER.GAUGE32:
390: return "Gauge";
391: case BER.IPADDRESS:
392: return "IpAddress";
393: case BER.NOSUCHINSTANCE:
394: return "NoSuchInstance";
395: case BER.NOSUCHOBJECT:
396: return "NoSuchObject";
397: case BER.NULL:
398: return "Null";
399: case BER.OPAQUE:
400: return "Opaque";
401: }
402: return "?";
403: }
404:
405: /**
406: * Gets a textual description of this Variable.
407: * @return
408: * a textual description like 'Integer32'
409: * as used in the Structure of Management Information (SMI) modules.
410: * '?' is returned if the syntax is unknown.
411: * @since 1.7
412: */
413: public final String getSyntaxString() {
414: return getSyntaxString(getSyntax());
415: }
416:
417: /**
418: * Returns the BER syntax ID for the supplied syntax string (as returned
419: * by {@link #getSyntaxString(int)}).
420: * @param syntaxString
421: * the textual representation of the syntax.
422: * @return
423: * the corresponding BER ID.
424: * @since 1.6
425: */
426: public static int getSyntaxFromString(String syntaxString) {
427: for (int i = 0; i < SYNTAX_NAME_MAPPING.length; i++) {
428: if (SYNTAX_NAME_MAPPING[i][0].equals(syntaxString)) {
429: return ((Integer) SYNTAX_NAME_MAPPING[i][1]).intValue();
430: }
431: }
432: return BER.NULL;
433: }
434:
435: /**
436: * Converts the value of this <code>Variable</code> to a (sub-)index
437: * value.
438: * @param impliedLength
439: * specifies if the sub-index has an implied length. This parameter applies
440: * to variable length variables only (e.g. {@link OctetString} and
441: * {@link OID}). For other variables it has no effect.
442: * @return
443: * an OID that represents this value as an (sub-)index.
444: * @throws UnsupportedOperationException
445: * if this variable cannot be used in an index.
446: * @since 1.7
447: */
448: public abstract OID toSubIndex(boolean impliedLength);
449:
450: /**
451: * Sets the value of this <code>Variable</code> from the supplied (sub-)index.
452: * @param subIndex
453: * the sub-index OID.
454: * @param impliedLength
455: * specifies if the sub-index has an implied length. This parameter applies
456: * to variable length variables only (e.g. {@link OctetString} and
457: * {@link OID}). For other variables it has no effect.
458: * @throws UnsupportedOperationException
459: * if this variable cannot be used in an index.
460: * @since 1.7
461: */
462: public abstract void fromSubIndex(OID subIndex,
463: boolean impliedLength);
464:
465: /**
466: * Indicates whether this variable is dynamic, which means that it might
467: * change its value while it is being (BER) serialized. If a variable is
468: * dynamic, it will be cloned on-the-fly when it is added to a {@link PDU}
469: * with {@link PDU#add(VariableBinding)}. By cloning the value, it is
470: * ensured that there are no inconsistent changes between determining the
471: * length with {@link #getBERLength()} for encoding enclosing SEQUENCES and
472: * the actual encoding of the Variable itself with {@link #encodeBER}.
473: *
474: * @return
475: * <code>false</code> by default. Derived classes may override this
476: * if implementing dynamic {@link Variable} instances.
477: * @since 1.8
478: */
479: public boolean isDynamic() {
480: return false;
481: }
482:
483: }
|