Considerations When Developing Applications That Use Oracle Messaging Cloud Service

Oracle Messaging Cloud Service is largely based on the Java Message Service (JMS) programming model.

Topics:

Oracle Messaging Cloud Service provides the same messaging patterns as JMS and also introduces a new pattern of its own. If you have experience with JMS, the concepts should be familiar. If you are not yet familiar with JMS, see About Messaging Concepts to review basic concepts such as destinations, producers and consumers, and connections and sessions.

The concepts in this section apply to both the REST API and Java library. Regardless of your experience with JMS or REST APIs, and whether you are developing applications with the Java library or REST API, it is important that you review the general guidelines documented in this section first, before referring to the subsequent sections for specific guidelines on Using the Java Library or Using the REST API.

About Queues and Topics

Queues and topics are types of destination to which messages can be sent.

  • Queues: Messages sent to a queue are received by one and only one consumer.

    When a message is sent to a queue, the message is kept until it is either received or until it expires. This quality of queues removes the timing dependency between producers and consumers, which means producers and consumers don't have to be available and communicating at the same time.

    When a message is received, the consumer can either automatically or manually acknowledge message receipt to the messaging platform, indicating whether the message was received or not. See About Sessions, Acknowledgement Modes, Transactions, and Provisional Messages for information about acknowledgement modes.

    Each instance of Oracle Messaging Cloud Service has a bound on the number of queues it can have. See About Resource Limits for the maximum number of messaging resources per service instance in paid and trial service subscriptions.

  • Topics: Messages sent to a topic may be received by multiple consumers or none.

    Consumers must be connected to a topic when a message is sent to the topic in order to receive the message. This quality of topics implies there is a timing dependency between message producers and message consumers. If a client has no consumers on a topic, it will miss any messages sent to the topic until it creates a consumer on the topic.

    If the timing dependency for receiving a message is undesirable, a client can create a durable subscription for the topic. A durable subscription stores all messages sent to a topic until each message is received.

    Each instance of Oracle Messaging Cloud Service has a bound on the number of topics and durable subscriptions it can have. See About Resource Limits for the maximum number of messaging resources per service instance in paid and trial service subscriptions.

The time-to-live or maximum time a message can live in Oracle Messaging Cloud Service is 14 days. The time-to-live can be set to a value less than 14 days for any given message. When a message reaches the defined time-to-live value, it is permanently deleted.

About Message Push and Message Push Listeners

Messages sent to a destination can be pushed to either another destination or to a user-defined URL through message push listeners.

A user-defined URL's scheme can be either HTTP or HTTPS. For message push to an HTTPS URL to succeed, the server to which messages are pushed must have a valid Secure Sockets Layer (SSL) certification from Verisign. A message push listener asynchronously receives messages from a queue, topic, or durable subscription. When a message is received by a message push listener, the message is pushed to the configured target.

Messages can only be pushed to a user-defined URL via PUT and POST HTTP requests.

Message push listeners can have specific retry and failover policies that define what the message push listener does if delivery of a message to a target fails. Failover policies can push a message to another destination or user-defined URL. If a message push listener cannot deliver a message and no failover policy is specified, then the message push listener discards the message.

Message push listeners will not follow HTTP redirects. If a message push listener makes an HTTP request to push a message to a user-defined URL, and receives a redirect response (that is, a response whose status is in the range 300-399), the message push listener will treat it as an error response. Any further handling of the message will be as defined by the message push listener's failover policy. The message push listener will not push the message to the location specified in the redirect response.

Each instance of Oracle Messaging Cloud Service cannot be assumed to support more message push listeners than available connections (which are required to send and receive messages). See About Connections for information about why it is important to assume that each message push listener will use a dedicated connection.

About Verification of Message Push Listeners

A message can be pushed to a user-defined URL using a message push listener. Before a message push listener can be created, Oracle Messaging Cloud Service must verify all URL targets of the message push listener. In other words, a message push listener is created only after the service has verified that all URLs to which the listener might push a message are willing to receive such pushes, as shown in the following diagram.

The process to verify the URL targets of a message push listener is as follows:

  1. A request is made to create a new message push listener, through either the REST API or Java library. Note that the message push listener is not actually created until its URL targets have been verified.

  2. Oracle Messaging Cloud Service sends a verification request to every user-defined URL in the listener's definition (primary target and failover targets). Each verification request is an HTTP request that includes the following:

    • An HTTP header with name X-OC-MPL-CHALLENGE whose value is a service-generated pseudorandom challenge token

    • An HTTP header with name X-OC-MPL-VERIFICATION whose value is a user-provided verification token

  3. For a verification request to succeed, the user-defined URL must echo the pseudorandom challenge token as the body of the HTTP response, and the HTTP response must return with a status code of 200. Optionally, the user-defined URL can also validate the value of the user-provided verification token. If the verification request receives a redirect response, the verification request fails.

    The verification token is essentially a way for a client creating a message push listener to identify itself to the endpoint as a trusted entity. The verification token could, for example, be a secret string shared between the endpoint and the client, a one-time password, or a cryptographically signed token. The client and endpoint are free to use the value in whatever way they like.

  4. If the verification request fails for any user-defined URL, the creation of the message push listener will also fail.

    The purpose of verifying each user-defined URL is to ensure that the owner of the URL is willing to accept pushed messages.

About Destination Deletion

This section provides information about what happens when a client is using, or attempts to use, a non-temporary destination (queue or topic) that is deleted.

Deleting a non-temporary destination is a non-blocking operation. The operation of deleting a destination (either through the REST API or through the Java library) can complete and return control to the client, and the destination can still be in the process of being deleted. Destinations that are in the process of being deleted, but whose deletion is not yet complete, are referred to as being marked for deletion. Destinations that are marked for deletion will still be listed when all destinations are listed, and their properties can still be retrieved. They will, however, have status MARK_FOR_DELETION. A destination can have status MARK_FOR_DELETION for some time.

Any use of a destination that is marked for deletion may fail. This includes sending to it, receiving from it, browsing it (if it is a queue), creating message push listeners on it, having a message push listener push messages from it or to it, etc. This applies both to attempts to use the destination after it is marked for deletion and uses of the destination that began before it was marked for deletion. Sends to a destination that is marked for deletion may succeed or fail. Messages on or sent to a destination that is marked for deletion may or may not be lost. It is recommended that applications be implemented so as to avoid making any use of destinations that are marked for deletion, for example, by using a fixed set of destinations that does not change over time, shutting down all uses of a destination before deleting it, etc.

Message push listeners that listen on a destination that becomes marked for deletion will be deleted automatically. Applications that might be sensitive to the exact time that a message push listener is deleted after its destination is marked for deletion should delete the message push listeners on a destination manually before deleting the destination.

Message push listeners that push to a destination that is marked for deletion or fully deleted will not be deleted automatically when that destination is deleted. Any message push listener that pushes to a destination that might be deleted while the message push listener still exists should be configured with a failure policy that ensures that messages will not be lost if the target destination is deleted.

About Connections

Connections are required to send and receive messages between clients and Oracle Messaging Cloud Service. A connection represents all of the resources needed for communication between clients and the messaging platform.

A client usually uses one connection for all of its sending and receiving operations, though a client can use multiple connections if desired.

Each instance of Oracle Messaging Cloud Service has a quota of concurrent connections. When an instance is using 100% of the connection quota, additional attempts to establish new connections will fail.

A new connection is used when:

  • A connection is created through the REST API

  • A JMS connection is created through the Java library

  • A message push listener is created

Oracle Messaging Cloud Service may allocate message push listeners to connections in different ways in order to optimize performance. The service may have multiple message push listeners use the same connection or have different message push listeners use different connections.

See Messaging Context and HTTP Cookies and Cross-Site Request Forgery (CSRF) Prevention for additional guidelines when using the REST API.

About Sessions, Acknowledgement Modes, Transactions, and Provisional Messages

Once a connection has been created, a session must also be created before messages can be sent or received. A session provides a behavioral context that defines what it means for messages to be sent and received between clients and Oracle Messaging Cloud Service.

A single connection can have multiple sessions. Unless explicitly closed, sessions persist until the connection from which they are created is closed.

A message received by a client through a session must be acknowledged before its receipt is treated as final by Oracle Messaging Cloud Service. When a session is created, its acknowledgement mode must be set to one of the following options:

  • Auto-acknowledge: In auto-acknowledge mode, every message received is automatically acknowledged immediately after it is received. Any message received through an auto-acknowledge session is final.

  • Client-acknowledge: In client-acknowledge mode, clients must explicitly acknowledge all message receipts. This allows clients to examine a message to determine if it is prepared to consume the message or not. It is recommended that this mode be used if the session is not transacted and if the Messaging Service is being used for applications in which messages must not be lost.

  • Duplicates-OK: In duplicates-OK mode, message receipts are acknowledged automatically, but lazily, which means messages are not individually acknowledged when they are received but are automatically acknowledged at a later point in time. This mode reduces the communication overhead between clients and the messaging platform, and may increase the rate at which messages are received.

    Using the duplicates-OK mode, however, may result in any given message being delivered multiple times, potentially to more than one client. If a session using this acknowledgement mode unexpectedly closes, the messages delivered to the client since the last acknowledgement may be made available for delivery to other clients.

Sessions created through the REST API are auto-acknowledged by default. Sessions created through the Java library must have their acknowledgement mode specified explicitly.

Instead of specifying an acknowledgement mode, a session can be configured such that sequences of send and receive operations are grouped into atomic operations known as transactions. Like acknowledgement modes, sessions are configured to be transacted or not transacted when the session is created. In a transaction, all grouped send and receive operations either complete or do not complete collectively.

At any point in a transaction, the client can call rollback, and all previous send and receive operations within that transaction will be cancelled. Transacted sessions must be explicitly committed. When the client calls commit on a transaction, all send and receive operations are made permanent and a new transaction is started.

In sessions whose mode is client-acknowledge, the client receiving messages is responsible for explicitly acknowledging that messages have been received. Until the client explicitly acknowledges a message, received messages are considered to be provisional. If the client fails to perform acknowledgement, provisionally received messages are returned to the destination and made available to be received by another client when the session or its connection is closed.

Similarly, in transacted sessions the client sending and receiving messages is responsible for explicitly committing the session. Until the session is committed, all sent and received messages within the transaction are considered to be provisional. Failure to commit transacted sessions will cause provisionally sent messages to be discarded and provisionally received messages to be made available for delivery to other clients. In both client-acknowledge mode and transacted sessions, when a provisionally received message becomes available to be delivered to another client, and when the message is so delivered, it is marked as redelivered.

About Producers, Consumers, and Selectors

A session sends messages through a producer and receives messages through a consumer. A session can have multiple producers and multiple consumers. Unless explicitly closed, producers and consumers persist until the session in which they are created is closed.

A producer defines the default characteristics of how messages are sent to and stored within the messaging platform. A producer can specify the destination to which all messages are sent, how sent messages should be stored on the target destination, and how long sent messages can live in the service before they expire.

A consumer defines how messages are received from the messaging platform. A consumer must specify the destination from which messages are received.

Optionally, consumers can select a subset of all available messages to be received from a destination by specifying a selector. A selector is an SQL-like expression that specifies a condition that a message must satisfy to be eligible for the consumer to receive the message. Selectors can only select messages based on criteria in the message headers and properties. Selectors cannot select messages based on the contents or type of a message body (for example, Text or Object type). For the syntax of selectors, see the Message Selectors section of the Java API reference for the javax.jms.Message class. For the syntax of selectors, see the Message Selectors section of the Java API reference for the javax.jms.Message class at the URL:

http://docs.oracle.com/javaee/6/api/javax/jms/Message.html

About Parts of a Message

Messages are unique, discrete units of information that pass between two or more clients through Oracle Messaging Cloud Service. Each message has three parts: headers, properties, and a body.

Topics:

Message Headers

Message Properties

Message Body and Message Size

Message Headers

Message headers are predefined key/value pairs associated with a message.

Message headers are used by the messaging platform for message identification and routing purposes. Some headers are client-set and some are broker-set. For client-set headers, ‘Required’ means that the client must supply the header and ‘Optional’ means that client need not supply it. For broker-set headers, ‘Required’ means the broker will always set it, whereas ‘Optional’ means the broker may or may not set it.

The headers, some of which may or may not be present in a message, are:

  • Correlation ID: An identifier that can be set by the sending client to correlate multiple messages. The value of this header is set by the sending client.

  • Delivery Mode: Required. The persistence type of the message. The value of this header is set by the sending client.

  • Destination: Required. The destination to which the message was sent. The value of this header is set by the sending client. For more information, see About Persistent and Non-Persistent Messages.

  • Expiration: Required. The time when the message will expire. The value of this header is a long integer, and is interpreted as Unix time. This value is set by the JMS broker, but is partially a function of the message's time-to-live, which is set by the sending client.

  • Message ID: Required. The globally unique ID of the message. This value is set by the JMS broker.

  • Redelivered: Required. Indicates whether the message has been delivered at least once before. Value is true or false. This value is set by the JMS broker.

  • ReplyTo: A destination to which replies to this message should be sent. Controlled by the sending client.

  • Time: The time when the message was sent to the destination. Set by the JMS broker.

Message Properties

Message properties are optional key/value pairs associated with a message. Some message properties are user-defined, and some are set by the system.

Message property values can have the following classes:
  • Boolean

  • Byte

  • Short

  • Integer

  • Long

  • Float

  • Double

  • String

Selectors can use message properties to restrict the messages received by a consumer. See About Producers, Consumers, and Selectors for information about selectors.

Message Body and Message Size

A message body must have a type, which defines the format and structure of the body.

The message type can be one of the following:

  • PLAIN: The message has no body. It only has headers and properties.

  • TEXT: The message body is a String.

  • BYTES: The message body is an array of bytes.

  • OBJECT: The message body is a serialized Java object.

  • MAP: The message body is a set of key/value pairs. Keys are Strings and values are Java objects. Each value can be either a Boolean, Byte, Character, Short, Integer, Long, Float, Double, String, or an array of bytes.

  • STREAM: The message body is a stream of Java objects. Each object in a stream is either a Boolean, Byte, Character, Short, Integer, Long, Float, Double, String, or an array of bytes.

  • HTTP: The message body is a serialized Java object that contains a byte array representing the body of an HTTP request or response, and Strings representing the HTTP headers that specify the language and media type of that body.

Messages have a maximum size of 512KB. Send operations with messages larger than 512KB will fail. See Accessing Oracle Messaging Cloud Service Using REST API and Accessing Oracle Messaging Cloud Service Using Java Library for information about how message size is calculated.

About Persistent and Non-Persistent Messages

When a message is sent to a destination, the message delivery mode is marked as persistent by default.

Persistent messages are guaranteed to be stored in a durable medium while being processed by Oracle Messaging Cloud Service. This means persistent messages are not lost if Oracle Messaging Cloud Service temporarily goes down.

Optionally, messages can be marked as non-persistent. Non-persistent messages may or may not be stored in a durable medium. Since non-persistent messages do not require as much I/O as persistent messages, higher throughput rates may be achieved by using non-persistent messages. If Oracle Messaging Cloud Service temporarily goes down, however, non-persistent messages may be lost.

A queue or topic cannot have more than 100,000 messages at any given time. Send operations to a destination with 100,000 messages will fail. The 100,000 limit applies to either persistent or non-persistent messages, or a combination of both.

Note:

While persistent messages are always stored in a durable medium, there are rare instances when the durable medium and the messages stored may be lost. Additional copies of mission-critical data should therefore always be stored in secure and reliable locations.

About Authorization

User roles and privileges are described in Getting Started with Oracle Cloud.

In addition to the roles and privileges described in Managing User Accounts and Managing User Roles in Getting Started with Oracle Cloud, two default account roles, Messaging Administrator and Messaging Worker, are created for Oracle Messaging Cloud Service when the service instance is provisioned. Each role has privileges that define what operations users are authorized to perform in the service instance. Any user with the Messaging Administrator role can potentially delete any destination within the instance. Any user with the Messaging Administrator role or the Messaging Worker role can potentially send or receive messages to any destination within the instance. See About Oracle Messaging Cloud Service Roles and Users for additional privileges associated with each role.

About Service Termination

When an instance of Oracle Messaging Cloud Service is terminated, no customer data is archived. Messages residing on destinations are deleted immediately upon service termination.

Before terminating an instance, be sure to drain and store messages from queues and durable subscriptions if the contents of stored messages are important.

About the Ordering of Message Delivery

Oracle Messaging Cloud Service does not provide any strict guarantees about the order in which messages are delivered.

Applications for which message ordering is critical should use the "redelivered" message header to detect redeliveries, and should consider the use of timestamps or sequence numbers, possibly attached to messages as message properties or in the message bodies, to ensure that messages are processed in the proper order.

Using Message Groups

Message groups can be used to send a message that is larger than 512KB in a set of multiple smaller messages using a queue.

Message groups are used to group different messages that should all be processed by the same consumer. Message groups are created implicitly by sending messages that have a message ID and sequence number set on them to a queue.

A message group is defined by a group ID and a group sequence number. If a message belongs to a group, the group it belongs to is defined by the value of its JMSXGroupID property.

Note:

  • If you send messages in a specific group to a queue, and the sequence numbers are out of order, the consumer will receive them in the order in which they were sent, not in order of message sequence.

  • The first message sent in a group must have sequence number 1.

  • Sequence numbers have no effect on message receipt order from queues.

  • Sequence numbers are not used to eliminate duplicate messages.

  • The use of message groups has no effect when used with topics: all consumers on the topic will get all messages, and receipt order will not be affected by sequence numbers.

  • Sequence numbers play an important role in the delivery of messages in a message group. For example, consider a scenario such as the following:
    • Messages are sent in group G. Consumer C is chosen to receive group G, and gets some of the messages.

    • After consumer C receives few messages from group G, consumer C is closed. However, more messages are sent in group G.

    • A new consumer, D, is chosen to receive messages in G.

    • When consumer D receives messages that don’t start with sequence number 1, D can conclude that some other messages in the group were sent to some other consumer.

Method: POST

Path: /producers/producerName/messages

Scope: Messaging Context

Authorization: Messaging Administrator or Messaging Worker

Request Parameters:

Parameter Description

groupId

This parameter is used to set the JMSXGroupID property on the message being sent. This is the name of the message group of which this message is a part, if any.

Note:

If the JMSXGroupID property is set as an HTTP request header, it must be set to an escaped value String or a badParameter error response will be generated. For more information on escaped value Strings, see About Escaped Value Strings. If the JMSXGroupID property is set as a query string parameter, the usual conventions for escaping query string parameters hold.

groupSeq

This parameter is used to set the JMSXGroupSeq property on the message being sent. This is the sequence number of the message within the message group specified by the groupId parameter. The groupSeq parameter must be set to an integer or a badParameter error response will be generated.

Result: Sends multiple messages as a message group using a queue.

Error Responses:

Error Message Description

badParameter

The groupSeq parameter was not set to an integer.

incompleteGroupProperties

Exactly one of the JMSXGroupID and JMSXGroupSeq properties was set on the message. Either both properties must be set, or neither must be set.

See Use Message Groups for a code sample on sending messages using message groups.

Sending Large Objects as Messages Using Oracle Storage Cloud Service

You can use Oracle Storage Cloud Service to send large message payloads using a message that contains a reference to the payload. This is especially useful for storing and consuming messages with a message size of up to 5 GB, the maximum size of a single object stored in a Storage container.

You can send large payloads with a combination of Oracle Messaging Cloud Service and Oracle Storage Cloud Service and use the following features that the services allow:

  • Store the message payload as a storage object with an automatic deletion time.

  • Create a temporary URL to access the object containing the payload.

  • Send a message containing the temporary URL for the object.

  • Fetch the payload from the temporary URL.

  • Delete the corresponding message object from an Oracle Storage Cloud container.

About Oracle Storage Cloud Service

Oracle Storage Cloud Service is an Infrastructure as a Service (IaaS) product, which provides an enterprise-grade, large-scale object storage solution for data of any type. Oracle Storage Cloud Service stores data as objects within a flat hierarchy of containers.

Oracle Storage Cloud Service allows you to create temporary URLs for your objects, authenticate storage requests, and auto-delete the objects after a certain period.

To learn more about Oracle Storage Cloud Service, see Features of Oracle Cloud Infrastructure Object Storage Classic in Using Oracle Cloud Infrastructure Object Storage Classic.

About Temporary URLs

Temporary URLs are time-limited URLs that expire after a configured time period. You can create temporary URLs to provide a secure, temporary access to protected resources like objects in your Oracle Storage Cloud Service account. A temporary URL specifies both the object and the HTTP method with which the object can be accessed. If you do not have access to Oracle Storage Cloud Service, you can download an object from the service using a temporary URL.

Note:

The auto-delete time set on the object and the time at which the temporary URL expires are not necessarily the same time; they can be set independently.

See Downloading an Object Using a Temporary URL in Using Oracle Cloud Infrastructure Object Storage Classic..

Step-by-Step Procedure to Send a Large Object Stored in a Storage Container as a Message

The example shows a step-by-step procedure for sending a large object stored in a storage container as a message:

Note:

  • Only relevant HTTP headers are shown in the example.

  • The values of the secret key, password, etc. mentioned as <***> should be replaced by the appropriate values for the service or account, or by user-chosen values.

  1. Get an authentication token

    HTTP Request

    GET /auth/v1.0 HTTP/1.1
    Host: storage.oraclecorp.com
    Accept: */*
    X-Storage-User: Storage-msnerd:msnerd.Storageadmin
    X-Storage-Pass: <account password>

    HTTP Response

    HTTP/1.1 200 OK
    Server: nginx/1.10.2
    Content-Length: 0
    X-Auth-Token: AUTH_tk7e51d668b970abcc71f91c64e0fa5e38
    X-Storage-Token: AUTH_tk7e51d668b970abcc71f91c64e0fa5e38
    X-Storage-Url: https://storage.oraclecorp.com/v1/Storage-msnerd

    The authentication token and storage-URL generated in this section will be used in the requests for accessing the message payload.

  2. Create a container for your object

    HTTP Request

    PUT /v1/Storage-msnerd/storage-payload HTTP/1.1
    Host: storage.oraclecorp.com 
    Accept: */*
    X-Auth-Token: AUTH_tk7e51d668b970abcc71f91c64e0fa5e38

    HTTP Response

    HTTP/1.1 202 Accepted
    Server: nginx/1.10.2
  3. Set the container key using the information retrieved from steps 1 and 2

    HTTP Request

    POST /v1/Storage-msnerd/storage-payload HTTP/1.1
    Host: storage.oraclecorp.com
    Accept: */*
    X-Auth-Token: AUTH_tk7e51d668b970abcc71f91c64e0fa5e38
    X-Container-Meta-Temp-URL-Key: <container_secret_key>

    <container_secret_key> is a password key value specific to the account owner.

    HTTP Response

    HTTP/1.1 204 No Content
  4. Create the payload, upload the object to the storage container, and set an auto deletion time on the object

    HTTP Request

    PUT /v1/Storage-msnerd/storage-payload/<UUID> HTTP/1.1
    Host: storage.oraclecorp.com
    Accept: */*
    X-Auth-Token: AUTH_tk7e51d668b970abcc71f91c64e0fa5e38
    Content-Type: <object_content_type>
    X-Delete-At:  <Time-stamp _for_auto-delete>
    Content-Length: <Size_of_the_payload>
    [Message payload]

    Here are some examples of the variables used in the above request:

    • UUID - To create a payload, generate or use a unique ID that can be used to refer to the payload. For example, 1faf75ea-5619-4a0e-a8a3-764f360ee0eb.

    • object_content_type - This is the media type of the object. For example, application/octet-stream.

    • Time-stamp _for_auto-delete - This is the UNIX Epoch timestamp representing the date and time at which the object should be deleted. For example, 1416218400 represents November 17, 2014 10:00:00 GMT. See http://www.epochconverter.com/.

    • Size_of_the_payload - 3523574 (In Bytes)

    HTTP Response

    HTTP/1.1 201 Created
    Server: nginx/1.10.2
    Content-Type: <object_content_type>
    Content-Length: 0
    

    object_content_type is the media type of the object. For example, application/octet-stream.

    Sample Code to compute a temporary URL

    You can refer to the following sample codes to compute a temporary URL:

    • Java Code

    • Python Code

    Here’s a sample Java code to compute a temporary URL:

    import java.io.UnsupportedEncodingException;
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;
    import java.security.SignatureException;
    
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    
    /**
     *  Utility class for creating HMAC-SHA1 signatures and
     *  OSS temporary URLs.
     */
    public class HMACUtils
    {
        private static final String algorithm = "HmacSHA1";
    
        // Lower-case hex digits in order
        private static final char[] hexDigit = "0123456789abcdef".toCharArray();
    
        /**
         *  Method to convert a byte array into a string of lower-case hex digit pairs.
         *
         *  @param bytes
         *      Bytes to convert.
         *      May not be <code>null</code>.
         *
         *  @return
         *      String of hex digits corresponding to the input array.
         */
        public static String bytesToHexPairs(byte[] bytes)
        {
            StringBuilder sb = new StringBuilder();
    
            for(byte b : bytes)
            {
                sb.append(hexDigit[(((int)b) & 0b11110000) >>> 4]);
                sb.append(hexDigit[((int)b) & 0b1111]);
            }
    
            return sb.toString();
        }
    
        /**
         *  Method to compute HMAC-SHA1 signature from arbitrary data.
         *
         *  @param key
         *      Secret key for the signature, set on the OSS account or container.
         *      May not be <code>null</code>.
         *
         *  @param data
         *      Data to be signed.
         *      May not be <code>null</code>.
         *
         *  @return
         *      Bytes for the signature.  Must be rendered as lower-case hex-digit pairs in an OSS temporary URL.
         *
         *  @throws SignatureException
         *      An error occurred in generating the signature.
         *
         *  @throws NoSuchAlgorithmException
         *      Should not occur; the algorithm used should
         *      be valid HMAC-SHA1.
         *
         *  @throws InvalidKeyException
         *      An error occurred in processing the key.
         */
        public static byte[] signature(byte[] key, byte[] data)
            throws SignatureException, NoSuchAlgorithmException, InvalidKeyException
        {
            SecretKeySpec signingKey = new SecretKeySpec(key,algorithm);
            Mac mac = Mac.getInstance(algorithm);
            mac.init(signingKey);
            return mac.doFinal(data);
        }
    
        /**
         *  Method to compute HMAC-SHA1 signature for a method, expiration time, and path.
         *
         *  @param key
         *      Secret key for the signature, set on the OSS account or container.
         *      May not be <code>null</code>.
         *
         *  @param method
         *      HTTP method (e.g. GET, POST, DELETE, PUT).
         *      May not be <code>null</code>.
         *
         *  @param expiration
         *      Unix epoch time in seconds at which the signature will expire.
         *      Must be non-negative.
         *
         *  @param path
         *      Path (the part of the URL starting with '/' and coming after the host and port) to
         *      which the signature will give temporary access.
         *      May not be <code>null</code>.
         *
         *  @return
         *      Bytes for the signature.  Must be rendered as lower-case hex-digit pairs in an OSS temporary URL.
         *
         *  @throws SignatureException
         *      An error occurred in generating the signature.
         *
         *  @throws NoSuchAlgorithmException
         *      Should not occur; the algorithm used should
         *      be valid HMAC-SHA1.
         *
         *  @throws InvalidKeyException
         *      An error occurred in processing the key.
         *
         *  @throws UnsupportedEncodingException
         *      Should not occur; only UTF-8 encoding is used to convert characters to bytes.
         */
        public static byte[] signature(byte[] key, String method, long expiration, String path)
            throws SignatureException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException
        {
            return HMACUtils.signature(key, (method+'\n'+expiration+'\n'+path).getBytes("UTF-8"));
        }
    
        /**
         *  Method to compute a temporary OSS URL.
         *
         *  @param key
         *      Secret key for the signature, set on the OSS account or container.
         *      May not be <code>null</code>.
         *
         *  @param method
         *      HTTP method (e.g. GET, POST, DELETE, PUT).
         *      May not be <code>null</code>.
         *
         *  @param timeToExpiration
         *      Amount of time in seconds after which the
         *      temporary URL will expire.
         *
         *  @param path
         *      Path (the part of the URL starting with '/' and coming after the host and port) to
         *      which the signature will give temporary access.
         *      May not be <code>null</code>.
         *
         *  @return
         *      Path portion of the temporary URL.
         *
         *  @throws SignatureException
         *      An error occurred in generating the signature.
         *
         *  @throws NoSuchAlgorithmException
         *      Should not occur; the algorithm used should
         *      be valid HMAC-SHA1.
         *
         *  @throws InvalidKeyException
         *      An error occurred in processing the key.
         *
         *  @throws UnsupportedEncodingException
         *      Should not occur; only UTF-8 encoding is used to convert characters to bytes.
         */
        public static String temporaryURLPath(byte[] key, String method, long timeToExpiration, String path)
            throws SignatureException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException
        {
            long expiration = System.currentTimeMillis()/1000 + timeToExpiration;
            byte[] sig = HMACUtils.signature(key,method,expiration,path);
    
            return  path +
                    ((path.indexOf('?') < 0) ? '?' : '&') +
                    "temp_url_sig=" +
                    bytesToHexPairs(sig) +
                    "&temp_url_expires=" +
                    expiration;
        }
    
        /**
         *  <p>
         *      Command-line interface.  This interface takes
         *      arguments specifying a key and a temporary
         *      access to an object in OSS (such as a large
         *      message payload) and outputs the path portion of
         *      the temporary URL with which to access the object.
         *  </p>
         *  <p>
         *      Specifically, the arguments are as follows:
         *  </p>
         *  <ul>
         *      <li>
         *          the secret key with which to generate
         *          the signature; this must must have been
         *          set previously on the OSS container or
         *          account that will contain the object to
         *          be accessed
         *      </li>
         *      <li>
         *          the HTTP method to put into the signature
         *          (e.g. GET, POST, PUT, DELETE); this will
         *          determine what methods can be used with
         *          the temporary URL
         *      </li>
         *      <li>
         *          the amount of time, in seconds, after
         *          which the temporary URL will no longer
         *          function; the signature generated will
         *          work for that many seconds from the time
         *          at which HMACUtils is run.
         *      </li>
         *      <li>
         *          the path to the OSS object to be accessed
         *          via the temporary URL; this is the part
         *          of the full URL that comes <strong>after</strong>
         *          <nobr><code>https://</code><i>&lt;host&gt;</i><code>:</code><i>&lt;port&gt;</i></nobr>
         *          (including any leading '/').
         *      </li>
         *  </ul>
         *  <p>
         *      Usage:
         *      <nobr>
         *          <code>java HMACUtils</code>
         *          <i>&lt;key&gt;</i>
         *          <i>&lt;HTTP method&gt;</i>
         *          <i>&lt;time to expiration&gt;</i>
         *          <i>&lt;OSS object path&gt;</i>
         *      </nobr>
         *  </p>
         *  <p>
         *      The output on standard out will be the
         *      path portion of the temporary URL by which to
         *      access the object, that is, the portion that
         *      comes <strong>after</strong>
         *      <nobr><code>https://</code><i>&lt;host&gt;</i><code>:</code><i>&lt;port&gt;</i></nobr>.
         *  </p>
         */
        public static void main(String[] argv) throws Exception
        {
            byte[] key = argv[0].getBytes("UTF-8");
    
            String method = argv[1];
            long timeToExpiration = Long.parseLong(argv[2]);
            String path = argv[3];
    
            System.out.printf("%s\n",HMACUtils.temporaryURLPath(key,method,timeToExpiration,path));
        }
    }
    

    Here’s a sample Python code to compute a temporary URL:

    User Input: Specify the following parameters:

    serviceInstanceName = 'Storage' # Leave as is unless your service instance has a different name
    identityDomainName = 'acme' # Name of your identity domain
    container = 'myContainer' # Container that has the objects you need the tempURL for
    key = 'mykey' # X-Container-Meta-Temp-Url-Key or X-Account-Meta-Temp-Url-Key value
    object = 'myObject' # Object name that you need the tempURL for. This is optional if a container-level key is used.
    urlDuration = 300 # Seconds for which the temp URL should work
    serviceRestEndpoint = 'https://acme.storage.oraclecloud.com/v1/Storage-acme' # REST endpoint URL of your service instance, as shown in the Service Details page in My Services
    

    Code to generate the temporary URL:

    Note:

    Do not modify any values in this section.
    import hmac
    from hashlib import sha1
    from time import time
    path = '/v1/' + serviceInstanceName + '-' + identityDomainName + '/' + container + '/' + object
    expires = int(time() + urlDuration)
    hmac_body = '%s\n%s\n%s' % ('GET', expires, path)
    sig = hmac.new(key, hmac_body, sha1).hexdigest()
    url = serviceRestEndpoint + '/' + container + '/' + object +'?temp_url_sig=' + sig + '&temp_url_expires=' + str(expires)
    print(url)
    
  5. Use Oracle Messaging Cloud service to send a message with the temporary URL as the payload.

    See Sending Messages.

  6. Retrieve the payload using the temporary URL.

    HTTP Request

    GET /v1/Storage-msnerd/storage-payload/<UUID>?temp_url_sig=<temp_url_sig>&temp_url_expires=<temp_url_expire> HTTP/1.1
    Host: storage.oraclecorp.com
    Accept: */*

    temp_url_sig The sample Java/Python code above generates the full temporary URL. Part of the URL it creates is the value of temp_url_sig. This is the HMAC-SHA1 signature of the method, expiration time, and path to the object, signed using the container key set in step 3.

    temp_url_expires is the UNIX Epoch timestamp representing the date and time at which the temporary URL expires. Note that this is not when the object is auto-deleted, but the time when the temporary URL will stop working.

    HTTP Response

    HTTP/1.1 200 OK
    Content-Type: application/octet-stream 
    Content-Length: <Size of Payload>
    X-Delete-At: <Time-stamp for auto-delete>
    [Message payload]