---
schema_version: "1"
id: 36825c9d9953c01247e97ddbb667167f193fadf6dd1be4c532a0b7305205125d
title: Define Operations
collection: mulesoft
version: current
locale: en-us
portal_url: https://docs.salesforce.com/mulesoft/current/en-us/mule-sdk/latest/define-operations.md
canonical_url: https://docs.mulesoft.com/mule-sdk/latest/define-operations
source_site: docs.mulesoft.com
content_type: documentation
content_hash: 45ba3b94a9c2577108fb0784a2ff72d47a413202eb4d6b103e358a78bd179786
description: MuleSoft Documentation Site
release: "1.13"
latest: true
---
---
product: Mule SDK
version: 1.13
is-latest-version: true
---

# Define Operations

> For the full documentation index, see: https://docs.mulesoft.com/llms.txt

The sections that follow provide general rules that apply to all operations.

## Position Independence

Operations **must not** depend on their position in the flow for them to work.

This means that no component **should** depend on the side effects of a prior or next component to
fulfil its contract.

For example, consider the following flow:

```xml
<flow name="independence">
  <my-connector:operation-a />
  <my-connector:operation-b />
</flow>
```

`<operation-b>` **must not** depend on `<operation-a>` to have been previously executed for it to
work.

## Non-Metadata Key Parameters Must Not Change the Output Type

Behavior parameters **must not** change the operation’s return type.

> [!NOTE] There’s a special case regarding parameters that act as Metadata Keys. This rule applies
> to all non-metadata key parameters. For example, a module wants to offer the capability to perform
> a given action both synchronously (which means that the output results are available immediately)
> or asynchronously (the operation returns a promise ID that can be later be used to retrieve the
> result).

This could be solved like this:

```xml
<fruits:get-apple async="false"/>
<fruits:get-apple async="true"/>
```

The first operation returns an apple object, and the second returns a String representing a job ID.

This example is illegal. The get-apple operation **must** always return an apple. It cannot change
its return type based on the async parameter.

This **should** be modeled as two different operations:

```xml
<fruits:get-apple />
<fruits:get-apple-async />
```

## Dynamic Types and Metadata Key Parameters

Metadata Key parameters are treated as special because they are allowed to change the output type of
an operation. This change can be considered in two levels:

- The operation always returns an apple, but the definition of an apple changes depending on the
  system instance or user in use. Even if the structure of the apple is dynamic, it remains an
  apple.

- The operation is generic and the Metadata Key parameters actually determine which type will be
  returned.

For example:

```xml
<my-connector:search searchType="ORDERS" query="......" />
```

This operation does not violate the rule that the non-metadata key parameters **must not** change
the output type because:

- This operation returns a List, even if the type of the items in that list depends on the
  searchType parameter

- searchType is set to “ORDERS”, it always returns a list of ORDERS. There’s no other parameter in
  the operation that makes it return a list of ORDER\_REFERENCES instead.

## Operation Output

The output of operations **must** comply with the following:

### Always Communicate the MIME Type

For DataWeave to handle the output of an operation, it is critical to make sure that the MIME type
of the output value is always communicated.

The SDK does this automatically when the return type is a Map, POJO, or Java simple type (String,
Integer, Short, and so on).

However, when the output type is a String, InputStream, or byte\[\], the module **must** specify the
mimeType for that value. Otherwise DataWeave will not know how to handle it.

There are two ways of doing this:

- @MediaType annotation

  When the output type of the operation is always certain, the @MediaType annotation **must** be
  used:

  ```java
  @MediaType("application/json")
  public InputStream getPerson(String personId) {
  // ....
  }
  ```

- Use the Result object

  There are other cases in which the return MIME type is known only at runtime. Examples of this
  include:

  - Reading a file (the actual MIME type depends on the file being read)

  - Hitting an HTTP endpoint that accepts several MIME types (the server communicates the MIME type
    through the Content-Type header).

    For example:

    ```java
    public Result<InputStream, FileAttributes> read(String path) {
            return Result.<InputStream, FileAttributes>builder()
             .output(readStream(path))
             .mediaType(inferMediaTypeFromFileName(path))
             .attributes(getAttributes(path))
             .build();
    }
    ```

- Using Java POJOs as return types

  If the operation returns a POJO, then:

  - The class **must** be part of the module’s API.

  - The object **must** be a value object that cannot contain any logic.

  - The object **must not** contain any static state.

  - The object **must** be Serializable.

- Handling JSON and XML output types

  Some operations return values in the form of JSON or XML documents.

  - Java Types

    The JSON and XML documents **must** be returned in the form of an InputStream. None of the usual
    Java types for representing these types (Reader, Sax objects, Document, Node, JSON Node, and so
    on) are allowed.

  - DataSense Resolution

    For tooling to assist the user with handling the documents returned by the operation, the schema
    of the returned documents needs to be known. Therefore, operations **must** clearly define the
    output schema.

    - Static DataSense

      Sometimes, the schema is fixed and well known. If the schema is well known and static, the
      problem **must not** be resolved through the use of dynamic DataSense resolvers. The connector
      **must** specify that schema through either of the following instead:

      - @OutputJsonType

        Define the output type by pointing to a JSON schema that’s part of a module’s resources:

        ```java
        @OutputJsonType(schema = "person-schema.json")
        public InputStream getPerson(String personId) {
           // ...
        }
        ```

      - @OutputXmlType

        Similar to @OutputJsonType, but for XML schemas:

        ```java
        @OutputXmlType(schema = "order.xsd", qname = "shiporder")
        public void getOrder(String orderId) {
        ...
        }
        ```

      - OutputStaticTypeResolver

        Define the type programmatically.

        ```java
        @OutputResolver(OrderTypeResolver.class)
        public InputStream getOrder(String orderId) {
        ...
        }
        ```

### Dynamic DataSense

If the schemas are dynamic, a dynamic output resolver is needed. This is done by implementing the
OutputTypeResolver interface.

The key rule is that the module **must** resolve to a specific schema that the user can abide by.
The dynamic resolver cannot return a generic ANY type.

```java
@OutputResolver(DynamicOrderTypeResolver.class)
public InputStream getOrder(String orderId) {
...
}
```

### Dynamic Java Type Parameters

It’s very common for connectors to handle dynamic types. For example, services like Salesforce,
NetSuite, or SAP define a set of core entities (Person, Order, and so on) that have a base structure
that the user can customize.

Other services go further and allow for a completely custom type set, for example, any OData
service.

All operations returning these dynamic types **must** have an associated @OutputTypeResolver that
provides the actual type definition to be used for the current configuration.

Prior sections discussed this for output values communicated in the form of JSON or XML documents.
The same applies for parameters represented as Java types, such as Map or custom POJOs. A well
defined, non trivial OutputTypeResolver **must** exist for all other dynamic types, regardless of
which Java class is being used to represent them.

## Streaming

### Favor InputStream over byte\[\]

InputStream **should** be used as a return type instead of a byte\[\] array. Returning an array of
bytes is valid when such array is certain to be of a small size.

If the array size is unknown, variable or potentially large, then the operation **must** return
InputStream.

This avoids large memory requirements that can lead to an OutOfMemoryError, especially when running
in small workers. Returning InputStream implies that your code needs to avoid fully loading that
large piece of information into memory. Loading the entire byte\[\] array into memory and only
wrapping it into a ByteArrayInputStream completely defeats the purpose of this rule and **must** be
avoided.

### Don’t Return Large Strings

Returning a String is acceptable, but only if you can ensure that the string does not become a
String larger than 4 KB. If that’s a possibility, then the operation **must** return InputStream
instead, and use @OutputResolver to provide metadata on the expected media type.

### Use the StreamingHelper

When a component returns a streamable resource (InputStream or PagingProvider), the SDK
automatically makes those streams repeatable (provided that the application is configured to do so).

When those resources are contained in a higher level structure such as a Map, you **must** use
[StreamingHelper](http://www.mulesoft.org/docs/site/4.1.1/apidocs/org/mule/runtime/extension/api/runtime/streaming/StreamingHelper.html)
to explicitly tell the SDK to perform that adaptation.

Common cases:

- Stream contained inside a Map

  An example of this case is the <db:select> operation, in which the result set obtained from the
  database is returned in the form of a Map for which the keys are the column names and the values
  are the actual result. If the result set contains a BLOB column, then that value is of type
  InputStream. For that case, the connector **must** use this method, which returns a new Map in
  which all streamable resources have been made repeatable:

  `org.mule.runtime.extension.api.runtime.streaming.StreamingHelper#resolveCursors(Map, boolean)`

- Stream contained in a POJO

  Although not applicable for the case of database, it is possible to have a use case in which
  InputStream is actually contained in a field of a POJOo.

  In the previous case, `<db:select>` returns a Map because the actual structure of the response is
  unknown, as it changes with each query. Ensure you plan ahead to avoid causing problems that
  require difficult troubleshooting.

  However, if you’re using a POJO, it’s because the structure is fixed, which means that the POJO
  most likely contains some well-known attributes and some streamable information. Although we
  cannot 100% ensure it , most likely the fixed attributes of that POJO **should** go into an
  Attributes object and the stream into the payload, reducing this to the standard case.

  On the corner case in which doing this actually makes sense, the field containing the streaming
  resource **must** be adapted using the
  org.mule.runtime.extension.api.runtime.streaming.StreamingHelper#resolveCursorProvider(Object)
  method.

  > [!NOTE] Although the pattern of a POJO containing a streamable resource is not explicitly
  > forbidden, it is a big code smell that **should** be reviewed carefully and **must** be matched
  > with a custom Metadata resolver.

  Assuming that the scenario is valid, a custom Metadata resolver is necessary because you don’t
  know if the value will be adapted into a CursorProvider or not. It will have to be of type Object,
  which means it will become obscure to DataSense without the aid of a custom resolver.

### Favor Non-Blocking I/O

Mule 4 is a reactive execution engine that is optimized to perform non-blocking I/O operations.

When an input or output operation executes in non-blocking mode, the connector **must** adhere to
the MuleSoft [Non-Blocking Operations](https://docs.mulesoft.com/mule-sdk/latest/non-blocking-operations) requirements.

Best uses for non-blocking I/O:

- Accessing files in the local file system

- HTTP requests

- TCP/UDP socket requests

- Any I/O operation for which a non-blocking client exists

Where not to use non-blocking I/O:

- Accessing a database through JDBC (non-blocking JDBC drivers are still experimental)

- Using clients where non-blocking equivalents do not exist or are unreliable, such as using Apache
  Commons Net for accessing FTP servers where no reliable non-blocking option exists.

#### Non-Blocking and Asynchronous Are Not the Same

It’s common to confuse asynchronous with non-blocking. They are not the same thing. This rule of
favoring non-blocking I/O does not mean that all I/O operations **should** be dispatched to a
separate thread that is executed asynchronously just for the sake of it.

Be sure you have a good understanding of the principles of non-blocking IO and how it’s implemented
in Mule.

See [Non-Blocking Operations](https://docs.mulesoft.com/mule-sdk/latest/non-blocking-operations).

### Use Transactions

When an operation interacts with a transactional system (including XA transactions), the operation
**must** support that capability. See Transactions.

## See Also

- [Salesforce Help](https://help.salesforce.com)

- [Mule SDK Development Best Practices](https://docs.mulesoft.com/mule-sdk/latest/best-practices)
