Contents
The coding services provide support for the various ASN.1 encoding rules. The com.oss.asn1 package contains a single subclass of Coder that concretely implements each encoding rule.
The following sets of ASN.1 encoding rules are supported:
ASN.1 Encoding Rule | Concrete Implementation | SOED runtime | TOED runtime |
---|---|---|---|
BER - Basic encoding rules | BERCoder | yes | yes |
CER - Canonical encoding rules | CERCoder | yes | no |
DER - Distinguished encoding rules | DERCoder | yes | yes |
JSON - JSON encoding rules | JSONCoder | yes* | yes |
OER - Octet encoding rules | OERCoder | yes | yes |
COER - Canonical Octet encoding rules | COERCoder | yes | yes |
PER - Packed (Aligned, Canonical Aligned, Unaligned, or Canonical Unaligned) encoding rules | PERAlignedCoder PERUnalignedCoder CPERAlignedCoder CPERUnalignedCoder |
yes | yes |
XER - XML encoding rules | XERCoder | yes | yes |
CXER - Canonical XML encoding rules | CXERCoder | yes | yes |
E-XER - Extended XML encoding rules | EXERCoder | yes | no |
*The JSON encoding rules are supported in the SOED runtime in version 7.0 and later.
The API for accessing the coding services is defined in the com.oss.asn1.Coder class. The Coder class defines methods for encoding, decoding, and setting the various runtime options.
public void encode (AbstractData pdu, OutputStream sink) throws EncodeNotSupportedException, EncodeFailedException;
public void encode (AbstractData pdu, ByteBuffer sink) throws EncodeNotSupportedException, EncodeFailedException;
The data contained within pdu is encoded according to the ASN.1 encoding rules implemented by the Coder and is written to sink.
When the encode operation fails, an EncodeFailedException is thrown.
The PER encoding rules specify that default values must not be encoded. However, by specifying a value that is the default one, you instruct the PER encoder to ignore the rules and encode the value. By omitting a value, when it corresponds to the default, you instruct the PER encoder to not encode the value. In either case, the PER-encoded message is properly structured.
public AbstractData decode (InputStream source, AbstractData pdu) throws DecodeNotSupportedException, DecodeFailedException;
public AbstractData decode (ByteBuffer source, AbstractData pdu) throws DecodeNotSupportedException, DecodeFailedException;
When the pdu parameter does not support the decode operation, the DecodeNotSupportedException is thrown.
The data contained within source is decoded according to the ASN.1 encoding rules that are implemented by the Coder and are used to populate pdu.
When the decode operation fails, a DecodeFailedException is thrown.
To save the size/offset information of the encoded PDU components when decoding, the following method can be used (available in the SOED runtime only).
public AbstractDataWithPositions decodeWithPositions( InputStream source, AbstractData sink) throws DecodeFailedException, DecodeNotSuportedException;
The size/offset information specifies the bit-field position of the encoded components within the PDU encoding.
The decodeWithPositions() method is similar to that of the decode() method, however its return type is AbstractDataWithPositions. The AbstractDataWithPositions class is a container that wraps an AbstractData decoded value object and an AbstractDataPositions object and that represents the offset/size information of various components of the decoded value.
The AbstractDataWithPositions class provides getter methods for the decoded value, the positions of its components in the encoded data, and the entire PDU size in bits.
public final void decodePartial(InputStream source, ContentHandler handler) throws DecodeNotSupportedException, DecodeFailedException
public final void decodePartial(ByteBuffer source, ContentHandler handler) throws DecodeNotSupportedException, DecodeFailedException
During partial decoding, when the decoder encounters a field to which the OSS.DataCallback or OSS.InfoCallback compiler directive is applied, it invokes the specified callback methods (provided by the user).
When the -helperAPI pdudecoder command line option is specified, the ASN.1/Java compiler generates a PDU class and PDU_ID enum class as inner classes of an ASN.1/Java project class. In application code, the new classes can be referenced as <project>.PDU and <project>.PDU_ID, where <project> is the name of a project class.
The PDU_ID enum class enumerates all ASN.1 PDU types that are defined in the current project. The enum class instance names are the same as the PDU type names.
public enum PDU_ID { PDU_1, PDU_2, PDU_3, ... }
The PDU class serves as a container for a decoded PDU type value and an instance of the PDU_ID enum that identifies the decoded value's type.
The PDU class instances are created by decoding. The PDU class defines public static PDU decode(input, coder) methods for all suitable encoding rule options specified on the compiler command line with the -helperAPI pdudecoder option. For example, when the -ber -helperAPI pdudecoder options are used, the PDU class contains methods to decode a PDU from the BER input:
public static PDU decode(InputStream source, BERCoder coder) throws DecodeFailedException; public static PDU decode(java.nio.ByteBuffer source, BERCoder coder). throws DecodeFailedException;
With proper source and coder parameters, calling the above decode() methods will create a new instance of the PDU class that is populated with the decoded value of one of the project's PDU types and the PDU_ID enum instance that identifies the type.
For each of the project's PDU types there is a compiler-generated getter method to access the decoded PDU values inside the container. The getter method's signature uses the following pattern:
public <PDU_type_name> get<PDU_type_name>();
Here is an incomplete code fragment that illustrates usage of the PDU class:
import com.oss.asn1.*; import java.io.*; import project.Project; import project.Project.PDU; import project.Project.PDU_ID.*; InputStream source; // Assuming project is initialized and // proper java.io.InputStream source is prepared ... BERCoder coder = Project.getBERCoder(); Project.PDU pdu; try { pdu = Project.PDU.decode(source, coder) // Assuming existence of process(Type) methods for each // PDU type switch (pdu.getTypeID()) { case PDU_1: process(pdu.getPDU_1()); case PDU_2: process(pdu.getPDU_2()); case PDU_3: process(pdu.getPDU_3()); ... } } catch (DecodeFailedException ex) { System.out.println("Failed to decode PDU: " + ex); }
The -helperAPI pdudecoder option is available in version 7.0 and later.
You can enable or disable debugging (tracing) output during encode and decode operations.
public void enableEncoderDebugging(); public void disableEncoderDebugging(); public void enableDecoderDebugging(); public void disableDecoderDebugging();
Turning on the debugging feature does not automatically enable printing of exception stack traces. To enable this, you need to define the tracer.exceptiontrace system property. For example, if you are running an application with Sun JVM, use the following command-line argument:
-Dtracer.exceptiontrace=on
You can enable or disable constraint enforcement during encode and decode operations.
public void enableEncoderConstraints() public void disableEncoderConstraints() public void enableDecoderConstraints() public void disableDecoderConstraints()
When PER is used, encoding can fail due to a constraint violation even when runtime constraint checking is disabled.
You can specify whether the BERCoder should use Definite Length Encoding or Indefinite Length Encoding during encode operations. These two options are mutually exclusive, and are only relevant when using the Basic Encoding Rules with the SOED runtime. The TOED runtime BER encoder currently supports only definite-length encoding.
public void useDefiniteLengthEncoding() public void useIndefiniteLengthEncoding()(SOED only) public void usingDefiniteLengthEncoding()
When invoked on a Coder that is not an instance of BERCoder, the methods are silently ignored.
You can check whether it is possible to automatically detect the type of the incoming PDU from the bits on the wire.
public boolean isPDUDetectionAvailable()
To make sure you can pass a null sink argument, call this API before coder.decode(). This is useful when you need to decode a PDU without prior knowledge of its type. For example:
Coder coder = MyProject.getBERCoder(); AbstractData pdu = null; if (coder.isPDUDetectionAvailable()) pdu = coder.decode(encoding, null); else System.out.println("Automatic detection of incoming PDU is not available.");
This method returns true if the coder can automatically detect the type of incoming PDU from the bits on the wire. Currently, this method returns true when:
To check if a particular type is a PDU, use the isPDU() method of AbstractData.
You can prevent deferred decoding of components marked with the DeferDecoding directive. The coder ignores the directive and fully decodes the components.
You can allow deferred decoding of components marked with the DeferDecoding directive. Deferred decoding is enabled by default, so you can use this method if the Coder class was disabled.
public void disableDeferredDecoding() public void enableDeferredDecoding()
Note that these methods have no effect on PERAlignedCoder(), PERUnalignedCoder(), CPERAlignedCoder(), CPERUnalignedCoder(), XERCoder(), CXERCoder, or EXERCoder because deferred decoding of a component is not supported for the Packed Encoding Rules or the XML Encoding Rules.
You can enable or disable automatic encoding or decoding of OpenTypes. When automatic encoding is enabled, the coder automatically encodes any instance of an OpenType with a component relation constraint or a type constraint applied.
public void enableAutomaticEncoding() public void disableAutomaticEncoding() public void enableAutomaticDecoding() public void disableAutomaticDecoding() public void enableContainedValueEncoding() public void enableContainedValueDecoding() public void disableContainedValueEncoding() public void disableContainedValueDecoding()
Use the four latter methods when your ASN.1 definition contains Contents Constraints. Contained values carried within a BIT STRING or OCTET STRING with a contents constraint can be automatically encoded and decoded providing the encoding rules specified by the ENCODED BY clause are supported by the OSS runtime library and automatic encoding and decoding is enabled.
The enableContainedValueDecoding() has an overloaded version.
public void enableContainedValueDecoding(boolean preserveEncoding)
It enables automatic decoding of contained values and, when the preserveEncoding parameter is set to true, instructs the decoder to keep the original bits/octets of a BIT STRING or OCTET STRING and present them with the decoded contained value.
When the automatic decoding of contained values is enabled with the enableContainedValueDecoding() method without an argument, the original bits/octets are discarded and only the decoded contained value is presented.
This feature is supported in the SOED runtime only. For compatibility, the TOED runtime defines a similar method but currently it has no effect.
Unless you explicitly enable automatic encoding/decoding, only manual encoding/decoding is possible.
In order for the decoder to figure out what type to expect in an open type slot, the ASN.1 syntax must specify a component relation constraint.
To help you better understand the benefit of automatic encoding and decoding, see the Java FAQ section of the OSS website (requires a customer ID and password). The example demonstrates automatic decoding when the PDU contains numerous open types at various nesting levels. It first seeks to automatically decode open types that are nested in the complex NBAP message and then goes on to demonstrate how much extra code you would need instead to manually decode these nested OpenTypes.
public void enableRelaxedDecoding() public void disableRelaxedDecoding()
Use the enableRelaxedDecoding method when your application needs to process third party generated encodings that are not compliant to the ASN.1 standard. When the deviation is not severe and the decoder can parse non-standard encodings, you can instruct the decoder to ignore these errors and continue decoding. This method causes the decoder to silently ignore certain inconsistencies in the input encoding.
By default, relaxed decoding mode is disabled.
Here is a list of decoding errors that are ignored when the relaxed decoding mode is set:
Errors in BER encoding:
Errors in PER encoding:
You can enable or disable the ability to retrieve partially decoded PDU data in the exception object.
public void enableRichDecoderExceptions(); public void disableRichDecoderExceptions();
enableStrictDecoding() disableStrictDecoding()
This feature is available only with the PER Aligned version of the Coder.
When the enableStrictDecoding() method is used, during decoding the application checks the padding bits while it tries to skip them. If one or more padding bits are non-zero, the application issues an exception message: "0263E: Non-zero padding bit was detected".
When this method is used while decoding data, the application will catch any DecodeFailedExceptions.
By default, strict decoding mode is disabled. When this feature is disabled, the application silently skips the padding bits without detecting issuing any exception. This feature is not supported for the TOED runtime.
You can control the whitespace generated by the XER, E-XER and JSON encoders (available for SOED and TOED).
public void enableCompact(); public void disableCompact();
The enableCompact() method disables whitespace generation. When this method is called, compact mode is enabled: the encoding is written as a single line of text with no whitespace.
The disableCompact() method adds whitespace to improve readability. When this method is called, newlines are inserted and indentation is applied.
By default, compact mode is disabled. The XER, E-XER, and JSON encoders generate whitespace to improve readability. Text encodings are generated on multiple lines with a two space indent by default. This indent is increased by two spaces for each indentation level.
You can also control the whitespace generated by the XER and JSON encoders using the following methods:
public void setIndentWidth(int width); public int getIndentWidth();
The setIndentWidth(int width) method controls indentation. The width parameter specifies the number of spaces added to the existing indent for the next indentation level.
{ "name":"Casey", "team":"Mudville Nine", "age":32, "position":"left field", "handedness":"ambidextrous", "batting-average":{ "base":2, "value":0.25 } }
The default indentation width is 2. When the indentation width is set to 10 (by calling the coder.setIndentWidth(10) method), the following output is generated:
{ "name":"Casey", "team":"Mudville Nine", "age":32, "position":"left field", "handedness":"ambidextrous", "batting-average":{ "base":2, "value":0.25 } }
The getIndentWidth() method returns the current indentation width setting.
The com.oss.asn1.Coder class defines eight methods to control the destination and the format of the diagnostic output. This class is used in conjunction with the com.oss.util.ASN1PrintWriter class.
These methods get or set the destination for the diagnostic output generated by the encoder and decoder.
public void setEncoderDebugOut (PrintWriter out); public PrintWriter getEncoderDebugOut (); public void setDecoderDebugOut (PrintWriter out); public PrintWriter getDecoderDebugOut ();
If your application is multi-threaded and the debug lines are printed from multiple threads, the whole diagnostic output on System.out (the default destination for the diagnostic output) will look confusing because the lines printed by one thread will intermix in random order with the lines printed by another thread. To make the output deterministic, you can associate a StringWriter with each thread and capture its diagnostic output in the string buffer. Then print the accumulated debug lines immediately after the thread completes the encode or decode operation.
Because the ASN1PrintWriter inherits from the java.util.PrintWriter, you can use it to print trace output as well. You can create one instance of ASN1PrintWriter and use it to capture the diagnostic output from the application (including the diagnostic trace output and other messages that the application code prints explicitly).
These methods get or set various parameters that affect the format of the diagnostic output. However, instead of providing the individual method to get or set each parameter (truncation limit, indent width) the parameters are passed or retrieved as java.util.Properties.
public Properties getEncoderDebugProperties (); public void setEncoderDebugPropeties (Properties params); public Properties getDecoderDebugProperties (); public void setDecoderDebugProperties (Properties params);
Currently, the following properties are supported by the runtime:
oss.tracer.TruncationLimit=number
Specifies how the lengthy content trace data is truncated. When the value is set to "0", truncation is disabled.
oss.tracer.IndentWidth=number
For better readability, certain lines in the diagnostic output are indented to the right. For example, in BER diagnostic output indentation is used to reflect the nested TLV structure. This property specifies the number of print positions in one indentation level.
oss.tracer.ExceptionTrace=on|off
Setting this property to "on" causes the exception stack trace to be included in the diagnostic output.
Along with applying the set of debugging properties to the individual instance of com.oss.asn1.Coder, you can also set default values using the -D switch on the JVM command-line:
java -Doss.tracer.TruncationLimit=0 -Doss.tracer.ExceptionTrace=on MyApp
In this case the diagnostic output produced by every encode or decode operation will never truncate the content trace and will include the exception stack trace in the output by default.
The first example constructs the Properties object and applies it to a particular instance of com.oss.asn1.Coder. The encoder is instructed that the lengthy content trace is never truncated and that the diagnostic output should include the exception stack trace if any exception occurs during the encode operation. Instead of printing to System.out, the whole diagnostic output is captured in the string buffer.
/* Construct the settings inline */ StringWriter stringBuffer = new StringWriter(); ASN1PrintWriter debugOut = new ASN1PrintWriter(stringBuffer); Properties params = new Properties(); params.setProperty("oss.tracer.TruncationLimit", "0"); params.setProperty("oss.tracer.ExceptionTrace", "on"); Coder coder = MyProject.getBERCoder(); coder.setDebugOut(debugOut); coder.setEncoderDebugProperties(params);
Alternatively, the diagnostic output settings for decoding can be loaded from the disk file:
/* Read the settings from the disk file */ Properties params = new Properties(); params.load(new FileInputStream("debug.settings")); coder.setDecoderDebugProperties(params);
The sample contents of the debug.settings file is as follows:
oss.trace.TruncationLimit=40 oss.trace.IndentWidth=1 oss.trace.ExceptionTrace=on
In this case, the settings are loaded from the debug.settings disk file. The decoder is instructed to truncate the content trace that is longer than 40 characters. The indent width setting is decreased to 1, which makes the output more compact. And the diagnostic output is again set to include the stack trace of an exception that could occur during the decode operation.
The encoder and decoder can be configured to automatically invoke constraint checking on decoded data after it was decoded or before encoding it. Currently this feature is available only for the SOED runtime.
When you disable automatic constraint checking by the encoder/decoder you can still invoke the constraint checker directly. To do so, use the isValid() method of the AbstractData class (defined below) or the validate() method of the Coder class.
public boolean isValid() throws ValidateFailedException;
The method returns true if the instance of the AbstractData subclass that invoked it satisfies its ASN.1 subtype constraints. If there is a constraint violation, the ValidateFailedException is thrown. You can analyze the message text from the ValidateFailedException to determine which field within the data value caused the constraint violation.
We recommend that you use the isValid() method when you need to perform constraint checking directly and not using the automatic constraint checking feature of the encoder/decoder.
The Coder class also provides a method for constraint checking:
public int validate(AbstractData data) throws ValidateNotSupportedException, ValidateFailedException;
The data parameter is an instance of an ASN.1 compiler for Java generated class.
The validate() method returns 0 if the data value satisfies its ASN.1 type constraints. Otherwise, the return value is non-zero.
We do not recommend using the validate() method. It is provided only for compatibility purposes with applications built with the obsolete JNI version of the OSS ASN.1 Tools for Java. It is marked as deprecated in the current API.
A coding service abstraction layer is generated into the Project class. If your ASN.1 specification requires a single encoding rule, use the following factory method to create instances of the default Coder implementation (you will not need to modify your code if the default encoding rules for your application change):
public Coder getDefaultCoder()
When you compile without any encoding rule option, getDefaultCoder() returns an instance of BERCoder. When you compile with a single encoding rule option such as, for example, -per, the getDefaultCoder() method returns an instance of the Coder class for that encoding rule. When you compile with several encoding rule options, for example, -per and -xer, the getDefaultCoder() returns a Coder instance according to the following order: -ber, -der, -per, -cper, -uper, -cuper, -cer, -xer, -cxer, -exer. That is, for -per -xer, the method returns an instance of the PERAlignedCoder class.
Methods specific to the individual ASN.1 encoding rules are also generated into the Project class. Encoding rule-specific methods are generated for support of the encoding rules by the Java command line.
public BERCoder getBERCoder() public BERCoder getCERCoder() public DERCoder getDERCoder() public JSONCoder getJSONCoder() public OERCoder getOERCoder() public COERCoder getCOERCoder() public PERAlignedCoder getPERAlignedCoder() public PERUnalignedCoder getPERUnalignedCoder() public CPERAlignedCoder getCPERAlignedCoder() public CPERUnalignedCoder getCPERUnalignedCoder() public XERCoder getXERCoder() public CXERCoder getCXERCoder() public EXERCoder getEXERCoder()
The encoding rule-specific methods are useful when you must employ more than one encoding rule in an application.
Coder coder = MyProject.getDefaultCoder(); // encode try { MyPDU outPDU = new MyPDU(...); FileOutputStream mySink = new FileOutputStream("myPDU.enc"); coder.enableEncoderDebugging(); //print debugging output to console coder.disableEncoderConstaints(); //disable constraint checking coder.encode(outPDU, mySink); } catch(EncodeNotSupportedException e) { // Encode is not supported } catch(EncodeFailedException e) { // the encode() operation failed } // decode try { FileInputStream mySource = new FileInputStream("myPDU.enc"); coder.disableDecoderDebugging(); // disable debugging output coder.enableDecoderConstaints(); // enable constraint checking MyPDU inPDU = (MyPDU)coder.decode(mySource, new MyPDU()); } catch(DecodeNotSupportedException e) { // Decode is not supported } catch(DecodeFailedException e) { // the decode() operation failed }
You can handle the allocation of external memory (for example, files) for encoding to or decoding from.
public StorageManager getStorageManager()
Gets the current setting of the StorageManager and returns an instance of the StorageManager to allocate the external storage.
public void setStorageManager(StorageManager storageManager)
You instruct the runtime to use your customized storage manager instead of the default one. Make sure that the object can be accessed by concurrent threads (is thread safe).
The storageManager parameter is the instance of the class that implements the StorageManager interface.
ValueInFile, Storage interface, huge values.
The following example illustrates a nested value.
Name ::= SEQUENCE { first UTF8String, last UTF8String } Info ::= SEQUENCE { name Name, age INTEGER, children SEQUENCE OF Name }
When a decoding error occurs during decoding of the last field of the second element of children and the BER coder is used, the following application code is present:
try { AbstractData value = coder.decode(source, new Info()); } catch (DecodeFailedException e) { AbstractData badValue = e.getDecodedValue(); if (badValue != null) { // Examine the data if (badValue.getName() != null) // examine 'name' of 'badValue' ... } ... }
badValue will contain a partially decoded PDU where name and age are fully decoded and children contains two elements. The first element is fully decoded and the second element has the last field equal to null.
Note that the runtime does not identify why a particular component is missing (or why the getter method returns null). This might happen when the component is past the location where the decoding error was thrown (and has not been decoded because of a decoding error) or when the component is optional and was omitted from the encoding.
A partially decoded PDU that is returned via the DecodeFailedException may contain values of primitive types: BOOLEAN, INTEGER, ENUMERATED, REAL, NULL, BIT STRING, OCTET STRING, character strings, OBJECT IDENTIFIER, GeneralizedTime and UTCTime, but will never contain partially decoded primitive values. For scalar types, a "partially decoded" value does not make sense. In other words, if the decoding error occurs in the middle of the string, the whole contents of the string is discarded (and is not inserted in the AbstractData that is aggregated in the decoder exception), even octets that were read from the input before the DecoderError was thrown.
Partial decoding is not supported for SET OF or SEQUENCE OF with the ValueInFile directive applied. If a decoding error occurs in the middle of a huge SET OF or SEQUENCE OF, the whole value is discarded and is not returned in the decoder exception. If you are interested in additions to what is decoded, contact Technical Support <support@oss.com>.
PER encoding pads out the PDU to the next whole octet. For example, if the encoded data is 110 bits (13 octets and 6 bits), two zero bits are appended to create a full octet boundary. The com.oss.asn1.getNumberOfPaddingBits method is useful when you need to know how much of the PDU is actual data (for 3GPP layer 2 applications, for example).
public int getNumberOfPaddingBits()
This method returns the following:
The PER decoder performance can be enhanced with two performance tuning parameters that can be set by the Java system properties. The parameters affect the internal buffering strategy used when decoding large byte arrays. The affected ASN.1 types are OCTET STRING, BIT STRING and open type.
While encoding, large amounts of content are split into a sequence of 64 KB fragments for PER, so the receiving party needs to reconstruct the original content by merging the fragments. PER does not provide a length determinant that would precede the fragmented content to specify its total length in bytes. It is not possible to allocate a buffer that could hold all the fragments, so a buffer reallocation scheme needs to be applied.
By default, the PER decoder allocates a new buffer that is expanded by a fragment size each time the decoder sees a new fragment. Content decoded so far is copied into the new buffer and merged with the octets of the new fragment. The old buffer is discarded. Due to the frequent reallocations and copying, this scheme can create a performance bottleneck when decoding large content. The PER decoder tunable parameters are used to avoid this possible performance issue. The number of buffer reallocations can be kept to a minimum by specifying:
The com.oss.per.in.bufsize property causes the PER decoder to allocate a byte array of a specified size and use it as the initial buffer for every decoding of fragmented content of the OCTET STRING, BIT STRING, or open type. Once allocated, the buffer is kept as part of the PER coder object and is reused. The syntax of the com.oss.per.in.bufsize property is "nnnn[K|k|M|m]", where "nnnn" is an integer that specifies the buffer size in bytes, KB or MB. The optional letter, "K" or "M", appended to the end of the integer value designates whether "nnnn" is in KB or MB.
The com.oss.per.in.incr property specifies a buffer size increment that is used when resizing a fragmented content decode buffer for the OCTET STRING, BIT STRING, or open type. The syntax of the com.oss.per.in.incr property specifies an absolute increment in bytes, KB, or MB, or a relative increment as a percent of the current buffer size. The syntax of the property is "nnnn[K|k|M|m|%]", which is the same as "bufsize", except that the ending symbol, "%", specifies the relative increment.
The above properties are queried by the PERCoder object at construction time by the System.getProperty() method. Properties can be specified by the "-D" command-line option or by calling the System.setProperty() method. With the latter choice, it is possible to create PERCoder objects that use different settings. The properties apply to both the Aligned and Unaligned PER coders.
System.setProperty("com.oss.per.in.bufsize", "4M"); Coder coder1 = <Project>.getPERAlignedCoder(); System.setProperty("com.oss.per.in.bufsize", "2M"); System.setProperty("com.oss.per.in.incr", "1M"); Coder coder2 = <Project>.getPERAlignedCoder();
The coder1 object is configured to use a 4 MB initial buffer for decoding the fragmented byte array content, while the coder2 object will start decoding with a 2 MB buffer and will allocate new buffers with a 1 MB size increment.
The XML Encoding Rules (XER) describe a simple way to encode ASN.1 abstract values using XML. These encoding rules are useful for an ASN.1 application that exchanges values with XML applications.
The Extended XML Encoding Rules (E-XER) support additional encoder options, and take account of encoding instructions that specify variations of the XER encodings to support specific styles of XML documents. There are many aspects of an XML representation of data (using XML attributes instead of child elements, or using whitespace-delimited lists, for example) whose use is a matter of style and XML designer choice. If a type defined in an ASN.1 specification is encoded by XER, then there is a single fixed style used for the XML representation, with no user control of stylistic features; E-XER allows you to apply this kind of control. This enables ASN.1 to provide a complete XML Schema language, such as DTDs or W3C XML Schema.
To learn more about the encoding instructions that can be used by E-XER, see the E-XER white paper.
A character-encodable type is an ASN.1 type containing values that can serve as XML attribute values. A character is an atomic unit of text as specified by ISO/IEC 10646. Legal characters are tab, carriage return, line feed, and the legal characters of Unicode and ISO/IEC 10646.
Certain E-XER encoding instructions can be assigned only to character-encodable ASN.1 types, for example, the ATTRIBUTE encoding instruction. Similarly, the LIST encoding instruction can be applied only to SET OF or SEQUENCE OF types if all type components are character-encodable, and the USE-UNION encoding instruction can be applied only to CHOICE types if all alternatives of CHOICE are character-encodable.
You must specify either a Single Value Constraint or a Permitted Alphabet Constraint for character-encodable types. When other constraints are present and the -exer compiler option is specified, the compiler issues an error. To ignore the error, use the -ignoreError compiler option.
It is recommended to define a separate character-encodable type, so that the character-encodable constraint definition is not repeated too many times in your ASN.1 syntax. For example, you can create another XMLCompatibleString type as shown below, and use this type in your ASN.1 syntax when a character-encodable type is needed.
XMLCompatibleString ::= UTF8String (FROM( {0, 0, 0, 9} | {0, 0, 0, 10} | {0, 0, 0, 13} | {0, 0, 0, 32} .. {0, 0, 215, 255} | {0, 0, 224, 0} .. {0, 0, 255, 253} | {0, 1, 0, 0} .. {0, 16, 255, 253})) Attr ::= XMLCompatibleString Color ::= XMLCompatibleString ("red" | "blue" | "green") S ::= [LIST] SEQUENCE OF Color C ::= [USE-UNION] CHOICE { c1 [TAG: 0] XMLCompatibleString, c2 [TAG: 1] XMLCompatibleString, c3 [TAG: 2] XMLCompatibleString, c4 [TAG: 3] XMLCompatibleString }
The following features are not supported when using E-XER:
If you are interested in any of these features for E-XER, contact Sales <info@oss.com>.
You can customize XML encodings specifying the UserClass directive with user-defined classes that implement the XERCoderInterface. The Coder class provides methods for enabling and disabling this feature.
public void enableXERCoderInterface(); public void disableXERCoderInterface();
When the feature is enabled, the encoder checks if the component being encoded implements XERCoderInterface. If it does, the encoder invokes the encode() method defined in the component rather than the default method defined in the XML coder class.
So user-defined XML can be put into the target output stream instead of the standard encoding. Note that because these user-defined encodings are non-standard they cannot be decoded by the standard decoder.
The JSON Encoding Rules (JER) describe a simple way to encode ASN.1 abstract values using JSON. These encoding rules are useful for an ASN.1 application that exchanges values with JSON applications.
You can customize JSON encodings specifying the UserClass directive with user-defined classes that implement the JSONCoderInterface. The Coder class provides methods for enabling and disabling this feature.
public void enableJSONCoderInterface(); public void disableJSONCoderInterface();
When the feature is enabled, the encoder checks if the component being encoded implements JSONCoderInterface. If it does, the encoder invokes the encode() method defined in the component rather than the default method defined in the JSON coder class.
So user-defined JSON can be put into the target output stream instead of the standard encoding. Note that because these user-defined encodings are non-standard they cannot be decoded by the standard decoder.
You can convert encoded data between different sets of encoding rules used to represent the data. For example, you can convert between BER and PER, PER and XER, etc. To do so, the data must be decoded using the input coder object and re-encoded using the output coder object. For example, to convert BER-encoded data into aligned PER-encoded data, you can use the decode() method of the BERCoder class, and then the encode() method of the PERAlignedCoder class. Alternatively, you can use the convert() method of the EncodingRuleConvertor class for a one-step conversion.
OSS provides five helper classes for encoding rule conversions:
Use the EncodingRuleConvertor class to convert between two encoding rules. This class extends the Coder class. Objects of the class are constructed from two Coder objects: decoder and encoder.
public class EncodingRuleConvertor extends Coder { // Public Constructors public EncodingRuleConvertor(Coder decoder, Coder encoder) // Public Instance Methods public void convert(InputStream source, OutputStream sink, AbstractData pdu) throws DecodeFailedException, EncodeFailedException public Coder getDecoder() public void setDecoder(Coder decoder) public Coder getEncoder() public void setEncoder(Coder encoder) }
Constructors | Description |
---|---|
EncodingRuleConvertor(Coder decoder, Coder encoder) |
Creates an EncodingRuleConvertor object and associates it with the two encoding rules. Parameters:
|
Instance Methods | Description |
---|---|
convert((InputStream source, OutputStream sink, AbstractData pdu) |
Performs the conversion. The method decodes the source stream containing the pdu value and re-encodes it into the sink stream. If pdu detection is available for the input coder object, then the pdu parameter can be set to null. Parameters:
|
Coder getDecoder() |
Returns the Coder used for decoding. Returns: input decoder |
setDecoder(Coder decoder) |
Sets the Coder used for decoding. Parameters: decoder - input decoder |
Coder getEncoder() |
Returns the Coder used for encoding. Returns: output decoder |
setEncoder(Coder encoder) |
Sets the Coder used for encoding. Parameters: encoder - input encoder |
The Binary2XMLConvertor and XML2BinaryConvertor classes are extensions of EncodingRuleConvertor. The classes provide type-safe constructors for configuring binary to XML and XML to binary conversions.
public class Binary2XMLConvertor extends EncodingRuleConvertor { // Public Constructors public Binary2XMLConvertor(BinaryCoder decoder, XMLCoder encoder) // Public Methods public void setDecoder(BinaryCoder decoder); public void setEncoder(XMLCoder encoder); } public class XML2BinaryConvertor extends EncodingRuleConvertor { // Public Constructors public XML2BinaryConvertor(XMLCoder decoder, BinaryCoder encoder) // Public Methods public void setDecoder(XMLCoder decoder); public void setEncoder(BinaryCoder encoder); }
The XMLCoder and BinaryCoder are interfaces used to distinguish XML and binary coder classes.
The coder objects that can be used as BinaryCoder typed parameters are: BERCoder, CERCoder, DERCoder, AlignedPERCoder, and UnalignedPERCoder.
The coder objects that can be used as XMLCoder typed parameters are: XERCoder, CXERCoder, and EXERCoder.
To convert from XML to binary, use the XML2BinaryConvertor class.
The following step by step example shows you how to convert from an XML encoding (E-XER) to binary (Aligned PER).
First, create input and output stream objects. You decode from a file and encode into a memory byte array.
InputStream source = new FileInputStream("sample.xml"); OutputStream sink = new ByteArrayOutputStream();
Then create XML2BinaryConvertor with two coders EXERCoder decoder and AlignedPER encoder obtained from the same "project":
XML2BinaryConvertor convertor = new XML2BinaryConvertor(project.getEXERCoder(), project.getAlignedPERCoder());
Configure the convertor for the desired decoder and encoder options.
convertor.enableAutomaticDecoding(); convertor.enableDecoderConstraints(); convertor.enableEncoderDebugging();
Invoke the convertor's convert() method. The input stream should contain the XML encoding for the MyPDU class.
try { convertor.convert(source, sink, new MyPDU()); } catch (DecodeFailedException e) { System.out.println("Decode failed."); } catch (EncodeFailedException e) { System.out.println("Encode failed."); }
The Binary2JSONConvertor and JSON2BinaryConvertor classes are extensions of EncodingRuleConvertor. The classes provide type-safe constructors for configuring binary to JSON and JSON to binary conversions.
public class Binary2JSONConvertor extends EncodingRuleConvertor { // Public Constructors public Binary2JSONConvertor(BinaryCoder decoder, JSONCoder encoder) // Public Methods public void setDecoder(BinaryCoder decoder); public void setEncoder(JSONCoder encoder); } public class JSON2BinaryConvertor extends EncodingRuleConvertor { // Public Constructors public JSON2BinaryConvertor(JSONCoder decoder, BinaryCoder encoder) // Public Methods public void setDecoder(JSONCoder decoder); public void setEncoder(BinaryCoder encoder); }
The JSONCoder and BinaryCoder are interfaces used to distinguish JSON and binary coder classes.
The coder objects that can be used as BinaryCoder typed parameters are: BERCoder, CERCoder, DERCoder, AlignedPERCoder, and UnalignedPERCoder.
The coder object that can be used as a JSONCoder typed parameter is: JSONCoder.
To convert from JSON to binary, use the JSON2BinaryConvertor class.
The following step by step example shows you how to convert from a JSON encoding (JSON) to binary (Aligned PER).
First, create input and output stream objects. You decode from a file and encode into a memory byte array.
InputStream source = new FileInputStream("sample.json"); OutputStream sink = new ByteArrayOutputStream();
Then create JSON2BinaryConvertor with two coders JSONCoder decoder and AlignedPER encoder obtained from the same "project":
JSON2BinaryConvertor convertor = new JSON2BinaryConvertor(project.getJSONCoder(), project.getAlignedPERCoder());
Configure the convertor for the desired decoder and encoder options.
convertor.enableAutomaticDecoding(); convertor.enableDecoderConstraints(); convertor.enableEncoderDebugging();
Invoke the convertor's convert() method. The input stream should contain the JSON encoding for the MyPDU class.
try { convertor.convert(source, sink, new MyPDU()); } catch (DecodeFailedException e) { System.out.println("Decode failed."); } catch (EncodeFailedException e) { System.out.println("Encode failed."); }
The lean runtime is designed for use in production environments.
If your application does not require the full range of functions provided by the oss.jar runtime library, consider using the lean version of the runtime, osslean.jar located in the lib subdirectory of the installation. The lean runtime is half the size of the oss.jar and runs up to 20% faster.
Note that the lean runtime does not include support for the following functions provided by the oss.jar runtime:
It is recommended that you compile using the -lean command-line option. The option helps you save space by excluding constraint and debugging information, which is not needed when using the lean runtime. Also, when compiling with the -lean option, the ASN.1 compiler will issue warning messages if your .asn input contains types or directives that are not supported by the lean runtime. Note that when the -lean option is used with osslean.jar, the -soed compiler option must also be used.
To use the lean runtime you, replace the path to oss.jar in your CLASSPATH setting with the path to the osslean.jar file.
JIA API stands for Java InterActive Application Programming Interface. It is a separate runtime library that allows manipulation of ASN.1 data in a generic way. The JIA API class library requires the SOED runtime.
The OSS ASN.1/Java Tools assumes that your application code knows the type of the value that you wish to modify, instantiate, or examine. However, certain applications cannot predict the type, and need generic access to data, for example:
Additionally, certain applications can instantiate a value to provide the name of its ASN.1 type rather than the Java class name, or access a field of a SEQUENCE or SET that provides the field name.
The JIA API also provides the ASN.1 parser so that you can read values in ASCII format. Every self-contained valid ASN.1 value notation is parsable. Self-contained means that each value does not reference other parts of the ASN.1 schema. So for example, a BOOLEAN value can be parsed if it is "TRUE", "FALSE", "T", "F", "0" or "1" (case insensitive). An OCTET STRING is an Hstring ('12345'H) or a Bstring ('1010101'B). For a CHOICE value, the parsable input is 'selector : value'.
You can find the JIA API class library and the documentation in the jiaapi.zip that resides in the root directory of the installation. To use the JIA API, unzip the jiaapi.zip at the location of your choice and append the jiaapi.jar to your CLASSPATH.
When using the BER, DER, CER and PER encoding rules, you can obtain encoded value offset or size information from the decoder. This feature is available with the SOED runtime and is not available for XER, CXER, or E-XER.
A PDU usually consists of several components. Each encoded value component occupies a bit-field inside the entire PDU encoding. During decoding, the offset/size information of each bit-field can be collected by the decoder and stored in an AbstractDataPositions table object. When decoding is complete, an application can retrieve the offset/size information of the PDU components from the table object using either the JIAAPI or (with some limitations) directly, using a decoded component reference.
The decodeWithPositions() method of the com.oss.asn1.Coder class is used to receive offset/size information from the decoder in addition to the decoded value. The method's signature is the same as that of decode() except. Its return type is an AbstractDataWithPositions object.
public AbstractDataWithPositions decodeWIthPositions (InputStream source, AbstractData pdu) throws DecodeNotSupportedException, DecodeFailedException;
NOTE: Position information is not collected by the XER, CXER, and E-XER coders. The decodeWithPositions() method returns an empty positions table when invoked for the above encoding rules.
The AbstractDataWithPositions class is a container that wraps an AbstractData decoded value object and an AbstractDataPositions object.
The AbstractDataWithPositions class provides getter methods for the decoded value, its components' positions in the encoded data, and the entire PDU size in bits.
public class AbstractDataWithPositions { public AbstractData getDecodedValue(); public AbstractDataPositions getPositions(); public int getSize(); }
The position of individual fields can be obtained from the AbstractDataPositions object. An individual field's offset/size information is represented in bits by an instance of the com.oss.asn1.Position class. The instance also indicates whether the field is primitive or constructed in the case of BER/DER/CER encoded fields.
public class Position { public Position(int offset, int size); public int getOffset(); public int getSize(); public boolean isConstructed(); }
AbstractDataWithPositions, AbstractDataPositions, and Position classes.
The ASN.1 Basic Encoding Rules (BER), as well as DER and CER, use tag-length-value (TLV) triplets to encode values. The tag part identifies the value type and form of the encoding. The length part specifies the length of the value (content) part. All the parts are encoded into an integral number of octets.
BER encodings can be primitive (the content octets directly represent the value) or constructed (the content octets contain other nested TLVs).
The offset/size information returned by the BER/DER/CER decoder is specified in bits (instead of bytes) to be aligned with the PER decoder.
For both primitive and constructed encodings, the value of the offset points to the beginning of the value part of the TLV. For open type values, the offset points to the beginning of the tag. The constructed encodings are indicated by a boolean flag that can be queried by the isConstructed() method of the com.oss.asn1.Position class. The size indicates the value length specified in bits. In the case of a constructed encoding, the size also includes all the tag-length octets of any nested TLVs.
In PER encodings, component values are represented by bit-fields. The PER ALIGNED encoder may insert padding bits as necessary to ensure that a particular bit-field starts at an octet boundary. An offset/size returned by the PER decoder delimits a bit-field that corresponds to a value.
The padding bits inserted by the PER ALIGNED encoder are not treated as part of a bit-field, and are not included in the bit field's size.
The exact format of the bits delimited by the offset/size depends on the type of the value and is specified by the ITU-T X.691 - "Specification of Packed Encoding Rules (PER)" standard.
NOTE: For complex types the padding bits added by the PER ALIGNED encoder to align bit-fields of nested components are treated as a part of the enclosing value, and are included in the bits delimited by the offset/size for the SEQUENCE/SET/SEQUENCE OF/SET OF types. The PER decoder returns each offset/size in such a way that if the bits delimited by the offset/size are presented to the PER decoder again, it should be able to decode them into an abstract value.
The Java Interpretive ASN.1 API (JIAAPI) allows manipulation of the decoded data in a generic way without prior knowledge of the data type. Individual data elements are handled via instances of the JIA_IE (IE stands for "Information Element") class. To get the position information via the JIAAPI, the "root" IE must be initialized from an AbstractDataWithPositions object obtained by the decodeWithPositions() method of the coder class. A JIA_IE factory method accepts the data with positions.
public static JIA_IE getInstanceOf(AbstractDataWithPositions value);
Positions of individual information elements in the encoded data stream can then be obtained with the getPosition() method of the JIA_IE class.
public Position getPosition()
The method returns a reference to the Position object, or null if the position information for the element is not available. null will also be returned if the root IE was created using a plain AbstractData object, not an AbstractDataWithPositions object.
The following example shows how to decode a PDU and print the encoded data positions of its components. For simplicity, exception handling is not included in this example.
import com.oss.asn1.*; import com.oss.jiaapi.*; import baseball.*; import baseball.bcas.*; ... AbstractDataWithPositions decoded = coder.decodeWithPositions( source, new BBCard()); JIA_IE_Collection_IE root = (JIA_IE_Collection_IE)JIA_IE.getInstanceOF(decoded); int numComponents = root.getNumberOfComponents(); for (int i = 0; i < numComponents; i++) { JIA_Component comp = root.getComponent(i); if (comp.isPresent()) { JIA_IE ie = comp.getIE(false); Position pos = ie.getPosition(); if (pos != null) System.out.println("Component " + comp.getName() + " was encoded at offset " + pos.getOffset() + " size " + pos.getSize()); else System.out.println("Position not available for " + comp.getName()); } }
You can access the position information using the ASN.1 type specific API. Because the AbstractDataPositions tables store the positions of all the AbstractData components created while decoding a PDU, you can to retrieve the position information directly from the positions table using an AbstractData component reference.
An AbstractDataPositions table can be queried for an arbitrary AbstractData component position as follows:
AbstractDataPositions positions = decoded.getPositions(); AbstractData data = ...; Position pos = positions.get(data);
The above method has limited functionality and it does not work in some cases. For instance, when complex type components are mapped to the Java primitive types, int, boolean and double, their AbstractData representations are not available. Second, multiple AbstractData components of the ASN.1 ENUMERATED type can be represented by the same object reference, so there is no one-to-one correspondence between the AbstractData references and the encoded data positions. The above get() method will return null if the table contains more than one row that corresponds to the data parameter.
Here is a fragment from the example in the previous section, modified for this method:
printPosition(positions.get(card.getName()), "name"); printPosition(positions.get(card.getTeam()), "team"); // ERROR! the getAge() method return type is "int" printPosition(positions.get(card.getAge()), "age"); printPosition(positions.get(card.getPosition()), "position"); // ATTENTION! the following line can report the position not available printPosition(positions.get(card.getHandedness()), "handedness"); printPosition(positions.get(card.getBatting_average()), "batting_average");
The PER encoder fragments large binary data (such as open types, OCTET STRING, or BIT STRING with contents constraint imposed) into 64K, 32K or 16K chunks, where each chunk is prefixed with a length-determinant. Therefore it is possible that a bit-field representing a field nested in an open type is not contiguous, but has a length-determinant from the outer-level encoding in the middle. This case will not be supported by the PER decoder. If the encoding of a component crosses the chunk boundary, no position information will be returned for such a field.
A ::= SEQUENCE { f1 INTEGER, f2 TYPE-IDENTIFIER.&Type (B) } B ::= SEQUENCE { f1 OBJECT IDENTIFIER, f2 TYPE-IDENTIFIER.&Type(C) } C ::= SEQUENCE { f1 OCTET STRING, f2 INTEGER }
If the length of f1 (OCTET STRING) in the value of C is large (< 16K), the bits representing C.f2 in the value of A may have a length-determinant that belongs to A.f2 in the middle. The PER decoder will not be able to return the offset/size information of the C.f1 field in this case, and will return null to the calling application.
This limitation applies to the position information returned for the values that were manually decoded from an open type, OCTET STRING or BIT STRING with contents constraint imposed.
A ::= SEQUENCE { f1 INTEGER, f2 TYPE-IDENTIFIER.&Type } or B ::= SEQUENCE { f1 INTEGER, f2 OCTET STRING CONTAINING INTEGER }
Values of such types are usually decoded in two steps. First, the outer value is decoded. If the encoding of A.f2 or B.f2 is fragmented, the fragments are concatenated and any length-determinants are stripped at this step. Then the decoder is invoked for the second time with the input stream containing the A.f2 or B.f2 octets.
There will not be a way to pass the position information for A.f2 or B.f2 to the second invocation of the decoder. Therefore, the offset/size for the values decoded from A.f2 or B.f2 are relative to A.f2/B.f2, not from the initial buffer that carried the encoding of A or B.
The partial decoding feature enables decoding of particular fields from input messages while the remaining fields are ignored. Using the partial decoding feature, users of the OSS ASN.1 Tools for Java are able to
The decodePartial() method of a Coder object does not return the decoded PDU value. Instead, it invokes user-defined callback methods when decoding each field marked by the OSS.DataCallback or OSS.InfoCallback compiler directive and, optionally, passes the decoded field value to it. Your callback method can analyze the decoded value, and by setting the return code, instructs the decoder to either terminate or continue decoding.
The partial decoding feature requires the TOED runtime (-toed compiler option).
Only the BER, DER, PER, UPER, CPER, CUPER, OER, and COER binary encoding rules support partial decoding.
For the following ASN.1 syntax, the ZIP code nested within homeAddress will be extracted while the ZIP code nested within the company address is ignored:
M DEFINITIONS AUTOMATIC TAGS ::= BEGIN Subscriber ::= SEQUENCE { name VisibleString, company Company, homeAddress Address } Company ::= SEQUENCE { name VisibleString, address Address } Address ::= SEQUENCE { zipcode INTEGER( 0..99999 ), addressline VisibleString( SIZE (1..64) ) } END
First, apply the following directives and compile the specification with the -toed option and either the -enablePartialDecoding or -partialDecodeOnly option.
--<OSS.DataCallback M.Address.zipcode "myZipcode">-- --<OSS.InfoCallback M.Subscriber.homeAddress "homeAddressField">--
The options instruct the compiler to generate the PartialContentHandler class with the following abstract callback method declarations:
public abstract Response beginMyZipcode(); public abstract Response endMyZipcode(com.oss.asn1.INTEGER data); public abstract Response beginHomeAddressField(); public abstract Response endHomeAddressField();
The com.oss.asn1.ContentHandler.Response Java enum class defines possible return codes from the callback methods:
CONTINUE - Continue with normal processing. SKIP - If returned from a "begin" callback, skip to the end of the field, suppressing further callbacks that could occur inside the field, including the "end" field callback. If returned from an "end" callback, skip to the end of the PDU, consuming all the encoded stream data without any further callback invocations. ABORT - Terminate partial decode procedure and return to the caller immediately.
Then, define the subclass of the generated PartialContentHandler and implement the abstract methods as follows:
public class MyPartialContentHandler extends PartialContentHandler { private boolean _insideHomeAddress = false; public Response beginHomeAddressField() { _insideHomeAddress = true; return Response.CONTINUE; } public Response endHomeAddressField() { return Response.SKIP; } public Response beginMyZipcode() { return _insideHomeAddress ? Response.CONTINUE : Response.SKIP; } public Response endMyZipcode(INTEGER value) { // Consume ZIP code // ... return Response.CONTINUE; } public MyPartialContentHandler(AbstractData contentType) { super(contentType); } }
Finally, invoke the partial decode operation as follows:
Subscriber pduType = new Subscriber(); coder.decodePartial(source, new MyPartialContentHandler(pduType));
Alternatively, you can apply OSS.InfoCallback to Subscriber.company rather than Subscriber.homeAddress:
--<OSS.InfoCallback M.Subscriber.company "company">-- Define the content handler as follows: public class MyPartialContentHandler extends PartialContentHandler { public Response beginCompany(String name) { return Response.SKIP; } public Response endCompany(String name) { return Response.CONTINUE; } public Response beginMyZipcode() { return Response.CONTINUE; } public Response endMyZipcode(INTEGER value) { // Consume ZIP code // ... return Response.CONTINUE; } public MyPartialContentHandler(AbstractData contentType) { super(contentType); } }
You would get the same result: the homeAddress ZIP code is extracted and the company address ZIP code is ignored.
Note: Partial decoding is a chargeable feature in non-evaluation licenses. Contact Sales to obtain pricing information.
The "boolean isUnknownExtensionFound()" method of the com.oss.asn1.Coder class checks whether the last PDU decoded by the decode() method contained at least one unknown SEQUENCE, SET, or CHOICE type extension. The presence of unknown extensions indicates that the sender is using a newer version of the ASN.1 specification.
This documentation applies to the OSS® ASN.1 Tools for Java release 8.7 and later.
Copyright © 2024 OSS Nokalva, Inc. All rights reserved.
No part of this publication may be reproduced, stored in a retrieval system, or transmitted in any form or by any means electronic, mechanical, photocopying, recording or otherwise, without the prior permission of OSS Nokalva, Inc.
Every distributed copy of the OSS® ASN.1 Tools for Java is associated with a specific license and related unique license number. That license determines, among other things, what functions of the OSS ASN.1 Tools for Java are available to you.