# mulesoft Mule SDK - Full Documentation --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, classloading, isolation --- # About Classloading Isolation > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The Mule 4 classloading schema isolates the runtime, apps and modules from each other. Each specifies the packages (rather than the classes) to export as part of its API, making those specific packages visible. ## Modules Classloading Isolation Classloading is automatically filtered by package and not by class, meaning that modules need to specify which packages to export and make visible to the Mule app. To determine what packages to export, the SDK introspects each configuration, connection, and component return and input types to get their packages. So you need to make sure that any classes that you _do not_ want to export are kept out of exported packages. Exported classes should not share a package with classes that you do not want to export. The suggested package hierarchy (used internally for MuleSoft modules): org.mule.module \\--> .api.\* \\--> .internal.\* - `.api.*` contains all the classes that to be exported and seen by the Mule app: Return Types, Subtypes, Input Types, and Connection interfaces. - `.internal.*` contains all the classes used for specific behavior of the module: Annotated classes, `Connection` and `Operation` implementations, and internal classes. ### Knowing the Exported Packages and Return Types. MuleSoft recommends that **all** exported classes belong to the module. To avoid library clashes that make a module incompatible with other modules, app dependencies, or even runtimes, it is _highly recommended_ that you: - _Never_ return or receive objects from classes that belong to a library that a module is using. Consider the scenarios in the next sections. #### First Scenario: Exporting the Same Packages This scenario includes two modules with two very different operations. Both use the same library (GSON) to parse JSON and _return_ an instance of a `JsonObject` class. One of them uses GSON version `1.1`, and the other uses GSON version `2.3`. The `JsonObject` class lives in the same package in both versions, but the different implementations of each version make the classes incompatible. This example exposes the `JsonObject` class as the return type of the operation: First Module Operation ```java public JsonObject getEmployee(@Connection EmployeesServiceConnection connection, String id) { return new JsonObject(connection.getById(id)); } ``` Second Module Operation ```java public JsonObject getAllProducts(@Connection ProductsServiceConnection connection) { return new JsonObject(connection.getProdsJson()); } ``` When you deploy a Mule app and try to use two modules that export the same packages, Mule will fail because two modules cannot export the same package. #### Second Scenario: Returning Two Instances of the Same Class in Different Versions Building on the previous example, assume that the operations do not export the _GSON_ packages. Instead, they return a `java.lang.Object`. First Module Operation ```java public Object getEmployee() { // Here JsonObject is from GSON 1.1 return new JsonObject(); } ``` Second Module Operation ```java public Object getAllProducts() { // Here JsonObject is from GSON 2.3 return new JsonObject(); } ``` If you use both modules in an app, only one of the two versions will be loaded for the app because both classes have exactly the same fully qualified name. So the fully qualified names will clash, and the module that needs the _unloaded_ version of the package will behave differently and be prone to errors such as `ClassCastException` or `NoSuchMethodException`. ### The @Export annotation The `@Export` annotation can be used for extreme corner cases or complex cases in which a module needs to export a class that is not automatically exported by the SDK. The `@Export` annotation receives an array of classes to be exported. > [!WARNING] You cannot export a class without exporting all the classes in the same package as the > exported class. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: connector, certification, sdk, program guidelines --- # About MuleSoft Connector Certification Program Guidelines > For the full documentation index, see: https://docs.mulesoft.com/llms.txt This document pertains to connector certification for Mule 4. MuleSoft’s connector certification program guides you through the process of creating an enterprise-grade connector that is ready for certification by MuleSoft. Once certified, the connector is made available through Anypoint Exchange for discovery and use by customers and developers. ## Before You Begin Before embarking on the certification program, consider the following and make sure you can adhere to these requirements: - The connector must be kept current and secure, and it must get re-certified at least once a year. - The connector must be built and distributed for the enterprise version of the MuleSoft Software or MuleSoft Service. - All transactions for the connector must take place between the customer and the partner, not through MuleSoft. - The connector must be a fully functional, production-ready, enterprise-grade application and protocol connector. That is, it’s a connector that connects Mule with an external system. - Code must be ready for deployment into a continuous integration environment. Code should be compilable from the command line without the need to import private repositories. Credentials should be provided separately and be capable of getting set at compile time. In addition, sandbox credentials for your connector are required for certification. - The connector must only use third-party libraries that are open source or owned by you. No third-party, non-distributable libraries are accepted. - If a similar connector already exists on Anypoint Exchange, you can still submit your connector if it provides additional functionality that is superior to the existing connector. ## Providing Support Partners or developers are required to provide full support for each of their connectors on Anypoint Exchange. For support inquiries received by MuleSoft, MuleSoft provides a commercially reasonable level of first-tier telephone, web-based and/or email support directly to customers. MuleSoft takes and resolves any issues that are linked to commercial MuleSoft products. Any issues specific to the partner connector are escalated to the partner. - Partner responds to all support queries escalated from MuleSoft within one business day. - Partner makes a best effort to resolve technical support issues for customers within five business days. For help addressing issues related to partner-certified connectors, partners can contact `MSPartnerSupport@salesforce.com`. ## Pricing For Your Connector Partners or developers have sole discretion to decide whether to make your connectors available for free or for a fee. However, all connectors must be available at no cost within Anypoint Studio. While not mandatory, we recommend providing customers a trial of your connector for design and deployment. For more information, see the [Custom Module License](#see_also) link below.
CategoryDetails

Free

Connector is available for download and use for free to customers through Anypoint Exchange.

Paid

Paid connectors can be used in Mule apps at design time in Anypoint Studio. In deployment, the connector cannot run without a license. Customers need to purchase the connector directly from the partner and should manage the use of the paid connector using a generated license key provided by MuleSoft. The pricing of the connector is determined by the partner.

## License Terms Add the following legal terms before your license terms in `LICENSE.md`: **About MuleSoft Certified Connectors** MuleSoft Certified Connectors are developed by MuleSoft’s partners and developer community. These connectors have been reviewed and certified by MuleSoft. To purchase the Connector Name Connector, if required, or to receive assistance or support for it, contact _Partner Name_ directly at _Contact Information_. MuleSoft disclaims any support obligation for MuleSoft Certified Connectors. By installing this connector, you consent to MuleSoft sharing your contact information with the developer of this connector so that you can receive more information about it directly from the developer. _Your own license terms._ ## Export Compliance You may not use or otherwise export or re-export any MuleSoft Confidential Information or MuleSoft Documentation received from MuleSoft or your materials (including but not limited to Partner Documentation and Partner Connector) except as authorized by United States laws. In particular, but without limitation, MuleSoft Confidential Information, MuleSoft Documentation and your materials may not be exported or re-exported (a) into any U.S. embargoed countries or (b) to anyone on the U.S. Treasury Department’s list of Specially Designated Nationals or the U.S. Department of Commerce Denied Person’s List or Entity List. You represent and warrant that you are not located in any such country or on any such list. You also agree that you will not use or re-export any MuleSoft Confidential Information or MuleSoft Documentation and your materials for any purposes prohibited by United States law, including, without limitation, the development, design, manufacture or production of nuclear, chemical or biological weapons. You are solely responsible for ensuring that your provision of your materials to users through Anypoint Exchange comply with any and all regulations regarding export and import of technology in relevant jurisdictions. (Capitalized terms shall have same definition as in the Certified Connector Partner Agreement.) ## Development and Certification Tell MuleSoft about the connector you want to develop, and get certified by contacting [MuleSoft](mailto:isv@mulesoft.com). MuleSoft will contact you with further information. ## See Also - [Technical Guidelines for Connector Certifications](https://docs.mulesoft.com/mule-sdk/latest/certification-guidelines-for-connectors) - [Custom Module License](https://docs.mulesoft.com/mule-sdk/latest/license#custom-module-license) - [MuleSoft Trailblazer Community](https://trailhead.salesforce.com/trailblazer-community/neighborhoods/mulesoft) - [Contact MuleSoft](mailto:isv@mulesoft.com) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Accessing New Features from Legacy Modules > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The `ForwardCompatibilityHelper` interface enables modules using legacy `org.mule.runtime.extension.api` types to access features normally exposed as implicit parameters, such as distributed tracing, without breaking compatibility with earlier Mule runtime versions. For new modules built entirely with `org.mule.sdk.api`, consume these features directly through implicit parameters instead. `ForwardCompatibilityHelper` is defined in the `org.mule.sdk:mule-sdk-compatibility-api` artifact, in the `org.mule.sdk.compatibility.api.utils` package. This artifact supports both the legacy `org.mule.runtime.extension.api` types and the new `org.mule.sdk.api` types, which allows translations between them. ## Use the Compatibility Helper Use `ForwardCompatibilityHelper` if: - Your module uses legacy `org.mule.runtime.extension.api` types - You want to add support for new features (like distributed tracing) without forcing users to upgrade their Mule Runtime - You need to bridge between legacy API types and new SDK API types ### Inject ForwardCompatibilityHelper To use `ForwardCompatibilityHelper`, inject it as an `Optional` in your component: ```java import org.mule.sdk.compatibility.api.utils.ForwardCompatibilityHelper; import javax.inject.Inject; import java.util.Optional; public class MyOperations { @Inject private Optional forwardCompatibilityHelper; public void myOperation(CorrelationInfo correlationInfo) { if (forwardCompatibilityHelper.isPresent()) { // Use the helper on Mule 4.5.0+ DistributedTraceContextManager traceManager = forwardCompatibilityHelper.get().getDistributedTraceContextManager(correlationInfo); // Use traceManager... } else { // Fallback for older runtimes where the helper is not available } } } ``` The `Optional` field contains an instance on Mule 4.5.0 and later. On earlier Mule runtime versions, the `Optional` field is empty. ## Available Methods `ForwardCompatibilityHelper` provides the following methods to access the `DistributedTraceContextManager` for distributed tracing:
MethodUsage

getDistributedTraceContextManager(CorrelationInfo correlationInfo)

For use in operations. Accepts the legacy org.mule.runtime.extension.api.runtime.parameter.CorrelationInfo parameter type.

getDistributedTraceContextManager(org.mule.sdk.api.runtime.parameter.CorrelationInfo correlationInfo)

For use in operations. Accepts the new SDK API CorrelationInfo parameter type.

getDistributedTraceContextManager(SourceCallbackContext sourceCallbackContext)

For use in sources.

## See Also - [Forward Compatibility Patterns](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-patterns) - [Implicit Parameters](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-implicit-params) - [SDK Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, metadata, datasense, input, output, keys, type --- # Adding DataSense Support > For the full documentation index, see: https://docs.mulesoft.com/llms.txt DataSense is a Mule service that displays type metadata for the entities in a module. Although this feature is optional, MuleSoft strongly recommends that you use DataSense in your module so that it is easier for users of your module to use. Note that this documentation assumes that you are familiar with the architecture of connectors and with the concept of DataSense from the perspective of an end user. ## What is the Metadata of a Component? Integration developers often spend a lot of time trying to determine the input and output types for a given component (operation, sources, and so on) so they can transform the data at hand to connect the output of one component to the input of another. This often requires use of API documentation for each component to discover the parameters. This process is inefficient, fallible, and often frustrating. DataSense uses the metadata provided by these components to resolve all this information automatically and present it to your end user in design time, which drastically increases the speed of development. Note that unlike [Value Providers](https://docs.mulesoft.com/mule-sdk/latest/value-providers), which are meant to narrow down the possible values that a parameter of a known type can take, this feature is to discover dynamic types whose universe of possible values is not to be discovered by this feature. The term "types" refers to the `MetadataType` of an element. A `MetadataType` represents the kind and structure of a given element, like `StringType` or `NumberType` for elements that are basic string or numbers, `ArrayType` for collections, `ObjectType` for complex structures with nested elements (like a POJO or a JSON object), and `AnyType` for elements whose type is actually unknown and can be any of the other available types. See [DataWeave Types](https://docs.mulesoft.com/dataweave/latest/dataweave-types). ### Static Metadata Static metadata is metadata that is known at compile time and available from the types in the connector’s JAR file. Simple Java types are part of this static metadata as are custom POJOs defined by the developer in its model. What is relevant is that the structure is well known or can be introspected without depending on other parameters. ### Dynamic Metadata Dynamic metadata is metadata of a type with an unknown structure at compile time. It has to be resolved based on the configuration of the connector at design time. This metadata has different uses that span a range of cases. Uses span from cases where the type itself is known but its structure depends on the service configuration (for example, where the system has an account with fields that can be customized by the user, so its structure has to be discovered each time based on the user’s credentials) to cases where everything is dynamic and the structure described heavily depends on the configured parameters (for example, the described structure is the payload of a service operation that also depends on the definition of the service based on the user sandbox). To resolve dynamic metadata, the element has to be associated with a metadata resolver that knows how to obtain the desired type structure based on the current component configuration. This is explained in more detail in [MetaData Resolvers](#metadata_resolvers). ### Input Metadata Input metadata is the type resolution for the parameters of a component. Each parameter can provide either static or dynamic metadata that is isolated from the kind of metadata that other parameters of the same component expose. Only operations and sources can have parameters with dynamic metadata, while configurations and connections always have static metadata. ### Output Metadata The output of a component is bound to a static or dynamic resolution of its metadata. For example, a `void` operation has static metadata indicating that the output of that operation is of `VoidType`, while an operation that fetches a `User` profile from a remote service can dynamically describe the structure of the `User` type. As explained in [Structure](https://docs.mulesoft.com/mule-sdk/latest/module-structure), an operation can output a result that contains data for the payload and the attributes of the resulting message. The structure of these attributes is just as important as the structure of the payload. So, to improve the experience of your end user, metadata for each of them can be described independently, but dynamic metadata for attributes is always optional because some components might not produce attributes. Note that certain return types are forced to describe their metadata dynamically. This requirement is for consistency because it is necessary to consider the experience of the end user, and types that are too generic interfere with the quality of the user experience. ## Implementation Overview Implementing dynamic DataSense support starts with defining the type of metadata you want to provide for a given element. Once that is defined, you can make use of a combination of annotations and custom implementations of different metadata resolvers, such as `@MetadataKeyId(BucketKeysResolver.class)`. What to use depends on your use case. The next sections provide detailed information on implementing the cases described above. ### Metadata Keys Parameter To describe a dynamic metadata structure, you need to know what type has to be represented. This type reference is done by defining a `@MetadataKeyId` parameter in the operation that will hold the ID of the type to be described. This means, for example, that if your operation can save a generic record to Amazon S3, but you want to provide a better design-time experience by describing each supported type structure (for example, `Account` or `Organization`), then one of your operation parameters will be a type reference containing either the id `Account` or the id `Organization`. This parameter will be used to describe either the Account structure or the Organization structure for the record to be saved into the bucket, depending on what the end user decides to use. Having a `@MetadataKeyId` parameter is not always required because you might have only one structure (and not multiple) to define that vary dynamically based on the configuration. For example, you might have a `User` entity whose structure changes based on whether the connection credentials are for an administrator or not. The purpose of including a MetadataKey parameter is to use the value given to it to resolve the metadata of output and inputs of the component for which the key is defined. This means that you should not define a MetadataKey without any MetadataResolver for inputs, output, or Attributes. SDK versions 1.3 and later fail to compile connectors that contain a Component that has a MetadataKey parameter but does not have an Output Resolver or Input Resolvers. ### Metadata Resolvers Whenever you want to obtain a dynamic element for DataSense, you implement a metadata resolver. There are many different kinds of resolvers, each with its own responsibility and use cases (explained later), but they all share these main concepts: - Category Name: Name of the group that relates different metadata resolvers so they can work together. - Resolver Name: Name that uniquely identifies a given metadata resolver. Different resolvers can belong to the same `category` but _must_ have different resolver names. - Metadata Context: Provides access to all the configuration and connection elements used during the metadata fetch invocation, along with a set of utility components, such as `TypeLoader` and `TypeBuilder`. It is important to always use the implementations provided by the context when creating a dynamic type. --- product: Mule SDK version: 1.13 is-latest-version: true --- # Advanced Parameter Handling > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Features in the SDK can improve the quality of your module by adding UI and UX tuning, enabling extension points so your module can interact with elements of other modules, and providing implementation enhancements to avoid boilerplate code. ## UI and UX Tuning - [Special Parameters](https://docs.mulesoft.com/mule-sdk/latest/special-parameters) - [Customizing the Parameters UI](https://docs.mulesoft.com/mule-sdk/latest/parameter-layout) - [Customizing the Parameters DSL](https://docs.mulesoft.com/mule-sdk/latest/parameters-dsl) - [Exclusive Optional](https://docs.mulesoft.com/mule-sdk/latest/exclusive-optionals) - [Value Providers](https://docs.mulesoft.com/mule-sdk/latest/value-providers) ## Extension Points and Modules Reuse - [Importing Types From Other Modules](https://docs.mulesoft.com/mule-sdk/latest/imported-types) - [Using Generic Parameters For Extended Behavior](https://docs.mulesoft.com/mule-sdk/latest/subtypes-mapping) - [Stereotypes As A Way Of Referencing Elements By Kind](https://docs.mulesoft.com/mule-sdk/latest/stereotypes) ## Implementation Enhancers - [NullSafe](https://docs.mulesoft.com/mule-sdk/latest/null-safe) - [Mule Services Injection](https://docs.mulesoft.com/mule-sdk/latest/mule-service-injection) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Advanced Sources Settings > For the full documentation index, see: https://docs.mulesoft.com/llms.txt ## @ClusterSupport **Available since version 1.1** You can use this annotation to indicate how the Source behaves when the Mule Runtime is running in Cluster mode, for example: ```java @ClusterSupport(DEFAULT_PRIMARY_NODE_ONLY) public class JmsListener extends Source { // ... } ``` The `@ClusterSupport` annotation takes a parameter of type `SourceClusterSupport`, which can take the following values: - NOT\_SUPPORTED: The source does not support cluster mode, so it only runs on the primary node. If the primary node crashes, a new primary node will be designated, and the source will be started on it. - DEFAULT\_ALL\_NODES: Runs on all the cluster nodes by default, but the user can choose to only run on the primary one. - DEFAULT\_PRIMARY\_NODE\_ONLY: Only runs on the primary node by default, but the user can choose to run on all the nodes. When the selected value is other than `NOT_SUPPORTED`, a parameter named `primaryNodeOnly` is automatically added so you can configure whether the source should run in all nodes or just the primary ones. > [!TIP] Notice that when this parameter is added, it will be optional, and its default value will > depend on the selected cluster support mode. --- product: Mule SDK version: 1.13 is-latest-version: true --- # Authentication Handler > For the full documentation index, see: https://docs.mulesoft.com/llms.txt This handler allows you to configure the current context’s authentication, which is used for encryption and inbound authentication, based on a given request. ## How to Use the Authentication Handler When you add a parameter of type `AuthenticationHandler` to your operation, the Authentication Handler instance from the current context will be injected in runtime. You can use this instance to check the current authentication (if there is any) and set a new one. The Authentication Handler interface provides the following methods: - `createCredentialsBuilder` returns a builder that is used to create a `Credentials` instance. - `createAuthentication`, when given an instance of `Credentials`, creates an `Authentication` instance. - `setAuthentication`, when given an `Authentication`, sets that value as the current context’s authentication. Optionally, you can also provide a List of security providers (the names) that will be added to the `SecurityManager`. - `getAuthentication` returns the `Authentication` from the current context. There may be no `Authentication` for the current context. ## Usage Example Imagine that after receiving an HTTP Request, you want to authenticate and store the credentials on the current context’s Authentication Handler. You can use the `basic-security-filter` operation from the HTTP Connector for this use case. This simplified example shows how the `basic-security-filter` is implemented: ```java public class HttpOperations { /** * Authenticates received HTTP requests. Must be used after a listener component. */ @Throws(BasicSecurityErrorTypeProvider.class) public void basicSecurityFilter(@ParameterGroup(name = "Security Filter") HttpBasicAuthenticationFilter filter, AuthenticationHandler authenticationHandler) { (1) try { filter.authenticate(authenticationHandler); } catch (BasicUnauthorisedException e) { throw new ModuleException(BASIC_AUTHENTICATION, e); } catch (SecurityProviderNotFoundException | SecurityException | UnknownAuthenticationTypeException e) { throw new ModuleException(SERVER_SECURITY, e); } } // ... } ```
1The operation has an AuthenticationHandler parameter that will be injected in runtime.
```java public class HttpBasicAuthenticationFilter { @Parameter private String realm; @Parameter @Optional @NullSafe private List securityProviders; @Parameter @Optional(defaultValue = "#[attributes]") HttpRequestAttributes attributes; (1) public void authenticate(AuthenticationHandler authenticationHandler) throws SecurityException, SecurityProviderNotFoundException, UnknownAuthenticationTypeException { String header = attributes.getHeaders().get("Authorization".toLowerCase()); if ((header != null) && header.startsWith("Basic ")) { String username = retrieveUsername(header); String password = retrievePassword(header); Credentials credentials = authenticationHandler.createCredentialsBuilder() (2) .withUsername(username) .withPassword(password.toCharArray()) .build(); try { Authentication authentication = authenticationHandler.createAuthentication(credentials); (3) authenticationHandler.setAuthentication(securityProviders, authentication); (4) } catch (UnauthorisedException e) { (5) throw new BasicUnauthorisedException(authFailedForUser(username), e); } } else if (header == null) { throw new BasicUnauthorisedException(null, "HTTP basic authentication", "HTTP listener"); } else { throw new UnsupportedAuthenticationSchemeException("Http Basic filter doesn't know how to handle header " + header)); } } } ```
1HttpRequestAttributes from where the username and password will be taken.
2Credentials are created with the information taken from the request.
3An Authentication is created based on Credentials.
4The authentication information is checked against the securityProviders and set to the handler.
5When the authentication information is invalid, and an exception is thrown.
If there is another operation in the flow that has an Authentication Handler as a parameter, the instance that is injected will be the same as the one that is handled on the `basic-authentication-filter` operation. This way, by using the `getAuthentication` method, you will get the `Authentication` with the credentials from the HTTP Request. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, grant, oauth --- # Authorization Code > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Authorization Code is a grant type that allows an application to act on behalf of a user without the need for that user to share their actual credentials. This grant type allows an application to impersonate a user. ## Develop an Authorization Code-enabled Connector OAuth support is added at the `ConnectionProvider` level through the `@AuthorizationCode` annotation. Here is an oversimplified `ConnectionProvider` example for the Salesforce connector: ```java @AuthorizationCode( authorizationUrl = "https://login.salesforce.com/services/oauth2/authorize", accessTokenUrl = "https://login.salesforce.com/services/oauth2/token") public class SalesforceOAuthConnectionProvider implements ConnectionProvider { @Parameter @Optional(defaultValue = "34.0") private Double apiVersion; /** * Tailors the login page to the user's device type. */ @OAuthParameter private String display; /** * Avoid interacting with the user */ @OAuthParameter @Optional(defaultValue = "false") private boolean immediate; /** * Specifies how the authorization server prompts the user for reauthentication and reapproval */ @OAuthParameter @Optional(defaultValue = "true") private boolean prompt; @OAuthCallbackValue(expression = "#[payload.instance_url]") private String instanceId; @OAuthCallbackValue(expression = "#[payload.id]") private String userId; private AuthorizationCodeState state; @Override public SalesforceClient connect() throws ConnectionException { if (state.getAccessToken() == null) { throw new SalesforceException(MessageFormat.format(COULD_NOT_EXTRACT_FIELD, "accessToken")); } if (instanceId == null) { throw new SalesforceException(MessageFormat.format(COULD_NOT_EXTRACT_FIELD, "instanceId")); } return new SalesforceClient(state, instanceId, apiVersion); (1) } public void disconnect(SalesforceClient connection) { connection.close(); } @Override public ConnectionValidationResult validate(SalesforceClient connection) { return success(); } } ```
1The connection receives the state object and not the access token. Access tokens can be refreshed or unauthorized at any time, which can make the connection useless, thus you must always query the state object whenever the access token is needed. For more information, refer to Always Query the State Object on Each Request.
The class implements `ConnectionProvider`, just like other connections in the SDK. Operations that are authenticated through the object operate over the `SalesforceClient` object that the provider returns. This is an important design consideration because it permits you to decouple the operation from the authentication method that is used because the connector could define another `ConnectionProvider` that uses plain old, basic authentication, and all the operations would remain fully compatible. ## @AuthorizationCode This annotation indicates that this connection provider requires an OAuth dance using the Authorization Code grant type. The annotation has the following attributes: ```java public @interface AuthorizationCode { /** * @return The Url of the endpoint which provides the access tokens */ String accessTokenUrl(); /** * @return The url of the authorization endpoint which starts the OAuth dance */ String authorizationUrl(); /** * @return Expression to be used on the response of {@link #accessTokenUrl()} to extract the access token */ String accessTokenExpr() default "#[payload.access_token]"; /** * @return Expression to be used on the response of {@link #accessTokenUrl()} to extract the access token expiration */ String expirationExpr() default "#[payload.expires_in]"; /** * @return Expression to be used on the response of {@link #accessTokenUrl()} to extract the refresh token */ String refreshTokenExpr() default "#[payload.refresh_token]"; /** * @return The default set of scopes to be requested, as a comma separated list. Empty string means no default scopes. */ String defaultScopes() default ""; } ``` ## Connection Management Strategy The provider in the example above doesn’t implement any specialization of the `ConnectionProvider` interface, which means that the OAuth mechanism can be combined with the other connection management strategies. The connection objects can be pooled, cached, or created from scratch each time, depending on which interface is used (`PoolingConnectionProvider`, `CachedConnectionProvider`, `ConnectionProvider`, and so on). For more information on connectivity management, see the [connectivity reference](https://docs.mulesoft.com/mule-sdk/latest/connections). Be aware of the semantics of using the vanilla (generic) `ConnectionProvider` interface in this scenario. In a regular, “non-oauth” connection provider, using the vanilla interface means that each time a component requires a connection, a new one will be created, and it will be destroyed when the component is finished. Although this will remain true for the OAuth case, it doesn’t mean that the OAuth dance will be performed again. New connection objects will be created, but the same access token will be reused as long as it remains valid. ## Regular Parameters Versus OAuth Parameters This `ConnectionProvider` can have parameters, just like any other connection provider. However, you have to distinguish regular parameters from the concept of `@OAuthParameter`. An `OAuthParameter` is included as a custom parameter while performing the OAuth dance. So, for example, while the `apiVersion` parameter is something that the connection provider will use to create the `SalesforceClient`, the immediate parameter is actually sent on the OAuth request to the service provider. From the module’s point of view, it is just another parameter for which the user will provide a value. You can combine these parameters with `@Optional`, `@Expression`, and all the other annotations you can use with the traditional `@Parameter` annotation. In the DSL, regular and oauth parameters appear together. The module’s end user shouldn’t notice any difference. ### Request Alias Some custom OAuth parameters might include characters that aren’t supported in Java. For example "Api-Key". Since you can’t use "-" as part of a field name, the `@OAuthParameter` annotation has an optional parameter called `requestAlias`, for example: ```java @OAuthParameter(requestAlias = "api-key") private String apiKey; ``` ## @OAuthCallbackValue Callback values are extracted from the response that the service provider sends through the OAuth callback. Although most service providers simply return standard items (such as access and refresh tokens, expiration information, and so on), some others return additional items. In the Salesforce case, they return user and instance ids. The annotation includes an expression that is applied on the response to extract the value. That value is then assigned to the field for the connection provider to use. When the `connect()`, `validate()` or `disconnect()` methods are invoked, the fields are set and usable. ## @AuthorizationCodeState Every `ConnectionProvider` annotated with `AuthorizationCode` **MUST** contain one (and only one) field of type `AuthorizationCodeState`. It is a simple immutable POJO that contains information regarding the outcome of the OAuth dance. It contains the following information: ```java public interface AuthorizationCodeState { /** * @return The obtained access token */ String getAccessToken(); /** * @return The obtained refresh token */ Optional getRefreshToken(); /** * @return The id of the user that was authenticated */ String getResourceOwnerId(); /** * @return The access token's expiration. The actual format of it depends on the OAuth provider */ Optional getExpiresIn(); /** * @return The OAuth state that was originally sent */ Optional getState(); /** * @return The url of the authorization endpoint that was used in the authorization process */ String getAuthorizationUrl(); /** * @return The url of the access token endpoint that was used in the authorization process */ String getAccessTokenUrl(); /** * @return The OAuth consumer key that was used in the authorization process */ String getConsumerKey(); /** * @return The OAuth consumer secret that was used in the authorization process */ String getConsumerSecret(); /** * @return The external callback url that the user configured or {@link Optional#empty()} if none was provided */ Optional getExternalCallbackUrl(); /** * Customizes the placement of the client credentials in the request. * * @Since 1.4 * * @return the selected {@link CredentialsPlacement}. Defaults to {@link CredentialsPlacement#BODY}. */ CredentialsPlacement credentialsPlacement() default BODY; /** * Whether the redirect_uri parameter should be included in the refresh token request. Defaults to {@code true} * * @since 1.4.0 */ boolean includeRedirectUriInRefreshTokenRequest() default true; } ``` Through this object, the provider gains access to the `accessToken` and other standard information that was obtained during the OAuth dance. Returning to the original Salesforce example, you can see how the `connect()` method makes use of this POJO to create the client. ## Configure an Authorization Code Connector OAuth-enabled connectors automatically support the Authorization Code grant type. This section describes the parameters and how to configure them. ### Synthetic Parameters In addition to the parameters defined in the `ConnectionProvider`, the SDK automatically injects some standard OAuth parameters for Authorization Code.
Parameter NameRequiredExpressionsDefault ValueDescription

consumerKey

YES

SUPPORTED

N.A.

The OAuth consumer key as registered with the service provider.

consumerSecret

YES

NOT_SUPPORTED

N.A.

The OAuth consumer secret registered with the service provider.

authorizationUrl

YES

SUPPORTED

N.A.

The URL of the authorization endpoint to start the OAuth dance.

accessTokenUrl

YES

SUPPORTED

N.A.

The URL to request the access token after the OAuth dance.

scopes

NO

SUPPORTED

N.A.

The OAuth scopes to request during authorization. If not provided, defaults to those in the annotation.

resourceOwnerId

NO

SUPPORTED

N.A.

The ID of the authenticated user.

before

NO

SUPPORTED

N.A.

A flow to execute before authentication.

after

NO

SUPPORTED

N.A.

A flow to execute after authentication.

authServerTlsContext

NO

NOT_SUPPORTED

N.A.

A reference to the TLS context configuration used for the authorization server.

authServerProxyConfig

NO

NOT_SUPPORTED

N.A.

A reference to the proxy configuration used for the authorization server.

### About the Use of Expressions Many of the synthetic parameters accept expressions. Using expressions allows the configuration to be dynamic, just like with regular parameters. ### OAuth Connection DSL The following example shows a basic Authorization Code connection configuration. ```java ``` ### OAuth Connection DSL with TLS Context Configuration This example shows how to configure a TLS context for the Authorization Code flow. ```java ``` ### OAuth Connection DSL with Proxy Configuration This example shows how to set a proxy for the Authorization Code flow. ```java ``` ### Custom ObjectStore Configuration Access tokens are stored in an Object Store. By default, the SDK uses the app’s default Object Store, but you can define a custom one. ```java ``` ## Next Step [Configuring an OAuth enabled module](https://docs.mulesoft.com/mule-sdk/latest/oauth-configuring) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, streaming, binary --- # Binary Streaming > For the full documentation index, see: https://docs.mulesoft.com/llms.txt In binary streaming use cases, the stream is a flux of bytes that are treated in a generic way regardless of what they represent. Examples include the response of an HTTP request or retrieving and writing a file in a FTP server. The SDK supports this abstraction by using the Java `InputStream` class. ## Binary Streaming in Operations Operations can receive stream parameters, return streams, or do both. The Write operation in the File connector is an example of an operation that operates over a stream: ```java public class FileOperations { public void write(@Content InputStream content, String path) { // do the writing } } ``` If you have parameter of type `InputStream`, the SDK will provide all the necessary support for using Mule’s streaming capabilities. The `content` parameter in the example above will always be assigned to a repeatable stream that points to the beginning of the stream, treating it as if this is the first time the stream is getting read, whether that is true or not. Another possibility is that your operation _returns_ a binary stream instead, like the Read operation does: ```java public class FileOperations { public InputStream read(String path) { // do the reading } } ``` If you return an `InputStream`, the SDK will automatically add all the necessary magic to make that stream repeatable. ## Configuring Streaming By allowing elements such as ``, `` and ``, the SDK automatically adds support for configuring how streaming should behave. Here is an example that configures custom file store streaming for the Read example above: ```xml ``` ## Stream from Existing Files Available since version 1.3. When the `` strategy is used, a file temporarily holds information that doesn’t fit into the in-memory buffer. There are particular cases in which using this strategy is inefficient and leads to suboptimal I/O and disk space requirements. The most common cases of this include: Scenario 1 You are building an Operation or Source that returns the content of a file. The component that you are developing opens a file and returns an InputStream that contains the file content. If the file is bigger than the memory buffer, the starts building a temporary file with the stream content. That file becomes an exact duplicate of the original file that was already opened. Scenario 2 In a variation of Scenario 1, an Operation or Source obtains an InputStream and needs to pre-process it _before_ it is returned. Because the original stream is not yet pushed to the flow, it hasn’t been made repeatable. As a result, the source needs to implement its own buffering by storing the processed contents into a temporary file as it processes the original stream. When pre-processing finishes, the source then pushes the contents of the file, going back to Scenario 1. These two scenarios articulate a need to get a repeatable stream from the content of an existing file, without the repeatable streaming framework creating a new file of its own, since the contents already originate from a file. In these scenarios, use the `ImmutableFileRepeatableInputStream` class, which is similar to the standard `java.io.FileInputStream` but prevents Mule runtime engine (Mule) from generating a new buffering file and instructs Mule to reuse the existing file. For example, this class can be used in an operation: ```java @MediaType(value = ANY, strict = false) public InputStream read(String path) { File file = new File(path); if (!file.exists()) { // throw ModuleException } return new ImmutableFileRepeatableInputStream(file); } ``` An important aspect of working with files of this nature is understanding when to delete them. Depending on the use case, you might need to delete them through your own code. In simpler cases, you can delete files when the stream is closed. If you want to delete the file when the stream is closed, you can use the following convenience constructor, which enables you to instruct Mule to automatically delete the file when the stream is closed: `new ImmutableFileRepeatableInputStream(file, true);` > [!IMPORTANT] The file must be immutable for you to use this feature. That means that once the > `ImmutableFileRepeatableInputStream` instance is created, the contents of that file cannot change, > at least until `ImmutableFileRepeatableInputStream` is closed. Changing the file’s content while > the flow is reading it can lead to inconsistencies, dirty reads, and other types of race > conditions. ## See Also - [Streaming](https://docs.mulesoft.com/mule-sdk/latest/streaming) - [Repeatable Streams](https://docs.mulesoft.com/mule-runtime/latest/streaming-about#repeatable-streams) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, create, new, project, getting, started, version --- # Choosing the SDK Version > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Starting with `mule-java-extension-parent` version 1.11.0, the SDK supports forward compatibility. The SDK version is now decoupled from the minimum Mule version your module requires, so you can always use the latest SDK version while controlling runtime compatibility independently. For new modules, use the latest version of `mule-java-extension-parent`: ```xml org.mule.extensions mule-java-extension-parent 1.11.0 ``` With forward compatibility, you can: - Compile against the latest SDK APIs - Control your module’s minimum Mule version through the `min.mule.version` property - Use new SDK features for users on recent Mule runtime versions while maintaining compatibility with earlier Mule runtime versions. See [SDK Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility) for detailed information on how to configure and use this feature. ## See Also - [SDK Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility) - [Setting Up Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-setup) - [Extensions API (Deprecated)](https://docs.mulesoft.com/mule-sdk/latest/extensions-api-deprecated) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, grant, oauth --- # Client Credentials > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Available since version 1.3. Unlike the Authorization Code grant type, which focuses on authorizing an application that wants to act on a user’s behalf, the Client Credentials grant type focuses on authorizing an application to act on its own behalf. This grant type is better suited for machine-to-machine communication because it doesn’t require human intervention. ## Develop a Client Credentials-Enabled Connector You can add Client Credentials support at the `ConnectionProvider` level through the `@ClientCredentials` annotation. Here is an oversimplified `ConnectionProvider` example for Anypoint Connector for Salesforce (Salesforce Connector): ```java @ClientCredentials(tokenUrl = "https://login.salesforce.com/services/oauth2/authorize") public class SalesforceOAuthConnectionProvider implements ConnectionProvider { @Parameter @Optional(defaultValue = "34.0") private Double apiVersion; /** * Tailors the login page to the user's device type. */ @OAuthParameter private String display; /** * Avoid interacting with the user */ @OAuthParameter @Optional(defaultValue = "false") private boolean immediate; /** * Specifies how the authorization server prompts the user for reauthentication and reapproval */ @OAuthParameter @Optional(defaultValue = "true") private boolean prompt; @OAuthCallbackValue(expression = "#[payload.instance_url]") private String instanceId; @OAuthCallbackValue(expression = "#[payload.id]") private String userId; private ClientCredentialsState state; @Override public SalesforceClient connect() throws ConnectionException { if (state.getAccessToken() == null) { throw new SalesforceException(MessageFormat.format(COULD_NOT_EXTRACT_FIELD, "accessToken")); } if (instanceId == null) { throw new SalesforceException(MessageFormat.format(COULD_NOT_EXTRACT_FIELD, "instanceId")); } return new SalesforceClient(state.getAccessToken(), instanceId, apiVersion); } public void disconnect(SalesforceClient connection) { connection.close(); } @Override public ConnectionValidationResult validate(SalesforceClient connection) { return success(); } } ``` The class implements `ConnectionProvider`, just like other connections in the SDK. Operations that are authenticated through the object operate over the `SalesforceClient` object that the provider returns. This is an important design consideration because it permits you to decouple the operation from the authentication method that is used because the connector could define another `ConnectionProvider` that uses basic authentication, and all the operations remain fully compatible. ### @ClientCredentials This annotation indicates that this connection provider requires an OAuth dance using the Client Credentials grant type. The annotation has the following attributes: ```java public @interface ClientCredentials { /** * @return The Url of the endpoint which provides the access tokens */ String tokenUrl(); /** * @return Expression to be used on the response of {@link #tokenUrl()} to extract the access token */ String accessTokenExpr() default "#[payload.access_token]"; /** * @return Expression to be used on the response of {@link #tokenUrl()} to extract the access token expiration */ String expirationExpr() default "#[payload.expires_in]"; /** * @return The default set of scopes to be requested, as a comma separated list. Empty string means no default scopes. */ String defaultScopes() default ""; /** * Allows to customize the placement that the client credentials will have in the request. * * @return the selected {@link CredentialsPlacement}. Defaults to {@link CredentialsPlacement#BASIC_AUTH_HEADER} */ CredentialsPlacement credentialsPlacement() default BASIC_AUTH_HEADER; } ``` ### Connection Management Strategy The provider in the `@ClientCredentials` example doesn’t implement any specialization of the ConnectionProvider interface, which means that the OAuth mechanism can be combined with the other connection management strategies. The connection objects can be pooled, cached, or created anew each time, depending on which interface is used (for example, PoolingConnectionProvider, CachedConnectionProvider, ConnectionProvider, and so on). For more information about connectivity management, see the connectivity reference. Be aware of the semantics of using the unspecialized (for example, not PoolingConnectionProvider, CachedConnectionProvider) ConnectionProvider interface in this scenario. In a regular, “non-OAuth” connection provider, using the unspecialized interface means that each time a component requires a connection, a new one is created, and it is destroyed when the component is finished. Although this will remain true for the OAuth case, it doesn’t mean that the OAuth dance is performed again. New connection objects are created, but the same access token is reused as long as it remains valid. ### Regular Parameters versus OAuth Parameters This ConnectionProvider can have parameters, just like any other connection provider. However, you must distinguish regular parameters from the concept of `@OAuthParameter`. An OAuthParameter is included as a custom parameter while performing the OAuth dance. So, for example, while the `apiVersion` parameter is something that the connection provider uses to create the SalesforceClient, the immediate parameter is actually sent on the OAuth request to the service provider. From the module’s point of view, it is just another parameter for which the user provides a value. You can combine these parameters with `@Optional`, `@Expression`, and all the other annotations you can use with the traditional `@Parameter` annotation. In the DSL, regular and OAuth parameters appear together. The module’s end user shouldn’t notice any difference. ## requestAlias Parameter Some custom OAuth parameters include characters that aren’t supported in Java, for example `Api-Key`. Since you can’t use `-` as part of a field name, the @OAuthParameter annotation has an optional parameter called `requestAlias`: ```java @OAuthParameter(requestAlias = "api-key") private String apiKey; ``` ### @OAuthCallbackValue Annotation Callback values are extracted from the response that the service provider sends through the OAuth callback. Although most service providers return only standard items (such as access and refresh tokens, expiration information, and so on), others return additional items (such as user and instance IDs, in the case of Salesforce). The annotation includes an expression that is applied to the response to extract the value. That value is then assigned to the field for the connection provider to use. When the connect(), validate(), or disconnect() methods are invoked, the fields are set and usable. ### @ClientCredentialsState Every ConnectionProvider annotated with `@ClientCredentials` _must_ contain one (and only one) field of type ClientCredentialsState. It is a simple immutable POJO that contains information regarding the outcome of the OAuth dance. It contains the following information: ```java public interface ClientCredentialsState { /** * @return The obtained access token */ String getAccessToken(); /** * @return The access token's expiration. The actual format of it depends on the OAuth provider */ Optional getExpiresIn(); } ``` Through this object, the provider gains access to the accessToken and other standard information that was obtained during the OAuth dance. The original Salesforce example shows how the connect() method uses this POJO to create the client. ## Configure a Client Credentials Connector An important aspect of OAuth-enabled connectors or modules is how to use them. As much as the SDK does to hide the complexities of the OAuth protocol, things like the OAuth dance are unavoidable. The SDK’s approach to this problem is to standardize the experience of all OAuth modules to keep simplifying the user experience. This section discusses the steps a user must take to use the module. ### Synthetic Parameters In addition to everything explicitly defined in the ConnectionProvider, the SDK automatically adds some extra parameters and injects the proper behavior for them.
Parameter NameRequiredExpressionsDefault ValueDescription

clientId

YES

SUPPORTED

N.A.

The OAuth clientId value as registered with the service provider.

clientSecret

YES

NOT_SUPPORTED

N.A.

The OAuth clientSecret value as registered with the service provider.

tokenUrl

NO

SUPPORTED

Value provided in the @ClientCredentials annotation.

The service provider’s token endpoint URL.

scopes

NO

SUPPORTED

The value provided in the @ClientCredentials annotation.

The OAuth scopes to be requested during the dance. If not provided, this value defaults to those in the annotation.

objectStore

NO

NOT_SUPPORTED

N.A.

A reference to the ObjectStore used for storing each resource owner ID’s data. If not specified, the runtime automatically provisions a default one.

authServerProxyConfig

NO

NOT_SUPPORTED

N.A.

A reference to the proxy configuration used to perform the validation.

authServerTlsContext

NO

NOT_SUPPORTED

N.A.

A reference to the TLS context configuration used to perform the validation, with the trust store configured.

### About the Use of Expressions The synthetic parameters table identifies many of the synthetic parameters that accept expressions. Using expressions there has the same effect as using expressions in a regular parameter: it turns the configuration dynamic. ### OAuth Connection DSL The following example shows generated DSL: ```java ``` Regular and OAuth parameters are all shown at the connection provider level, as they would be in any other provider. The parameters related to the Client Credentials grant type are placed on a child element called . The parameters related to ObjectStore are placed in a child element called . ### OAuth Connection DSL with TLS Context Configuration This example shows how to set a TLS context for OAuth client credentials authorization. ```java ``` ### OAuth Connection DSL with Proxy Configuration This example shows how to set a proxy configuration for OAuth client credentials authorization. ```java ``` ### Configuring a Custom ObjectStore The obtained access tokens are stored in an Object Store. By default, the SDK stores them in the app’s default Object Store, but users can define their own custom Object Store, as shown in this example. ```java ``` ## Refresh an Expired Client Credentials Access Token Most access tokens have a limited lifespan: typically about 30 to 60 minutes after being issued. This is why most providers also supply a refresh token, which eliminates the need to perform the OAuth dance again, although this isn’t a standard that is strictly enforced by providers. There isn’t an enforced standard for how the SDK automatically detects an expired token and replaces it. Because different APIs communicate that in different ways, the `AccessTokenExpiredException` exception exists, shown below: ```java public void someOperation(@Connection SalesforceRestClient client) { Response response = client.performSomeOperation(); if (response.getStatusCode() == 401) { throw new AccessTokenExpiredException(); } } ``` This example uses an imaginary REST client to perform an operation: - It assumes that this REST client was created through a ConnectionProvider annotated with `@ClientCredentials`. - It performs the operation and receives a Response object that contains information about the HTTP call. - The example assumes that a status code of 401 (`UNAUTHORIZED`) means that the token is expired. - It throws the AccessTokenExpiredException. When the SDK detects this exception, it automatically performs the refresh dance and retries the operation with a new access token. As noted earlier, different APIs notify you of an access token expiration in different ways. Some give you clients that throw exceptions, while others give you custom messages. You should research the remote API to determine the best way to account for this. ### Invalidating Access Tokens Multitenancy implies the ability to invalidate the access token of a particular resourceOwnerId, causing the associated token information to be deleted. To invalidate an access token, the SDK automatically adds an operation called `unauthorize` to every OAuth-enabled connector or module. Given the following example, you might invalidate the token obtained like this: ```java ``` Note that this operation doesn’t invalidate the token on the service provider. It removes the service provider from Mule’s caches and Object Stores. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, config, configuration, override, parameter --- # Configuration Overrides > For the full documentation index, see: https://docs.mulesoft.com/llms.txt A parameter in your configuration that serves as a _global default_ specifies a default value or behavior for other operations and sources to use. For components in an app to override the value of that parameter, you can use `@ConfigOverride`. ## What is a Configuration Override? This feature binds a parameter from a source or operation to a parameter in a configuration. When a parameter is annotated with `@ConfigOverride`, but a value is not specified for it through the DSL, the configuration value for the parameter with _exactly_ the same name will be injected on it. The `@ConfigOverride` annotation can be applied to an argument of an operation method or to a field of a source. However, it cannot be used on configurations or connections. For example, assume that a parameter is declared in your configuration like this: ```java @Parameter @Optional(defaultValue = "10") int maxRetries; ``` Also assume that the above configuration is used in an operation: ```java public void request(@Config ConfigType config, @ConfigOverride int maxRetries) // ... } ``` The `maxRetries` parameter in the `request` operation above can receive its value in a few ways: - The default value specified in the `maxRetries` parameter configuration is used when the operation and configuration (shown in the XML below) _do not_ provide a value for `maxRetries`. ```xml ``` In the operation, the `maxRetries` parameter will take the default value of `10`. - The value provided for `maxRetries` in the configuration (shown below) is used when the value for the parameter is not specified in the operation but is explicit in the configuration. ```xml ``` In the operation, `maxRetries` will take the value `2`. - The value provided for the `maxRetries` parameter in the operation is used when this parameter is explicitly added to the DSL. ```xml ``` In the operation, the `maxRetries` parameter takes the value `5`. Note that this feature does not change the values set in the configuration. It only injects the configuration values into the parameters or retains the specified values. ## Configuration Override Requirements There are a couple of requirements for overriding configuration parameters: - The name and type of the parameter in the configuration must be the same as the one that is annotated with `@ConfigOverride`. - If an operation has a parameter that is annotated with `@ConfigOverride`, it must also have the corresponding configuration as parameter. - If a source has a field that is annotated with `@ConfigOverride`, it must also have the corresponding configuration as a field. ## Configuration Override Parameter within Parameter Groups This annotation can also be used to reference a `Parameter` that belongs to a `ParameterGroup` within a configuration, for example: ```java public class ConfigType{ @ParameterGroup(name = "Strategy") private RetryStrategy retryStrategy; // ... Parameter getters } ``` ```java public class RetryStrategy{ @Parameter private int maxRetries; @Parameter private String retriesExhaustedMessage; // ... Parameter getters } ``` This operation uses the `@ConfigOverride` that references `maxRetries` inside the `RetryStrategy` class: ```java public void request(@Config ConfigType config, @ConfigOverride int maxRetries) // ... } ``` ## Configuration Override with POJOs Your configuration can override any parameters that are POJOs. The content of the POJO parameter in the operation or source will be totally defined by the configuration or totally defined by the operation. This means that you cannot define a partially populated POJO and expect the rest of the POJO to have the configuration values injected. The logic to decide which values to use is the same as if it were a simple Java type. ## Configuration Parameter without a Default Value An optional configuration parameter does not have a default value, so its value will default to null for any operation or source parameter that references it with `@ConfigOverride`. The parameter in this example is declared in the configuration: ```java @Parameter @Optional private String retriesExhaustedMessage; ``` In the next example, an operation has configuration and configuration override parameters: ```java public void request(@Config ConfigType config, @ConfigOverride String retriesExhaustedMessage) // ... } ``` If `retriesExhaustedMessage` is not specified in the configuration and the operation, its value will be null. ## Configuration Override Example This section provides is a simplified example that uses `@ConfigOverride`. Here is the definition of the Configuration: ```java @Configuration(name = "config") @Operations({AmqpConsume.class, AmqpPublish.class, AmqpPublishConsume.class, }) public class AmqpConfig { @Parameter @Expression(NOT_SUPPORTED) @Optional(defaultValue = "*/*") private String contentType; @Expression(NOT_SUPPORTED) @ParameterGroup(name = "Consumer Config", showInDsl = true) private AmqpConsumerConfig consumerConfig; // ... All parameter getters } ``` The `AmqpConsumerConfig` class has parameters that will be referenced by the `@ConfigOverride` annotation in this example: ```java public final class AmqpConsumerConfig { @Parameter @Optional(defaultValue = "IMMEDIATE") @Expression(NOT_SUPPORTED) private AckMode ackMode; @Parameter @Optional(defaultValue = "false") @Expression(NOT_SUPPORTED) private boolean noLocal; @Parameter @Optional(defaultValue = "false") @Expression(NOT_SUPPORTED) private boolean exclusiveConsumers; @Parameter @Optional(defaultValue = "4") @Expression(NOT_SUPPORTED) private int numberOfConsumers; // ... All parameter getters } ``` This source has an `AmqpConfig` and parameters with the `ConfigOverride` annotation: ```java @Alias("listener") @EmitsResponse @MetadataScope(outputResolver = AmqpOutputResolver.class) public class AmqpListener extends Source { @Connection private ConnectionProvider connectionProvider; private AmqpTransactionalConnection connection; @Config private AmqpConfig config; (1) @Parameter private String queueName; @Parameter @ConfigOverride private AckMode ackMode; (2) @Parameter @ConfigOverride private int numberOfConsumers; (2) @Parameter @Optional private String consumerTag; @Override public void onStart(SourceCallback sourceCallback) throws MuleException { // ... } // ... @Override public void onStop() { // ... } // ... } ```
1Configuration that specifies the values that override the @ConfigOverride annotated parameters.
2Parameter that if not specified in the operation will inherit the configuration value of the parameter with exactly the same name. Note that the parameters with these names belong to a ParameterGroup.
This example shows how the override behaves: ```xml (1) (2) ```
1In the configuration, numberOfConsumers is explicitly set to 16, but ackMode takes the default value.
2In the source, numberOfConsumers is specified, so it will take the value 1. ackMode is not specified, so it will take the value from the config, which is IMMEDIATE.
--- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, security, oauth --- # Configuring an OAuth-Enabled Connector or Module > For the full documentation index, see: https://docs.mulesoft.com/llms.txt An important aspect of OAuth-enabled connectors or modules is how to use them. As much as the SDK does to hide the complexities of the OAuth protocol, things like the OAuth dance are unavoidable. The SDK’s approach to this problem is to standardize the experience of all OAuth modules to keep simplifying the user experience. This section discusses the steps a user must take to use the module. ## Synthetic Parameters In addition to everything explicitly defined in the Connection Provider, the SDK automatically adds some extra parameters and injects the proper behavior for them.
Parameter NameRequiredExpressionsDefault ValueDescription

resourceOwnerId

NO

SUPPORTED

N/A

To support multi-tenancy, users can specify the ID of the tenant that owns the associated access and refresh tokens. When performing the authorization dance, this value will provide the ID of the obtained token. When a component needs to obtain a new connection, it acts as a default owner ID to use.

consumerKey

YES

NOT_SUPPORTED

N/A

The OAuth consumerKey as registered with the service provider.

consumerSecret

YES

NOT_SUPPORTED

N/A

The OAuth consumerSecret as registered with the service provider.

authorizationUrl

NO

NOT_SUPPORTED

Value provided in the `@AuthorizationCode annotation.

The service provider’s authorization URL.

accessTokenUrl

NO

NOT_SUPPORTED

Value provided in the @AuthorizationCode annotation.

The service provider’s access token URL.

state

NO

REQUIRED

N/A

A String value that you send when the OAuth dance is initiated and that is returned back by the service provider with the token callback. It is used to correlate dance initiations with token callbacks. The most common use case for this attribute is multi-tenancy.

scope

NO

NOT_SUPPORTED

Value provided in the @AuthorizationCode annotation.

The OAuth scopes to be requested during the dance. If not provided, it will default to those in the annotation.

listenerConfig

YES

NOT_SUPPORTED

N/A

A reference to an <http:listener-config /> to be used in order to create the listener that will catch the access token callback endpoint.

callbackPath

YES

NOT_SUPPORTED

N/A

The path of the access token callback endpoint.

authorizePath

YES

NOT_SUPPORTED

N/A

The path of the local HTTP endpoint that triggers the OAuth dance.

externalCallbackUrl

NO

NOT_SUPPORTED

N/A

If the callback endpoint is behind a proxy or should be accessed through a non-direct URL, use this parameter to tell the OAuth provider the URL it should use to access the callback.

resourceOwnerId

YES

SUPPORTED

N/A

The resourceOwnerId that each component should use if it does not reference otherwise.

objectStore

NO

NOT_SUPPORTED

N/A

A reference to the Object Store to user for storing each resource owner ID’s data. If not specified, the runtime will automatically provision the default one.

before

NO

NOT_SUPPORTED

N/A

The name of a flow to be executed right before starting the OAuth dance (see Before Flow).

after

NO

NOT_SUPPORTED

N/A

The name of a flow to be executed right after an accessToken has been received (see After Flow)

## About the Use of Expressions The table above identifies many of the synthetic parameters that accept expressions. Using expressions there has the same effect as using expressions in a regular parameter: It will turn the configuration into a dynamic one. ## OAuth Connection DSL This is what the generated DSL looks like: ```xml ``` - Regular and OAuth parameters are all shown at the connection provider level, just like in any other provider. - The parameters related to the Authorization Code Grant type (`consumerKey`, `consumerSecret`, `authorizationUrl`, `accessTokenUrl`, `localAuthorizationHost`, `localAuthorizationPort`, `localAuthorizationPath`, `before`, `after`, `scope`, `defaultResourceOwnerId`) will be placed on a child element called ``. - The parameters related to the callback will be placed in a child element called ``. - The parameters related to object store will be placed in a child element called ``. ## Custom Logic Before and After the Dance End users often want to execute some random logic just before initiating the OAuth dance or right after it has been completed. Use cases include notifying an external system that a given owner ID has been successfully on-boarded, keeping activity logs, and so on. The authorization process is triggered by hitting an automatically created endpoint, so the `` child element has the `before` and `after` parameters. These optional parameters specify the name of a `` to be invoked before or after the OAuth dance. ### Before flow The before flow will be executed just before the OAuth dance is started. The payload of the event sent to that flow will be an instance of `AuthorizationCodeRequest`, which is an immutable POJO that looks like this: ```java public interface AuthCodeRequest { /** * @return The id of the user being authenticated */ String getResourceOwnerId(); /** * @return The scopes that were requested */ Optional getScopes(); /** * @return The OAuth state that was sent */ Optional getState(); /** * @return The external callback url that the user configured or {@link Optional#empty()} if none was provided */ Optional getExternalCallbackUrl(); } ``` In this flow, the user can perform any custom logic as needed. In particular, the user can set flow variables (see [After Flow](#after_flow)). ### After Flow The after flow is executed right after the access token has been received and stored. This flow is executed with an event that is equivalent to what came out of the before flow (or a blank event if no before flow was defined), except for the payload, which is replaced by the same `AuthorizationCodeState` object that is injected in the `ConnectionProvider`. However, any variables previously set are still there (or will be empty if no before flow was defined). ## Configuring a Custom ObjectStore The obtained access tokens are stored in an `ObjectStore`. By default, the SDK will store them in the apps’s default store, but users can define their own custom one, for example: ```xml (2) ```
1Define your custom store.
2Reference it on your module’s config.
## Next Step [Performing the OAuth Dance with an OAuth-Enabled Connector or Module](https://docs.mulesoft.com/mule-sdk/latest/oauth-dance) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Content Parameters > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Consider the following operation (pseudocode): ```xml ``` Notice that the parameters that compose operations often play different roles. ## Behavior Parameters These parameters manage settings that determine how the operation is going to behave. In the example above, the `overwrite` parameter configures the action to take if the file already exists. The `path` parameter indicates where to write the content. Both parameters configure the operation, but because this is a Write operation, they do not represent the main concept, which is the data to be written. Some operations are exclusively formed by behavior parameters, for example: ```xml ``` Both parameters are behavioral because the operation does not take any content. The content is in the file that is copied. ## Content Parameters If you consider the explanation of behavior parameters above, the definition of a content parameter is obvious. In the `file:write` example above, the content is a `@Content` parameter. Content parameters have the following characteristics: - They must accept expressions. Both SUPPORTS\_EXPRESSIONS and EXPRESSION\_REQUIRED are supported, but compilation will fail if `@Expression(NOT_SUPPORTED)` is used. - Each content parameter can embed its own DataWeave script to generate the content. - Content parameters always translate to the DSL as a text element, precisely to enable the embedded DataWeave script. Consider the `file:write` operation again, this time with content parameters. Suppose that you are using this operation in a flow, and the message payload is JSON that you want to store as XML. ```xml ``` In the module’s code, the `content` parameter is marked with the `@Content` annotation: ```java public void write(String path, @Content InputStream content) { // write code } ``` ## Primary Content Parameter In some cases, an operation has many content parameters, for example: ```xml #[body..] #[uri-params …] #[you get the picture..] ``` As you can see, you can have as many content parameters as you want, which means that the `@Content` annotation can be used on more than one method argument. However, the `body` parameter is still more important than the rest. Although headers are also part of the content sent in the HTTP request, those headers are complementary to the body that is sent. So, when an operation has more than one content parameter, one of them has to be marked as the primary content by using `@Content(primary = true)`. The primary content parameter has all of the same characteristics as the regular content parameters, plus these: - They are automatically made optional. - They automatically default to `#[payload]`. These two features are automatically added to the parameter by the runtime. > [!TIP] Going back to the `file:write` example in which there was only one content parameter: Is it > primary? _YES_. When an operation has only one content parameter, the SDK will automatically > consider it as primary. That means that the content parameter in the `file:write` operation is > automatically made optional and defaults to `#[payload]`. This helps to enforce consistency across > modules. ## Changing the Default of a Primary Content There are edge cases in which a primary content should default to something other than the payload. This tends to happen more when the operation has only one content parameter, and that parameter is not always needed. For example, consider the Database connector. The input parameters of a Select query are primary content, but not all queries require input parameters. So the real default should be an empty map. You can do that by combining the `@Content` and `@Optional` annotations. For example, the following sets the default to an empty map: ```java public List select(@Text String sql, @Optional(defaultValue="#[{}]") @Content Map inputParameters) { // select } ``` You can also set the content parameter as optional, with no default: ```java public List select(@Text String sql, @Optional @Content Map inputParameters) { // select } ``` Finally, you can combine the `@Content` annotation with `@NullSafe`, for example: ```java public List select(@Text String sql, @Optional @NullSafe @Content Map inputParameters) { // select } ``` > [!TIP] The first and third examples are equivalent. However, the `@NullSafe` option is preferred > because it gives the module’s user a better experience. Having `#[{}]` as an explicit default > might be confusing for inexperienced Mule users. ## Embedding Content Parameters in Parameter Groups The content parameters in the [`http:request`](#primary_content_parameter) operation above are contained in an element called `request-builder`. For usability reasons, the author of the connector chose to group all the request-related attributes into an enclosing object. This is supported by the SDK in the following manner: ```java public void request(String path, @ParameterGroup(showInDsl=true) HttpRequestBuilder requestBuilder) { // request } ``` As you can see, there are no content parameters here. However, if you look inside the `HttpRequestBuilder` class, you see `@Content` annotations: ```java public class HttpRequestBuilder { @Parameter @Content(primary = true) private InputStream body; @Parameter @Content @NullSafe private Map uriParams; @Parameter @Content @NullSafe private Map uriParams; } ``` > [!TIP] Removing the `@ParameterGroup` annotation from the `HttpRequestBuilder` argument in the > sample request operation will result in a compilation error. `@Content` is not allowed in complex > types. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, context, inject, event, location --- # Context Information Injection > For the full documentation index, see: https://docs.mulesoft.com/llms.txt To get information about the context in which an operation or source is being processed, you can add parameters to the operation or source. The parameters do not appear in the UI, but they are injected by the SDK into the runtime. ## Component Location When executing an operation or using a source, you might want to have some context about the location of the component, mostly for logging purposes. To achieve this, you can define a `ComponentLocation` as a parameter, and it will be injected. This examples shows how to log the component location when starting a source: ```java @Alias("listener") public class HttpListener extends Source { private static final Logger LOGGER = getLogger(HttpListener.class); @Connection private ConnectionProvider serverProvider; private ComponentLocation location; @Parameter private String path; private HttpServer server; @Override public void onStart(SourceCallback sourceCallback) throws MuleException { server = serverProvider.connect(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Starting HTTP Listener %s on path %s", location.getLocation(), path); } server.listen(path); } // ... } ``` This example shows how to log the component location in an operations (non-source) context: ```java //ComponentLocation is added as a parameter when used in an operation public String sayHi(String person, String q, ComponentLocation location) { // ... System.out.println("Location: " + location.getLocation()); // ... } ``` ## Tracking an Event **Available since version 1.1** To be able to trace an event through logs, you can use a `CorrelationInfo` parameter in your operations and source callback. This correlation info provides the event ID and correlation ID of the event that is processed. You simply need to add a `CorrelationInfo` parameter to your operation method or source callback. The instance will be populated with the event information the method or callback runs. This simplified example shows how to use `CorrelationInfo`: ```java public class ObjectStoreOperations { private static final Logger LOGGER = getLogger(ObjectStoreOperations.class); // ... @Throws(StoreErrorTypeProvider.class) public void store(String key, @Content TypedValue value, ObjectStore objectStore, CorrelationInfo correlationInfo) { objectStore.store(key, value); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Object stored with key: %s .The event id is: %s", key, correlationInfo.getEventId()); } } // ... } ``` --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, functions, function --- # Contributing Functions with Mule SDK > For the full documentation index, see: https://docs.mulesoft.com/llms.txt DataWeave is the main expression language in Mule 4, and the Mule SDK is also aligned with that view. This allows you to contribute functions to DataWeave from your module’s code in a similar fashion that you code a Mule [Operation](https://docs.mulesoft.com/mule-sdk/latest/operations). Using custom functions allows you to reuse code in a new way, providing extra functionality in a single Module with very little overhead regarding how they are declared compared to a Mule operation. ## Declaring a Function At Extension level, you can use the `@ExpressionFunctions` annotation to reference the classes that contain the public methods representing the DataWeave functions. This method’s code is executed directly from DataWeave scripts using it name as identifier: ```java @Extension(name = "Docs") @ExpressionFunctions(DocsFunction.class) public class DocsModule { } ``` In the `DocsFunction` class, this example defines the following method: ```java public Object xpath(@Optional(defaultValue = PAYLOAD) InputStream item, String expression){ try { return xPathFactory.newXPath().evaluate(expression, documentBuilder.parse(item)); } catch (Exception e) { return null; } } ``` This `xpath` function can now be used from inside a DataWeave script, referencing it _inside our Module’s namespace_. The next example reads XML and sets an XPath filter using the new `xpath` function: ```xml 35]/title")]"/> ``` To view the actual implementation of the simplified example shown above, refer to [XPathFunction.java](https://github.com/mulesoft/mule-xml-module/blob/1.3.3/src/main/java/org/mule/module/xml/internal/xpath/XPathFunction.java). ## Restrictions: Config-less and Connection-less By definition, functions are not allowed to depend on or receive a particular [Configuration](https://docs.mulesoft.com/mule-sdk/latest/configs) or [Connection](https://docs.mulesoft.com/mule-sdk/latest/connections). --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, forward compatibility, DoNotEnforceMinMuleVersion, version propagation --- # Controlling Version Propagation > For the full documentation index, see: https://docs.mulesoft.com/llms.txt By default, when a component uses an SDK API, it inherits that API’s minimum Mule version requirement. However, some APIs are designed to enhance functionality without affecting runtime compatibility, and in some cases you can override this inheritance behavior using the `@DoNotEnforceMinMuleVersion` annotation. ## APIs That Don’t Affect Minimum Mule Version Some SDK APIs are annotated with `@DoNotEnforceMinMuleVersion`, which means using them doesn’t raise your component’s minimum Mule version. These are typically APIs that: - Provide design-time enhancements (sample data, metadata) - Add semantic information for tooling (semantic terms, display annotations) - Enable optional features that degrade on earlier Mule runtime versions For example, annotations like `@SampleData`, `@DisplayName`, and semantic annotations such as `@Url` or `@Password` don’t affect your component’s minimum Mule version, even though they were introduced in specific runtime versions. ## Prevent Version Propagation Consider using `@DoNotEnforceMinMuleVersion` to prevent version propagation if: - You use a newer API but provide equivalent fallback behavior for earlier Mule runtime versions - You use runtime version checking to conditionally enable features (see [Forward Compatibility Patterns](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-patterns)) - The API you’re using is optional and its absence doesn’t break core functionality Do not use this annotation to lower your component’s minimum Mule version without implementing proper fallback behavior. Doing so can cause runtime errors when users deploy to earlier Mule runtime versions. ### Prevent Version Propagation With @DoNotEnforceMinMuleVersion Some SDK APIs automatically raise your component’s minimum Mule version when used. If you’ve implemented fallback behavior for earlier Mule runtime versions, use `@DoNotEnforceMinMuleVersion` to prevent version propagation. ```java import org.mule.sdk.api.annotation.DoNotEnforceMinMuleVersion; import org.mule.sdk.api.annotation.param.Parameter; public class MyOperations { public void myOperation(@DoNotEnforceMinMuleVersion @Parameter SomeNewType input) { // This parameter type requires Mule 4.12.0, but we have fallback logic // so we don't want it to raise the operation's Minimum Mule Version } } ``` > [!IMPORTANT] Don’t use this annotation if you haven’t verified that your component works correctly > on earlier Mule runtime versions. If the component depends on the API being fully functional, > don’t suppress version propagation. ## See Also - [Working with Minimum Mule Versions](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-min-mule-version) - [Forward Compatibility Patterns](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-patterns) - [SDK Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, config, configuration --- # Creating Configuration Objects > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Configurations are a set of configurable parameters that affect the overall behavior of your module. All modules are required to have at least one configuration, but you can define as many as needed. Different configurations might not only provide a different set of parameters but also specify their own set of operations, sources, and connection providers that are only available when using that configuration. > [!NOTE] The following examples contain classes mostly from the > [Mule Extensions API](https://github.com/mulesoft/mule-extensions-api). Some classes, such as the > `MessageSources.class`, are made up for the following examples. ## Defining Single Configuration Modules The simplest of cases is that of a Module that has only one configuration. For convenience, you can use the same class that you previously annotated with `@Extension` for that, for example: ```java @Extension(name="singleConfig") @Operations(Operations.class) @Sources(MessageSources.class) @ConnectionProviders({BasicAuthConnection.class, OAuthConnection.class}) public class SingleConfigModule { @Parameter private String someParameter; @Parameter private Integer numericParameter; public String getSomeParameter() { return someParameter; } public String getNumericParameter() { return numericParameter; } } ``` The example above shows the full skeleton of a module with a single configuration. You can see how the same class that declares the extension with all its operations, sources, connections, and so on, is also enriched with `@Parameter` annotated fields, which will translate into the configuration’s parameters. When another component, such as an operation or source, requires access to the configuration, it will do so through instances of the `SingleConfigModule` class. ## Externalizing Single Configuration Object Depending on how you want to organize your code, you might want to have a single configuration object without reusing the `@Extension` annotated class. Reasons for doing this might range from maintainability (you think that you might want to add more configurations in the future), separation of concerns, or simply personal preference. You can use the `@Configurations` annotation to do that. The same reworked example looks like this: ```java @Extension(name="singleConfig") @Configurations(Config.class) (1) public class SingleConfigModule { } @Configuration(name="config") (2) @Operations(Operations.class) @Sources(MessageSources.class) @ConnectionProviders({BasicAuthConnection.class, OAuthConnection.class}) public class Config { @Parameter private String someParameter; @Parameter private Integer numericParameter; public String getSomeParameter() { return someParameter; } public String getNumericParameter() { return numericParameter; } } ```
1Reference a Config class using the @Configurations annotation.
2Define a class that acts as a configuration and defines the parameters, plus all the other components such as operations, connections, and so on.
> [!TIP] Notice that the `@Configuration` annotation requires you to give the configuration a name. > For modules with only one configuration, the name `config` is recommended. ## Defining Multiple Configurations Let’s explain this one using the HTTP connector as an example. There are two sides to implementing the HTTP protocol: to listen for requests and to send requests. Each of these aspects not only requires very different sets of parameters, it also turns out that: - To listen for request, you need to define an `` element that acts as a message source. - To send a request, you need an `` element that acts as an operation. Because an application can contain many listener elements, the elements will only work when paired with an `` that contains general settings about how the connection is to be established and other behavioral parameters. The same happens with the requester operation, which is paired with an `` element. The code looks like this: ```java @Extension(name="http") @Configurations({HttpListenerConfig.class, HttpRequesterConfig.class}) (1) public class HttpConnector { } @Configuration(name="listener") (2) @ConnectionProviders(HttpListenerConnection.class) @Sources(HttpListener.class) public class HttpListenerConfig { /** * Base path to use for all requests that reference this config. */ @Parameter @Optional @Expression(NOT_SUPPORTED) private String basePath; public String getBasePath() { return basePath; } } @Configuration(name="requester") (3) @Operations(HttpRequester.class) @ConnectionProviders(HttpRequesterConnection.class) public class HttpRequesterConfig { // the requester parameters } ```
1The @Extension annotated class defines the two configurations.
2The HttpListener class defines the inbound connection and the message source that is exclusive to the listener feature.
3The HttpConfig class defines the request operation and the outbound connection type.
> [!TIP] Notice that this time, the name used in the `Configuration` annotation doesn’t follow a > specific default, but instead a descriptive name is used. ## Getting the Configuration Name For logging or debugging reasons, you may want to know the name that this config instance has on the application. For this, you can use the `@RefName` annotation. This annotation must be used on fields of type `String`, and no two fields in the same class should bear this annotation. Here is an example of how to use it. In this example, you can see how to log the name of our component when it is getting initialized. This way, you can have traceability of the different config instances. ```java @Configuration(name="config") @Operations(Operations.class) public class Config implements Initialisable { private static final Logger LOGGER = LoggerFactory.getLogger(Config.class); @RefName (1) private String configName; @Parameter private String someParameter; public String getSomeParameter() { return someParameter; } @Override public void initialise(){ LOGGER.debug("Initializing config with name: " + configName); } } ```
1The @RefName annotation signals that the name of the configuration used in the app should be injected to the field configName.
You can see a concrete example of a DSL to see what would happen: ```xml ``` In this case, the variable `configName` would take the value "exampleConfig". When this config is initialized, you will see this reflected on the logs with the message: `Initializing config with name: exampleConfig` --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, sources, listener, triggers --- # Creating Message Sources with the Mule SDK > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Operations are components that process a message and generate a result. Message sources are components that receive or generate new messages to be processed by the Mule Runtime. Examples of message sources: - An HTTP listener - A File watcher - A JMS/AMQP listener that retrieves messages from a queue to which the user previously subscribed - Salesforce Streaming API ## Similarities Between an Operation and a Message Source Operations and message sources have: - Parameters - A return type - Name and Description - Reconnection capabilities ## Differences Between an Operation and a Message Source
OperationsMessage Sources

Operations process messages.

Sources create messages and push them to a flow.

The lifecycle of operations is tied to the containing flow.

Message sources can be started and stopped independently of the containing flow.

Parameters of operations have the option of accepting expressions (the default).

Sources have a clear definition of which parameters can accept expressions and which cannot. Only parameters that are part of generating a response can accept expressions.

An operation can have a lifecycle, but its parameter values are not available or not needed.

A Message source must have start() and stop() phases, and they might require access to the parameter values.

A connection is obtained each time the operation is executed.

A connection is obtained each time the message source is started or when reconnection happens.

## Implementing a Message Source through the Mule SDK The differences listed above make it difficult to define a way to implement a message source in a way 100% consistent with the model defined for operations. Sources are required to extend the `Source` class, which takes two generics: one for the type of the generated event payload, the other for the type of the Attributes: ```java @Alias("listener") @EmitsResponse public class HttpListener extends Source {} ``` > [!TIP] You can use the `@Alias` annotation to force a name. Otherwise, the SDK will infer one. ## Next Step - [Defining Parameters in a Message Source](https://docs.mulesoft.com/mule-sdk/latest/sources-parameters) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, routers, router --- # Creating Routers with Mule SDK > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Conceptually, routers are [operations](https://docs.mulesoft.com/mule-sdk/latest/operations) that can receive many executable routes and a set of parameters that handle the execution of one, all, or none of them. Routes should be used for receiving multiple possible paths of execution, where the parameters of the route can be used to decide whether or not the contained processors should be executed. Stereotypes can be used in the route declaration to define are stereotypes for the contained chain. Routes don’t allow complex parameters to be defined inline because the DSL content of the element will be exclusive of the parameterized [operations](https://docs.mulesoft.com/mule-sdk/latest/operations). ## Defining a Route First you need to declare at least one route in your module by extending the `Route` base class: ```java public class WhenRoute extends Route { @Parameter @Optional private boolean shouldExecute; public boolean shouldExecute() { return shouldExecute; } } ``` ## Declaring the Router Next, you need to declare the router method as one of the module’s [operations](https://docs.mulesoft.com/mule-sdk/latest/operations). Any operation receiving at least one `Route` implementation along with a `RouterCompletionCallback` is registered as a router: ```java @Extension(name = "Docs") @Operations(ChoiceRouter.class) public class DocsModule { } ``` The `ChoiceRouter` class defines the following method: ```java public void choice(WhenRoute when, @Optional OtherwiseRoute otherwise, RouterCompletionCallback callback) { if (when.shouldExecute()) { when.getChain().process(callback::success, (error, previous) -> callback.error(error)); } else if (otherwise != null && otherwise.shouldExecute()) { otherwise.getChain().process(callback::success, (e, r) -> callback.error(e)); } else { callback.success(Result.builder().build()); } } ``` Above, you can see how a router is declared and how the routes are conditionally executed based on their configuration. Route execution is performed through their chain, exactly as though each route was an scope by itself. Using the router consists of configuring the router parameters and then adding each route declaration with its own parameters: ```xml ``` This executes the `http:request` operation contained by the `when` route whenever the `payload` isn’t `null`. If a `null` `payload` arrives, the fallback `otherwise` route executes intead. ## Routers with a Dynamic Number of Routes Routers can declare a parameter that represents a dynamic list of routes. This enables you to create routers that accept zero, one, or many route instances, rather than a fixed set. To declare a list of routes, define a router parameter typed as `List` in the router operation: ```java public void choice(List whens, RouterCompletionCallback callback) { ... } ``` The handling of the elements in the list is similar to the single-route approach. The code should select the route or routes to execute and then run them. The implementation decides whether to call one, none, or many routes, and whether many calls are sequential or parallel. Using the router involves configuring the router parameters and then adding each route declaration, with its own parameters within its wrapper element: ```xml ``` This executes the `http:request` operation in the first `when` route whenever the `payload` isn’t `null`. If a `null` `payload` arrives, the second `when` route executes instead. ## Void Routers **Available since version 1.1** You might need to create a router that at the end of its execution leaves the event and message the same as it was before the execution of the router. This type of router is called Void Router. To implement a Void Router, the method needs to have an argument of the `VoidCompletionCallback` type instead of `RouterCompletionCallback`. You use this callback the same was as before (with the `success` and `error` methods), except in this case, `success` doesn’t receive any parameter. ## Restrictions The following restrictions apply. ### Config-less and Connection-less Routers have some restrictions that differentiate them from [operations](https://docs.mulesoft.com/mule-sdk/latest/operations). By definition, routers aren’t allowed to depend on or receive a particular configuration or connection. ### Always Non-Blocking All routers have to be defined as a `void` method receiving a `RouterCompletionCallback` for communicating its result. This means that all routers are non-blocking by default. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, scopes, scope, components --- # Creating Scopes with Mule SDK > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Scopes are similar to [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations), but their execution includes the execution of other child [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations). This means that a Scope is basically an operation that receives one or more arguments that are simple [Parameters](https://docs.mulesoft.com/mule-sdk/latest/parameters) along with a single `Chain` component and the `CompletionCallback`. A `Chain` represents the child [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations) that you declare inside the Scope and provide an abstraction to execute them. ## Declaring a Scope You can see the basic structure of a Scope with this decorator example. It has the `Chain` to be executed and the `CompletionCallback` to invoke once the scope execution is done _(yes, all Scopes are declared as [Non-Blocking Operations](https://docs.mulesoft.com/mule-sdk/latest/non-blocking-operations))_: ```java @Extension(name = "Docs") @Operations(LogDecorator.class) public class DocsModule { } ``` ```java public void logDecorator(Chain operations, CompletionCallback callback) { LOGGER.debug("Invoking child operations") operations.process( result -> { LOGGER.debug(result.getOutput()); callback.success(result); }, (error, previous) -> { LOGGER.error(error.getMessage()); callback.error(error); }); } ``` Of course, if you receive a `Chain` of [Operation](https://docs.mulesoft.com/mule-sdk/latest/operations), you need to execute them in some way, and that is what the `process(successCallback, errorCallback)` does. With it, the example executes the `Chain` in a non-blocking fashion and listens for the results in case of success or error. Here, the `successCallback` is called with the execution result when a successful execution is completed by _all_ the [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations) in the `Chain`. The `errorCallback` will be called if an error occurs during the execution of _any_ child [Operation](https://docs.mulesoft.com/mule-sdk/latest/operations), with both the error and the last successful result as arguments. Here is how you use the scope in a Mule app: ```xml ``` This executes the `http:request` child operation wrapped by the `LOGGER` invocations. Any number of [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations) can be executed inside the scope, including other scopes or routers. ## Adding Parameters Scopes can receive other simple `@Parameters` and `@ParameterGroups`. They are _simple_ because Scopes are not allowed to receive _complex_ parameters that are declared as child elements. For example, if you want to customize the log level of the decorator, you will be able to receive that as a Parameter: ```java public void logDecorator(@Optional(defaultValue="true") boolean debug, Chain operations, CompletionCallback callback) { if (debug){ LOGGER.debug("Invoking child operations") } operations.process( result -> { Object message = result.getOutput(); if (debug){ LOGGER.debug(message); } else { LOGGER.info(message); } callback.success(result); }, (error, previous) -> { LOGGER.error(error.getMessage()); callback.error(error); }); } ``` With a minimal change in the Scope’s API: ```xml ``` By default, a `Chain` is executed using the Message that was received by the container Scope, but other definitions of the `process` method allow the user of the scope to customize the `payload` and `attributes` that will be used to execute the `Chain`. ## Using Stereotypes `Stereotypes` can be used to limit the [Operation](https://docs.mulesoft.com/mule-sdk/latest/operations) that are allowed to exist inside a `Chain`, which enforces consistency in its usage. Only the [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations) that have the given `Stereotypes` can be declared inside the Scope. ```java public void assertAll(@AllowedStereotypes(AssertionStereotype.class) Chain assertions, @Optional boolean failOnError, CompletionCallback callback) { assertions.process( result -> { callback.success(Result.builder().output(true).build()); }, (error, previous) -> { if (failOnError){ callback.error(error); } else { callback.success(Result.builder().output(false).build()); } }); } ``` ```xml ``` ## Config-less and Connection-less Scopes have some restrictions that differentiate them from [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations). By definition, Scopes are not allowed to depend on or receive a particular [Configuration](https://docs.mulesoft.com/mule-sdk/latest/configs) or [Connection](https://docs.mulesoft.com/mule-sdk/latest/connections). ## Only One Chain The `Chain` component is key in the definition of a Scope, so it is _required_ to have one and _only_ one `Chain` argument. ## Always Non-Blocking All Scopes must be defined as a `void` method receiving a `CompletionCallback` for communicating their Result, which means that all the Scopes are non-blocking by default. See [Non-Blocking Operations](https://docs.mulesoft.com/mule-sdk/latest/non-blocking-operations). --- product: Mule SDK version: 1.13 is-latest-version: true --- # Custom Static Metadata > For the full documentation index, see: https://docs.mulesoft.com/llms.txt **Available since version 1.1** We’ll use Static Metadata to reference the Metadata that is known at compile time an available from the types in the connector’s jar. Simple java types are part of this static Metadata, as well as custom POJOs defined by the developer in its model. The relevant part is that the structure is well known or can be introspected, without depending on other Parameters. The advantage of static metadata over dynamic is that, because it does not change, there is no need to recalculate its structure. This means that the resolution is already done when developing an application. When working with simple java types or POJOs as return or parameter types the SDK will create a proper static type for the element, but when your type is not defined as a java object (e.g. Json Schema, XSD, etc) you know exactly the structure of the type you are working with, you need to use the custom static metadata capabilities that the SDK provides. ## XML and JSON Types Operations may require parameters to be JSON or XML types that follow a certain structure (schema). It is possible to signal this to DataSense using the proper XSD or JSON schemas. > [!NOTE] this will only work for `InputStream` and `String` java type parameters. ### JSON Type To determine the structure of a JSON type, a JSON schema must be provided. - `@OutputJsonType` ```java @OutputJsonType(schema = "person-schema.json") public Result retrievePerson() { // ... } ``` - `@AttributesJsonType` ```java @AttributesJsonType(schema = "resolution-attributes-schema.json") public Result getSolution() { // ... } ``` - @`InputJsonType` ```java public void addPerson(@InputJsonType(schema = "person-schema.json") InputStream person) { // ... } ``` ### XML Type To determine the structure of an XML, you must provide an XSD file and the qualified name that is used to reference the element to be loaded. - `@OutputXmlType` ```java @OutputXmlType(schema = "order.xsd", qname = "shiporder") public Result retrieveOrder() { // ... } ``` - `@AttributesXmlType` ```java @AttributesXmlType(schema = "order-attributes.xsd", qname = "attributes") public Result getOrderStatistics() { // ... } ``` - `@InputXmlType` ```java public void createOrder(@InputXmlType(schema = "order.xsd", qname = "shiporder") InputStream order) { // ... } ``` ## Static Type Resolvers The way to resolve custom static metadata for input parameters, output payload or output attributes is very similar and it is shown on the next examples: On each example, the resolvers extends of an abstract class that implements `StaticResolver`. `getStaticMetadata` is the method that has to be implemented in order to resolve the metadata. ### InputStaticTypeResolver To use custom static metadata resolution on the input parameters of an operation, you must annotate the parameter with `@TypeResolver` and the value for the annotation must be a `InputStaticTypeResolver`. This is an example of a `InputStaticTypeResolver`, it must implement the method `getStaticMetadata`: ```java public class JsonPersonInputStaticTypeResolver extends InputStaticTypeResolver { @Override public MetadataType getStaticMetadata() { ObjectTypeBuilder object = BaseTypeBuilder.create(MetadataFormat.JSON).objectType(); object.id("json-object"); object.addField().key("name").value().stringType(); object.addField().key("last-name").value().stringType(); return object.build(); } } ``` ### OutputStaticTypeResolver to use custom static metadata resolution on the output payload of an operation, you must annotate the method with `@OutputResolver` and the output value for the annotation must be a `OutputStaticTypeResolver`. This is an example of a `OutputStaticTypeResolver`, it must implement the method `getStaticMetadata`: ```java public class CsvOutputStaticTypeResolver extends OutputStaticTypeResolver { @Override public MetadataType getStaticMetadata() { return BaseTypeBuilder.create(MetadataFormat.CSV).objectType().id("csv-object").build(); } } ``` ### AttributesStaticTypeResolver to use custom static metadata resolution on the output attributes of an operation, you must annotate the method with `@OutputResolver` and the attributes value for the annotation must be a `OutputStaticTypeResolver`. This is an example of a `AttributesStaticTypeResolver`, it must implement the method `getStaticMetadata`: ```java public class CsvAttributesStaticTypeResolver extends AttributesStaticTypeResolver { @Override public MetadataType getStaticMetadata() { ObjectTypeBuilder object = BaseTypeBuilder.create(MetadataFormat.CSV).objectType(); object.addField().key("location").value().stringType(); object.addField().key("size").value().numberType(); return object.build(); } } ``` --- product: Mule SDK version: 1.13 is-latest-version: true keywords: parameter, parameters, mule, sdk, dsl, xml, syntax --- # Customizing the Parameters DSL > For the full documentation index, see: https://docs.mulesoft.com/llms.txt To provide the best possible experience for the user, customize or limit some aspects of a component’s parameters. For example, you can hide irrelevant parameters for clarity. ## Receiving Strings As Reference Or As Value Let’s say we have an operation like the following: ```java public void send(@Connection MyConnection connection, Object value) { connection.doSend(value); } ``` Which will be invoked like this: ```xml ``` By default, Mule assumes that `requestPayload` is a reference to an object in the registry, like a global list or POJO. If instead we want the extension to actually receive the `requestPayload` string, we must modify the operation declaration, using the `@ParameterDsl` annotation: ```java public void send(@Connection MyConnection connection, @ParameterDsl(allowReferences = false) Object value) { connection.doSend(value); } ``` This change explicitly disables the use of references, so the operation will receive the string value. ## Defining How Complex Types Can Be Used Any Module is able to expose POJOs that conform to the Module API: ```java // This is the POJO declaration public class Door { @Parameter private boolean isLocked; @Parameter private String color; } // This is the configuration public class HouseConfig { @Parameter private Door mainDoor; } ``` When having this kind of parameters, the Module developer has the responsibility to configure how the `Door` can be used in the DSL. For this, we have the `@TypeDsl` annotation, that allows you to configure two important things: - `allowInlineDefinition`: Defines if the POJO (in the example, the `Door`) can be declared inline for the element that uses it as parameter. If this value is `true`, then the user can declare: ```xml (1) (2) ```
1The config is declared as the container of the parameter
2A Door is declared inline (in the config)
- `allowTopLevelDefinition`: Defines if the POJO (in the example, the `Door`) can be declared as a top-level element in the application. If this value is `true`, then the user can declare: ```xml (1) (2) ```
1A Door is declared as a global element of the application
2The config is declared separately, referencing the global Door element by name with the door-ref attribute
--- product: Mule SDK version: 1.13 is-latest-version: true --- # Customizing the Parameters UI > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The SDK provides a set of annotations used for customizing many aspects of the UI and UX. ## @DisplayName Specifies a custom label for an element or field to be used in the UI. ## @Example Sometimes it is helpful to provide a simple example with the values a parameter can take. ```java @Parameter @Example("0.0.0.0") private String port; ``` That parameter will be rendered as: ![Example of a parameter that takes values](/mulesoft/current/en-us/mule-sdk/latest/_images/example.png) ## @Summary `@Summary` is for a short description of the purpose of a parameter. For example, this description can be used in Studio as the text to be displayed while hovering over the parameter: ```java @Parameter @Optional(defaultValue = "SECONDS") @Summary("Time unit to be used in the Connection Timeout") private TimeUnit connectionTimeoutUnit; ``` That parameter will be rendered like this: ![Example of a tooltip that provides a summary of a parameter](/mulesoft/current/en-us/mule-sdk/latest/_images/summary.png) ## @Placement This annotation enables you to configure these settings: - A relative order within the group in which the parameter is defined. The value is relative in the sense that the element with order `10` is above one with value `25`. - A tab, which is a graphical way to group parameters together. This attribute specifies the name of the tab in which the parameter should be displayed. It uses a default of "General" is no name is specified. To display more than one parameter or field in the same the tab, the value for the tab should be the same for all of them. In the following example, `foo` and `bar` will be grouped into the same tab named `Advanced`, while `foobar` will be the only parameter in the default tab. ```java @Parameter @Placement(order = 1, tab="Advanced") private String foo; @Parameter @Placement(order = 2, tab="Advanced") private String bar; @Parameter private String foobar; ``` `foo` and `bar` parameters will be rendered in a new tab `"Advanced"` like this: ![Example list of tabs](/mulesoft/current/en-us/mule-sdk/latest/_images/placement-tab.png) ## @Text Indicates that a parameter supports a multi-line string input in the UI. This annotation also affects the DSL, making the annotated parameter a child element with the text as the content of the element. Note that this annotation should only be used in parameters of `String` type. Example ```java @Parameter @Optional @Text @DisplayName("SQL Query Text") private String sql; ``` In Studio, the `@Text` will be rendered like this: ![Example of a large text input](/mulesoft/current/en-us/mule-sdk/latest/_images/text.png) `@Text` also affects the DSL of the element that contains the parameter. using it creates a child element that accepts `CDATA` content. For example, assume that you have this operation: ```java public void execute(String message) { ... } ``` The DSL for the operation above looks like this: ```xml ``` This example annotates the `message` operation parameter with `@Text`: ```java public void execute(@Text String message) { ... } ``` So the corresponding DSL looks like this: ```xml ``` ## @Path Marks a parameter as a path to either a file or a directory. This parameter will be displayed in the UI as a file-picker field. Note that this annotation should only be used in parameters of `String` type. This annotation carries information about: - `type`: Whether the path points to a directory, a file, or any of those. - `acceptsUrls`: Whether the path parameter supports URLs. - `acceptedFileExtensions`: A list of file extensions this parameter can handle. This example accepts a `.txt` resource that can be remote: ```java @Parameter @Path(type = FILE, acceptedFileExtensions = "txt", acceptsUrls = true) private String resourcePath; ``` ## @Password Indicates that the parameter needs to be masked when it is populated from the UI. Note that this annotation should only be used in parameters of `String` type. ![Example of a password input](/mulesoft/current/en-us/mule-sdk/latest/_images/password.png) Figure 1. Example in the UI ## Putting It All Together This example uses features explained above: ```java @Parameter @Placement(order = 3, tab="Additional information") @Example("My name is Max the Mule and I love MuleSoft!") @DisplayName("User biography") @Summary("Information related to the user\'s life") @Text private String biography; ``` ## @ClassValue **Available since version 1.1** Indicates that a parameter is a reference to a class name. Note that this annotation should only be used in parameters of `String` type. Besides hinting to the tooling that the parameter is a class, it also allows you to specify restrictions on the classes that can be referenced. It can indicate that the assigned values must extend or implement (`extendsOrImplements`) specific classes or interfaces. Consider a Database connector that requires the end user to provide the class name of the JDBC Driver to be used, for example: ```java @Parameter @ClassValue(extendsOrImplements = "java.sql.Driver") private String driverClassName; ``` --- product: Mule SDK version: 1.13 is-latest-version: true --- # Define Configurations and Connection Providers > For the full documentation index, see: https://docs.mulesoft.com/llms.txt ## Distributing Parameters One key aspect is determining which parameters **should** be part of a configuration object and which **should** be part of a configuration provider. Connection providers **must** contain only parameters that are used to establish and configure connections to an external system. Any parameters used for any other purpose **must** go into the configuration object instead. ## POJO Parameters in Configurations When a configuration or connection provider uses a POJO as a parameter type, without the use of the @ParameterGroup annotation, then the POJO **must** comply with the following: - Have a default constructor - Expose a getter for each field annotated with @Parameter - Override the equals() and hashCode() methods with an implementation that uses all the @Parameter fields ## Don’t Establish Connections During the Lifecycle Phase ConnectionProviders can hook into the application lifecycle by implementing any of the corresponding interfaces (Initialisable, Startable, Stoppable, or Disposable). Connections **must not** be established in the initialisable() or start() methods, and they **must not** be disconnected during the stop() or dispose() methods. Therefore, the provider’s constructor and lifecycle methods **must** never throw a ConnectionException. Connections **must** be established only in the connect() method and **must** be severed only in the disconnect(T) methods ## Handling SSL Connections When a connector needs to establish a TLS/SSL secure connection, the SSLContext **must** be obtained through a TlsContextFactory parameter as explained in this article. When a ConnectionProvider has a TlsContextFactory parameter, it **must** also implement the Initialisable interface and invoke the initialise() method on such context during initialization: ```java public class MyConnectionProvider implements ConnectionProvider, Initialisable { @Parameter private TlsContextFactory tlsContext; @Override public void initialise() throws InitialisationException { ((Initialisable) tlsContextFactory).initialise(); } } ``` ## Defining a Connection Management Strategy When defining or designing the [connection management strategy](https://docs.mulesoft.com/mule-sdk/latest/connections), the following decision process **must** be followed: - The most efficient option is to use a CachedConnectionProvider. This however requires that the connection object **must** be thread safe, and that thread safeness **must not** come at the expense of significant synchronization contention. - If a CachedConnectionProvider is not possible, then a PoolingConnectionProvider **should** be used instead, especially if establishing the connection is expensive. - Only if neither of the previous two options are possible, **should** a plain ConnectionProvider be considered. ## Handling OAuth Protected Connections The SDK supports building connectors to systems protected by OAuth. [As explained in this article](https://docs.mulesoft.com/mule-sdk/latest/oauth), it automatically handles the process of obtaining and refreshing OAuth tokens, which are made available through an AuthorizationCodeState object (if the Authorization Code grant type is being used) or the ClientCredentialsState object (if the Client Credentials Grant Type is being used). The sections that follow provide additional considerations for OAuth use. ### Handle Token Expiration Although the SDK provides the ability to refresh access tokens in a thread-safe manner, there’s no standard way to detect that the token has actually expired because different service providers communicate this in different ways, making it each connector’s responsibility to detect such a condition. When using OAuth authentication, all connectors **must** detect when a token has expired and communicate it to Mule by raising an AccessTokenExpired exception. This is difficult because connectors **must** differentiate between the following conditions: - The access token expired and needs to be refreshed - The access token is valid, but lacks privileges to perform a given action - The access token has been remotely revoked ### Always Query the State Object on Each Request The AuthorizationCodeState and ClientCredentialsState objects are the means through which connectors gain access to the access tokens that Mule manages. Most precisely, this happens through the getAccessToken() method that both classes share. Because of the highly concurrent nature of Mule, those tokens can be refreshed or unauthorized at any time. Therefore, the getAccessToken() method **must** be invoked on the state object for every request to be sent. The access token **must not** be cached or stored in an internal variable. This consideration is especially important when using third-party client libraries that might perform caching. ### Do Not Use the Access Token in the Connection Creation The creation of the connection object in the `ConnectionProvider#connect` method must not depend on the presence of a valid access token. Connections that need to use the token to perform a one-time-only request should not rely on the creation of the connection. The request must be deferred to when the connection is first used. To adhere to this practice, you can use the `org.mule.runtime.api.util.LazyValue` class. When a value is initially used, the value is retrieved; subsequent uses get the value that was cached initially. The following example shows how to use the `org.mule.runtime.api.util.LazyValue` class: ```java @AuthorizationCode(accessTokenUrl = MyConnectionProvider.ACCESS_TOKEN_URL, authorizationUrl = MyConnectionProvider.AUTH_URL, defaultScopes = MyConnectionProvider.DEFAULT_SCOPE) public class MyConnectionProvider implements ConnectionProvider { public static final String ACCESS_TOKEN_URL = "accessTokenUrl"; public static final String AUTH_URL = "authUrl"; public static final String DEFAULT_SCOPE = "defaultScope"; private AuthorizationCodeState state; @Override public Connection connect() throws ConnectionException { return new Connection(new LazyValue(getUserId(state.getAccessToken()))); } private String getUserId(String accessToken){ ... } ... } ``` As such, the first time `LazyValue` is used, the information is retrieved, and all later usages get the value that has already been resolved. ## Connection Object Must Not Expose (Nor Be) the Inner Client A common anti-pattern often found in connectors is that the ConnectionProvider generates a connection object that exposes the client or implementation used to access the external system. For example: ```java public class HttpConnection { private HttpClient client; public HttpClient getClient() { return client; } } ``` An operation would then use it like in this pseudo code: ```java public void createCustomer(@Connection HttpConnection connection, @Content InputStream content) { connection.getClient().send(HttpRequest.builder() .path(connection.getPath() + "/customer") .method("POST") .entity(content) .addHeader("Accept", "application/json") .build() ); } ``` This code has several drawbacks. The most obvious is that the operations using the connection are strongly coupled to the implementation of the connection object. If the need ever comes to change the implementation of the client, all the components using that connection will be affected. Another problem is that the connection object is not testable. Because the client object is exposed, writing a test that interacts with a mock version of the connection object becomes too complicated. The biggest problem is that it introduces functional coupling between all components using the connection object and the functional nuances of all supported connection providers. For example, assume that the example above is part of a connector that supports both Basic Authentication and OAuth authorization mechanisms. If invalid credentials are used with the Basic Authentication connection, then the request will result in a HTTP 401 status code and the operation **should** fail. However, if the same response code is received using an OAuth protected connection, then the connector needs to execute logic to determine if the token has expired and an AccessTokenExpiredException **should** be thrown. As new connection types and components are added, the worse the problem becomes. This is only one example. It can happen with all other types of connections, not just HTTP clients. To prevent this, the connection objects **must** encapsulate their inner communication mechanisms and security schemes, leading to a pattern like this: ```java public void createCustomer(@Connection HttpConnection connection, @Content InputStream content) { connection.createCustomer(content); } ``` With this approach, each ConnectionProvider implementation can provide its own implementation of the HttpConnection object, removing the root cause of the problem and providing freedom to change the inner workings of the connection without affecting other components. ### Alternative: Leverage the Command Pattern Instead of giving the connection object one method per endpoint to be consumed, another option is to implement the command design pattern in a more generic way: ```java public void createCustomer(@Connection HttpConnection connection, @Content InputStream content) { connection.request(HttpRequest.builder() .path(connection.getPath() + "/customer") .method("POST") .entity(content) ); } ``` With this approach, the connection object has only one generic request method which receives an HttpRequestBuilder object. Notice that the builder object never receives the build() command. Depending on the implementation of the connection object, additional headers can be added, the request can be performed in different ways and the response can be processed accordingly. ## Do Not Export as Part of the Module’s API Configuration, ConnectionProvider and Connection classes **must not** be exported as part of the module’s API. ## See Also - [Salesforce Help](https://help.salesforce.com) - [Mule SDK Development Best Practices](https://docs.mulesoft.com/mule-sdk/latest/best-practices) --- 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 ``` `` **must not** depend on `` 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 ``` 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 ``` ## 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 ``` 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 read(String path) { return Result.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 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, `` 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) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Define Sources > For the full documentation index, see: https://docs.mulesoft.com/llms.txt ## Assure Restartability Tools like Anypoint Runtime Manager allow you to start and stop individual sources or flows, independently from the rest of the application. This means that all sources must: - Be able to be stopped and restarted at any time, without their functionality being affected - Release all resources when stopped including connections, threads, files, and so on. ## No Manual Polling Sources that work by doing polling **must not** implement the polling logic manually. They **must** extend the PollingSource class instead. ## Declare When a Response Is Emitted There are sources that only trigger messages into a flow. Others, also send a response to an external system, like the HTTP connector. Sources that emit a response **must** be annotated with [@EmitsResponse](https://docs.mulesoft.com/mule-sdk/latest/sources-response). ## Handle Asynchronous Callbacks Sources define callbacks (methods annotated with @OnSuccess and/or @OnError) in which asynchronous tasks are performed. These async tasks can range from sending a response using non-blocking I/O to sending notifications or auditing tasks. For asynchronous callbacks, the source **must** use SourceCompletionCallback to signal the completion of such tasks. See [Sending Asynchronous Responses from a Message Source](https://docs.mulesoft.com/mule-sdk/latest/sources-async-response). ## Use Transactions When the source obtains its messages from a transactional system, it **must** do so using [transactions](https://docs.mulesoft.com/mule-sdk/latest/transactions). ## Handle Back Pressure The Mule [execution engine](https://docs.mulesoft.com/mule-runtime/latest/execution-engine) may apply back pressure to a message source when at capacity. By default, the SDK forces the source to wait when back pressure occurs by making this method block and wait for Mule to have resources available to handle the request: `org.mule.runtime.extension.api.runtime.source.SourceCallback#handle(org.mule.runtime.extension.api.runtime.operation.Result)` Although this default behavior suits most cases, some modules require different actions. For example: - Instead of waiting, the HTTP connector sends a 503 response - The JMS connector makes it configurable if the source **should** wait, fail, or drop the message, as those are valid options in the messaging domain When the module’s domain includes use cases in which waiting **should not** be the default, or additional options **should** be available, then the source **must** use the @BackPressure annotation to provide that behavior. ### Custom Actions When Back Pressure Occurs Some modules may need to take custom actions when back pressure errors occur, such as:. - Resolving transactions - Releasing resources - Sending notifications to external systems Modules with this functional need **must** do so by including a callback method annotated with @OnBackPressure. For example: ```java @OnBackPressure public void onBackPressure(BackPressureContext ctx) { // .. implementation } ``` Notice that if the action to be taken is asynchronous, a SourceCompletionCallback **must** be leveraged too: ```java @OnBackPressure public void onBackPressure(BackPressureContext ctx, SourceCompletionCallback completionCallback) { // .. implementation } ``` ### Resolving Transactions If the source is transactional, then it **must** add a back pressure handler in which the transaction is resolved. ## Mind the Cluster Behavior All sources **must** consider how they are going to behave when Mule operates in cluster mode or the same app is deployed to several workers in CloudHub. In some cases, it’s okay for the source to run on all replicas. An example of this is the . The underlying protocol already guarantees that no two nodes will pick up the same file at the same time. This is the default behavior. However, there are some cases in which the source **should** run on the primary node only, as running on all replicas could lead to bogus behavior or data corruption. An example of this is a `` that is listening to a topic, or a source reading from a stream of events. In these cases, a source **must** be annotated with the @ClusterSupport annotation, and specifying the NOT\_SUPPORTED or DEFAULT\_PRIMARY\_NODE\_ONLY options. ## Display Name Sources **should** have a @DisplayName that uses the “On ..” prefix to communicate in which event or action the source/listener/trigger will be executed/triggered, for example: - JMS Connector The source is called On New Message communicating that the source will dispatch a message when a new message arrives to a Queue. - Database Connector On Table Row communicates that triggers for each table row. - Salesforce Connector On Created Object, every time the connector detects a new object in Salesforce, the connector triggers the flow. ### DSL Naming At the DSL level, the sources **should** instead use the `listener` suffix, or simply use the listener name. For example: - The HTTP connector defines the message source for exposing inbound HTTP endpoints - The WebSockets connector defines the and sources These can be controlled using the @Alias annotation. ## See Also - [Salesforce Help](https://help.salesforce.com) - [Mule SDK Development Best Practices](https://docs.mulesoft.com/mule-sdk/latest/best-practices) # Defining Parameters Defining parameters well is a key aspect of the usability of any component. Even if the components provides awesome functionality, the user is not going to have a great experience if the component is not easy and intuitive to configure. Handling JSON and XML Parameters Some operations expose parameters that expect values in the form of JSON or XML documents. - Java types The JSON and XML parameters **must** be received in the form of an InputStream. None of the usual Java types for representing these types (Reader, Sax objects, Document, Node, JsonNode, and so on) are allowed. - DataSense resolution Users need tooling support to make sure that they feed these parameters with documents that conformed to the expected schemas. Therefore, these parameters **must** clearly define the expected 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: - @InputJsonType annotation Allows you to define the expected type by pointing to a JSON schema that’s part of the module’s resources: ```java public void createPerson(@InputJsonType(schema = "person-schema.json") @Content InputStream person) { ... } ``` - @InputXmlType annotation Similar to @InputJsonType, but for XML schemas: ```java public void createOrder(@InputXmlType(schema = "order.xsd", qname = "shiporder") @Content InputStream order) { ... } ``` - InputStaticTypeResolver Another option is the use of an InputStaticTypeResolver to define the type programmatically: ```java public void createOrder(@TypeResolver(OrderTypeResolver.class) @Content InputStream order) { ... } ``` More information about [handling static DataSense](https://docs.mulesoft.com/mule-sdk/latest/static-metadata). - Dynamic DataSense If the schemas are dynamic, then a dynamic resolver is needed. This is done by implementing the InputTypeResolver interface. The key rule is that the module **must** resolve to a specific schema that the user can follow. The dynamic resolver cannot return a generic ANY type. ```java public void createOrder(@TypeResolver(DynamicOrderTypeResolver.class) @Content InputStream order) { ... } ``` ## [](#dynamic-java-type-parameters)Dynamic Java Type Parameters It’s very common for connectors to work with dynamic types. For example, services like Salesforce, NetSuite, or SAP define a set of core entities (Person, Order, and so on) which have a base structure that the user can customize. Other services go further and allow for a completely custom type set, such as an OData service. All parameters using these dynamic types **must** have an associated @TypeResolver that provides the actual type definition to be used for the current configuration. Prior sections discussed the need for @TypeResolver for parameters 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 TypeResolver **must** exist for all other dynamic types, regardless of which Java class is used to represent them. ## [](#streaming)Streaming Streaming is a key aspect of Mule 4. It’s important to define operations in a way that allows to leverage that. - Don’t use byte\[\] array parameters Parameters **must not** be of byte\[\] type. Use InputStream instead. Mule automatically transforms any byte\[\] parameter value into an InputStream. - Avoid large String parameters String parameters are perfectly fine, but always consider what are the odds of the assigned values to become larger than 4 KB. If that’s a possibility, then the parameter **must** be an InputStream instead and use an InputTypeResolver to provide metadata on the media type that’s expected. ## [](#layout)Layout The MuleSoft layout mechanism comprises two organizational concepts: groups and tabs. Although you are free to define your own groups and tabs, there’s a set of standard names for them. Default groups: - General - Advanced - Connection Default tabs: - Default - Advanced - Security - Connection If a given parameter does not fit these categories, define a new one that can be defined. That decision needs to be made explicit and documented in the extension’s design spec. - Required Parameters Required parameters **must** always be in the default group and tab, and **must** be the first to appear in the generated UI. Required parameters **must** be placed on the main group and tabs so that the end user can see at all of them at first glance. - Optional Parameters Optional parameters **should not** be placed in the main view. This is to not overload the view with too much content that in most cases won’t be configured by the average customer. Exceptions: - Some optional parameters are not required to be configured, but are used very frequently. - The parameter is optional because it has a default value, but it’s still key to the component’s behavior (for example, the method parameter of the http:request operation). - Default Values Expressions **should** never be used as default values. This includes expressions that access the event (such as `[vars]`, `[attributes]`, or `[payload]`), and expressions that obtain dynamic values (such as `[now()]`) or create default empty structures (such as `#[[]]`). - @NullSafe Optional parameters of Collection or Map types **must** be annotated with @NullSafe to guarantee that null values are avoided by automatically providing empty instances instead. - Security parameters When talking about security parameters, it’s important to distinguish between actual security parameters and connection parameters. For example, username and password could be thought of as security parameters because they’re used for access control. But in reality, you can’t establish a connection without those in the first place, so they are also required connection parameters. - Therefore, optional security parameters like tlsContext, or hashing algorithms **must** go into a security tab, except when they are required. - Required security parameters, **must** go in general as any other required parameter. - Parameter ordering As explained earlier, required parameters **must** also be shown first, but in which order **should** they be listed? The same thing applies to optional parameters: we know they go after the required ones, but in which order? Parameter ordering considerations: - There’s a strong and known relevance ranking In some cases, the developer knows that some parameters are more relevant than others. That relevance might be given based on how frequently they are used or by how relevant they are to the particular domain of the connector. For example: when talking about the http:listener, the path is by far the most relevant parameter, even if other required parameters exist. The same applies to the optional parameters: the allowed-methods parameter is more relevant than the keep alive settings. The problem with this case is that relevance might be subjectively biases. People might not agree on what’s relevant or not and the person making the call could simply be wrong. In these cases, the case for that relevance sorting **must** be made explicit in the extension’s design document and justified. Reviewers **must** also have the chance to challenge that decision. - Metadata Keys order: This scenario applies only for operations. If the operation has a metadata key hierarchy, parameters that match with metadata keys **should** follow the same order. - There’s no usage or relevance ranking - Mixed There might be a strong relevance among some group or subgroup of parameters, but another group (for example advanced or security) might not be. For cases with no explicit relevance, alphabetic sorting **should** be used. - Parameter Types When working on Java based connectors, the connector **must not** expose types from external libraries. For example: if is being used the MongoDB Library to create a MongoDB Connector, the result of the queries are represented as BSON objects. This could never be the output or input of an operation because of many reasons: - The connector gets coupled to implementations that are not managed by the connector developer. - The type is difficult or impossible to consume or create from DataWeave. - The internal library gets shared on the application classloader and could cause class clashing. ## [](#parameter-hints)Parameter Hints: - @Example For optional parameters that don’t specify a default, the @Example annotation **should** provide an example of a valid value. This increases the UX of Mule developers to be more sure about the kind and structure of data that they **should** configure.
This applies to parameters of simple types only. It doesn’t include POJOs, lists or maps.
- Display Name The SDK and Studio makes a best effort at transforming the parameter’s name to what is shown in the UI, but sometimes this is not enough. A name that makes sense on the connector’s DSL maybe doesn’t make sense in the UI and vice versa. For these cases, you **should** use the @DisplayName feature. Also because the parameter name used on the extension is also the one used for the DSL (XML) and the display name, once the connector is released, this name can’t change since that would break backwards compatibility. The @DisplayName annotation is also useful in those cases to change the name shown in the UI without affecting backwards compatibility. Lastly, keep in mind that unlike Java or the Mule DSL, the @DisplayName does accept spaces. - Expression Support By default, parameters accept expressions. There are some cases in which this default **should** be changed. Disable expression support when it doesn’t make sense to support expressions in the context of a configuration or connection provider parameter, for example, the port number in which to place an http listener. It doesn’t make sense for the port of an inbound listener to be changing each time mule processes a message. Make the use of expression mandatory when there’s no use case in which that parameter is to take a fixed value. For example, an aggregator’s group identifier. - Summary All parameters have a matching Javadoc that explains the purpose of that parameter and its semantics. Sometimes, that explanation can be quite lengthy. For those cases, it’s a good practice to use the @Summary annotation to provide a one liner description of the parameter. It’s ok if this summary doesn’t fully cover its meaning and purpose. It’s just intended to be a quick one liner that the UI can turn into a tooltip. - Configuration Overrides Sometimes a configuration object defines a parameter that **should** globally affect a connector. However, some components might want to override that parameter. The canonical example of this is a charset encoding. A file connector could define the encoding to use at a configuration level. However, in the context of an application, one might have one specific flow in which a different encoding **should** be used (even though the rest of the application might still use the one specified in the config). The @ConfigOverride annotation **must** be used in these cases. This is not only a matter of simplifying your own code (as opposed to manually checking for an override and defaulting to the config one instead). The @ConfigOverride not only does that automatically, but in Studio also overrides the config parameter allowing the UI to surface this accordingly. [Configuration Overrides](https://docs.mulesoft.com/mule-sdk/latest/config-override) are explained at length in this article. - Putting the pieces together Here’s an example of how the hinting features can interact together ```java @Parameter @Optional @Summary("The sender 'From' address. If not provided, it defaults to the address specified in the config") @Example("example@company.com") private String fromAddress; ``` ## [](#enum-versus-value-provider)Enum Versus Value Provider There are use cases in which certain parameters are exposed to the user as a list of predefined values. Sometimes that list is fixed, meaning that all possible values to select from are already defined at compilation time and never change. An example of this is the protocol of an SFTP proxy. Possible values are HTTP, SOCKS4, and SOCKS5. That list is fixed and unaltered by any other condition. For cases like this, modules **must** use a [Java Enum](https://docs.mulesoft.com/mule-sdk/latest/value-providers#java_enum) as the parameter type. The Enum lists all the possible values and the SDK automatically generates the proper experience at design time. Alternatively, the list of possible values can be dynamic and depends on other conditions. For example, the Slack connectors allow users to send messages to different channels. The list of available channels actually depends on the logged user. Therefore, the list is dynamic. To include values that are not known or dynamic, such as custom values, you **should** use [Value Providers](https://docs.mulesoft.com/mule-sdk/latest/value-providers#value_providers), instead. When to use an Enum? - When the values Set is well known - When the value set is limited - When the value set is not context dependant - When the value set is closed - When the required a combo inside a POJO (Value Providers are not supported inside POJOs) When to use a Value Provider? - When the values are not well known - When the value set is context dependent depending on the Connection or Configuration or other parameters - When the value set is open such that the connector can suggest some values, but other ones can also be valid - When the value set is dynamic ## [](#exclusive-parameters)Exclusive Parameters If parameters values are invalid, modules **should not** fail at startup time or execution time. For example, the scripting module executes user scripts that can be provided either inline or referenced from a file. This module provides two parameters: one called script for the inlining option, and another called scriptFile. The user can use only one of these at a time, which means that they’re both optional. To provide a good user experience, the UI **should** provide hints to a user at design time when: - The two parameters are provided at the same time - No parameter is provided Use cases like this **must** leverage the [Exclusive Optionals](https://docs.mulesoft.com/mule-sdk/latest/exclusive-optionals) feature to provide a good experience at design time. ## [](#using-pojo-parameters)Using POJO Parameters Sometimes, you need to define parameters of POJO types. Depending on the scenario, that practice might be advised or simply discouraged. - Using POJOs for parameter grouping One use case for a POJO parameter is when the same set of parameters constantly repeat across several components (either operations or sources). The Java good practice would be to put those together into a single reusable class. For example, consider the Aggregators module, in which all group-based aggregators require a set of common parameters. This example creates the GroupBasedAggregatorParameterGroup class: ```java public class GroupBasedAggregatorParameterGroup extends TimeoutContainingAggregatorParameterGroup { /** * An expression that determines the aggregation group unique ID. * This ID determines which events *must* be aggregated together. */ @Parameter @Expression(REQUIRED) @Optional(defaultValue = "#[correlationId]") private String groupId; /** * The size of the expected group to aggregate. All messages with * the same correlation ID *must* have the same groupSize. * If not, only the first message groupSize is considered and * a warning is logged. */ @Parameter @Expression(SUPPORTED) @Optional private Integer groupSize; /** * The time to remember a group ID once it was completed or timed out. * 0 means, don't remember, -1 remember forever */ @Parameter @Expression(NOT_SUPPORTED) @Optional(defaultValue = "180") private int evictionTime; /** * The unit for the evictionTime attribute */ @Parameter @Expression(NOT_SUPPORTED) @Optional(defaultValue = "SECONDS") private TimeUnit evictionTimeUnit; } ``` This object would then be used as follows: ```java @Alias("groupBasedAggregator") @Throws(GroupBasedAggregatorErrorProvider.class) public void aggregateByGroup( GroupBasedAggregatorParameterGroup aggregatorParameters, @Alias("incrementalAggregation") @Optional IncrementalAggregationRoute incrementalAggregationRoute, @Alias("aggregationComplete") AggregationCompleteRoute onAggregationCompleteRoute, RouterCompletionCallback completionCallback) throws ModuleException { // impl... } ``` This approach however introduces some problems: - The GroupBasedAggregatorParameterGroup object defines a complex type in the module’s type catalog, even though this type has no real semantic meaning for the module’s domain model. This is just a convenience internal type, derived from good practices of the programming language used to write the module. There would be no sense for this type to appear in the catalog. - Because the type appears in the catalog, it becomes part of the module’s API. Therefore, modifications to it, or even the decision to replace it with something else, results in a backwards compatibility problem. - Complex objects are hard to translate into a good user experience in Studio. All of the these issues can be traced to one root cause: the module’s structure **should not** reflect nor be affected by the nuances of the programming language being used (notice that we wouldn’t be having this discussion if the XML SDK would be used here instead). To be able to still maintain parameters in a POJO without generating the side effects in the generated module, use the @ParameterGroup annotation: ```java public void aggregateByGroup( @ParameterGroup( name = "Aggregator config") GroupBasedAggregatorParameterGroup aggregatorParameters, @Alias("incrementalAggregation") @Optional IncrementalAggregationRoute incrementalAggregationRoute, @Alias("aggregationComplete") AggregationCompleteRoute onAggregationCompleteRoute, RouterCompletionCallback completionCallback) throws ModuleException { // implemented as privileged operation in GroupBasedAggregatorOperationsExecutor } ``` This annotation opens the POJO and exposes its inner parameters, as if they had been inlined as method arguments. As far as the user and the type catalog know, there’s no GroupBasedAggregatorParameterGroup and the operation simply defines multiple String parameters. However, the developer still needs to leverage the Java POJO. - Forcing child elements in the Mule DSL Another use case for POJO parameters is forcing certain parameters to appear as child elements in the Mule DSL. For instance, consider this example from the email connector: ```xml Email Content ``` The is a POJO that contains the content and contentType parameters. Because the email connector is quite complex, we want the body to appear as a child element in the Mule DSL, for increased readability. To use a POJO just for this has the same drawbacks as in the prior example, but again, you can use the @ParameterGroup annotation to overcome them: ```java @Summary("Sends an email message") @Throws(SendErrorTypeProvider.class) public void send(@Connection SenderConnection connection, @Config SMTPConfiguration configuration, @Placement(order = 1) @ParameterGroup(name = "Settings") EmailSettings settings, @Placement(order = 2) @ParameterGroup(name = "Body", showInDsl = true) EmailBody body, @Placement(order = 3) @ParameterGroup(name = "Attachments") AttachmentsGroup attachments) { ``` As you can see, the @ParameterGroup annotation has a showInDsl attribute that can be used to force the DSL to put the inner parameters as a child element. - Defining Global Elements Another use case is the ability to define a global element (which is not a Configuration) to referenced from another component. For example, the File connector has the concept of a matcher, which is a set of rules that are used to decide whether a file **should** be processed or ignored. Matchers can be used in either the operation or in any of the message sources defined in the connector. Because the exact same matcher might be needed in different flows, the connector provides the ability to define global matchers than can be later referenced. For example: ```xml ``` For these cases, it makes sense to define the matcher parameter as a POJO, without the use of the @ParameterGroup annotation. Notice that in this case, having the concept of a matcher being listed in the connector’s type catalog makes sense, since this is not an implementation detail but a core piece of functionality. There’s one caveat though: for this to work, the class in which the matcher is implemented needs to declare that it is to be used as a global element: ```java @Alias("matcher") @TypeDsl(allowTopLevelDefinition = true) public class LocalFileMatcher { /** * Files created before this date are rejected. */ @Parameter @Summary("Files created before this date are rejected.") @Optional private LocalDateTime createdSince; /** * Files created after this date are rejected */ @Parameter @Summary("Files created after this date are rejected") @Optional private LocalDateTime createdUntil; ... } ``` - DSL Decision Making As a summary, here’s a simple flow chart to aid with the decision process of how to use POJOs and parameter groups. ![Decision tree for POJOs and parameter groups](/mulesoft/current/en-us/mule-sdk/latest/_images/mule-sdk-dsl-parameter-pojos.png) - Implementing `equals` and `hashCode` methods Java classes used for POJO parameters or Parameter Groups must implement the `equals` and `hashCode` methods for [Dynamic Configurations](https://docs.mulesoft.com/mule-sdk/latest/static-dynamic-configs#Dynamic-Configuration-Lifecycle) to function correctly. Even if existing configurations do not have POJO parameters, the configurations need `equals` and `hashCode` methods because future runtime functionality may depend on it. The following example from a class in the Database Connector that illustrates the implementation of `equals` and `hashcode` methods: ```java /** * Pooling configuration for JDBC Data Sources capable of pooling connections * * @since 1.0 */ @Alias("pooling-profile") public class DbPoolingProfile implements DatabasePoolingProfile { /** * Maximum number of connections a pool maintains at any given time */ @Parameter @Optional(defaultValue = "5") @Placement(order = 1) @Expression(NOT_SUPPORTED) private int maxPoolSize = 5; /** * Minimum number of connections a pool maintains at any given time */ @Parameter @Optional(defaultValue = "0") @Placement(order = 2) @Expression(NOT_SUPPORTED) private int minPoolSize = 0; /** * Determines how many connections at a time to try to acquire when the pool is exhausted */ @Parameter @Optional(defaultValue = "1") @Placement(order = 3) @Expression(NOT_SUPPORTED) private int acquireIncrement = 1; /** * Determines how many statements are cached per pooled connection. Setting this to zero will disable statement caching */ @Parameter @Optional(defaultValue = "5") @Placement(order = 4) @Expression(NOT_SUPPORTED) private int preparedStatementCacheSize = 5; /** * The amount of time a client trying to obtain a connection waits for it to be acquired when the pool is * exhausted. Zero (default) means wait indefinitely */ @Parameter @Optional(defaultValue = "0") @Placement(order = 5) @Expression(NOT_SUPPORTED) private int maxWait = 0; /** * A {@link TimeUnit} which qualifies the {@link #maxWait}. */ @Parameter @Optional(defaultValue = "SECONDS") @Placement(order = 6) @Expression(NOT_SUPPORTED) private TimeUnit maxWaitUnit; /** * Determines how many seconds a Connection can remain pooled but unused before being discarded. * Zero means idle connections never expire. */ @Parameter @Optional(defaultValue = "0") @Placement(order = 7) @Expression(NOT_SUPPORTED) private int maxIdleTime; @Parameter @Optional @Placement(tab = ADVANCED_TAB, order = 8) @Expression(NOT_SUPPORTED) @Summary("Additional properties used to configure pooling profile.") private Map additionalProperties = emptyMap(); //getters... @Override public int hashCode() { return Objects.hash(minPoolSize, maxPoolSize, acquireIncrement, preparedStatementCacheSize, maxWaitUnit, maxWait); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof DbPoolingProfile)) { return false; } DbPoolingProfile that = (DbPoolingProfile) obj; return maxPoolSize == that.maxPoolSize && minPoolSize == that.minPoolSize && acquireIncrement == that.acquireIncrement && preparedStatementCacheSize == that.preparedStatementCacheSize && maxWait == that.maxWait && maxWaitUnit == that.maxWaitUnit; } } ``` ## [](#parameter-type-restrictions)Parameter Type Restrictions You cannot add parameters or POJOs with types `org.mule.runtime.core.api.source.scheduler.Scheduler` or `org.mule.runtime.api.scheduler.SchedulingStrategy`. Mule runtime engine and the SDK add parameters of these types, for example, in polling sources. ## [](#see-also)See Also - [Salesforce Help](https://help.salesforce.com) - [Mule SDK Development Best Practices](https://docs.mulesoft.com/mule-sdk/latest/best-practices) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, sources, listener, triggers, parameters --- # Defining Parameters in a Message Source > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Parameters are defined as fields of your `Source` class. All the features and annotations (such as `@NullSafe`, `@ParameterGroup`, `@TypeResolver`) are available when defining an operation, for example: ```java @EmitsResponse public class HttpListener extends Source { @Config private HttpListenerConfig config; @Parameter @Optional(defaultValue = "/") private String path; @Parameter private Integer port; /** * Comma separated list of allowed HTTP methods by this listener. */ @Parameter @Optional @Placement(tab = Placement.ADVANCED_TAB) @Summary("Comma separated list of methods. Leave empty to allow all.") @Example("GET, POST") private String allowedMethods; } ``` This is similar to how configs are defined, except that none of these parameters accept expressions. Using the `@Expression` annotation to support or require expression support results in an error. ## Next step - [Lifecycle of a Message Source](https://docs.mulesoft.com/mule-sdk/latest/sources-lifecycle) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, config, dynamic, multitenant, multitenancy --- # Dynamic Configurations > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The need to use expressions in a configuration parameter is a common one. Typical use cases are: - Multi-Tenancy: Credentials or any other setting might change depending on the active user. - Dynamic endpoints: Different destinations are used depending on the routed message. - Dynamic settings: For example, a timeout might change depending on the destination of the message. ## Static Configurations A configuration is static when none of its parameters are assigned an expression (even if those parameters do support expressions), for example: Example: Static Configuration ```xml ``` All the parameters in the example have fixed values, so this configuration is static. Note that there is a difference between a parameter with a value that is not an expression and a parameter that does not accept expressions. The former might accept an expression as a value. ## Dynamic Configurations In dynamic configurations, at least one parameter has an expression, for example: Example: Dynamic Configuration ```xml ``` This configuration is dynamic because the `defaultWriteEncoding` can resolve to a different value for each execution. Here is another dynamic configuration: Example: Multi-tenancy ```xml ``` This example shows how to configure multi-tenancy. Expressions in connection parameters also resolve into dynamic configurations. ## Dynamic Configuration Lifecycle Static and dynamic configurations have very different lifecycles. For static configurations, a single instance is created and used each time it is needed. Dynamic configurations have a different behavior. Each time an operation that points to a dynamic configuration is executed, all the parameters in the configuration are evaluated. A configuration instance is created for each unique set of resolved parameters. In that way, each time the same values are resolved, the same instance is returned, but for each different set of values, a different instance is created. ## Dynamic Configuration Expiration Although it is easy to create dynamic configurations, you still need a configurable mechanism for disposing the configuration instances that are no longer needed (or that have been idle for a certain period). This is critical to prevent out-of-memory (OOM) issues. Returning to the [FTP multi-tenant](#multi_tenancy) example, the worst case scenario mandates that a new configuration instance is produced on a per-message basis. Depending on the app, these instances not be needed once the transfer is completed. By default, dynamic instances of any configuration element are automatically disposed of between 5 to 10 minutes after the configuration has been idle. This is because the disposal depends not only on the configuration’s expiration time, which is set to 5 minutes by default, but also on the frequency of the Expiration Monitor, which runs every 5 minutes and ultimately performs the disposal. > [!TIP] The instance is inactive if no operation has started _or finished_ in that lapse. If an FTP > transfer started 6 minutes ago, but the file is too big, and the transfer is still in progress, > that configuration will not be expire. SDK-based modules also support specifying custom timeouts for specific config definitions. This is something that the module user might do to fine-tune an app to handle cases where the default timeout is known to be too short or too long. The following example lowers the timeout to 30 seconds: Example: Modifying a Timeout ```xml ``` > [!TIP] No particular action is required from you for the `` element to be > available. The SDK does this automatically. It is also possible to configure the global default at an app level so that any dynamic configuration has a different default. You set this up through the `` element in the [Mule DSL](https://docs.mulesoft.com/mule-runtime/latest/about-mule-configuration), which is provided for you in Anypoint Studio: ```xml ``` Finally, it is also possible to set the frequency at which the runtime checks for and disposes of configurations that have expired (default is 5 minutes). This cannot occur on a config-by-config basis, only at the app level. The following example not only sets the default expiration time to 30 SECONDS, it also makes the runtime check every 1 minute. ```xml ``` --- product: Mule SDK version: 1.13 is-latest-version: true --- # Enforcing the Output MediaType > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The [`Result` object](https://docs.mulesoft.com/mule-sdk/latest/result-object) allows you to set the output `mimeType`. However, this setting is optional (SDK developers are not forced to set the `mimeType`). However, there are some cases in which it is mandatory to specify the output `mimeType` of an operation: - When the operation returns a `String`. - When the operation returns an `InputStream`. This restriction is not arbitrary. Mule 4 uses DataWeave as its expression language, so users do not need to worry about the format of the data. They only need to concern themselves with the its structure. However, this means that DataWeave must be able to determine the media type of all the values carried on the Mule Message. The SDK automatically handles most of the work needed for DataWeave to have that information automatically, but when the operation returns a generic type such as `String`, it is impossible to know if the data is supposed to be in JSON, XML, or even plain text format. The same thing happens with `InputStream`: it could be a stream of JSON, XML, or CSV, or it could be binary information. In either case, the SDK needs help from a developer to know this in design time. The `@MediaType` annotation exists for this purpose. Every source or operation that returns a `String` or an `InputStream` is forced to have this annotation, even if the specified mediaType is `**/**`. For example, suppose you are developing an operation that returns information about a customer. Also suppose that this operation returns the information in XML format. You can do this: ```java @MediaType(MediaType.APPLICATION_XML) public String getCustomerInfo(String customerId) { return fetchCustomerXml(customerId); } ``` Or suppose you are developing a video streaming module that returns videos in `mp4` format: ```java @MediaType("video/mp4") public InputStream getVideo(String videoId) { return getVideoStream(videoId); } ``` > [!TIP] The `@MediaType` annotation defines a set of constants with the most common media types. > However, as shown in the video media type example, you can use whatever custom \`mediaType \`you > need. ## Strict False Media Types In the examples above, it is impossible for operations to return a media type other than the one specified. In other cases, you might know what the media type is _most likely_ to be, or you simply do not know what the `mediaType` is. For these cases, the `@MediaType` annotation has a `strict = false` option. ### Variable Media Type Returning to the example that gets a customer information operation, suppose that the service to which you are connecting returns XML by default, but you could also configure your particular account to use a different format instead. Also suppose there is no way for you to know this beforehand. You can do this: ```java @MediaType(value = MediaType.APPLICATION_XML, strict = false) public String getCustomerInfo(String customerId) { return fetchCustomerXml(customerId); } ``` Setting the `strict` parameter to `false` automatically adds an `outputMimeType` parameter to the operation. This parameter allows the module’s user to manually override the `mimeType`. So, users who know that their account and are configured to use JSON can do this: ```xml ``` ### Unknown Media Type Consider the case of an HTTP request. Depending on which endpoint you hit, the obtained media type changes. Because you do not know which media type you will get, you use the `ANY` (`**/**`) one. ```java @MediaType(value = "*/*", strict = false) public InputStream request(String path) { return httpClient.request(path); } ``` Again, the `strict = false` attribute allows the end user to manually set the `mimeType` depending on the endpoint: ```xml ``` > [!TIP] The `outputMimeType` parameter that is automatically added is optional and has no default. > The user can just leave it blank. In that case, the SDK uses the media type you set in the > annotation. --- product: Mule SDK version: 1.13 is-latest-version: true --- # Error Handling > For the full documentation index, see: https://docs.mulesoft.com/llms.txt ## Defining Error Types The module **must** define an error type for all business related or technical conditions that can be expected and make sense for the user to want to handle specifically. Examples of conditions that **should** have a matching error type: - Entity referenced by ID does not exists - Account has no funds - User is unauthorized - User lacks privileges to perform a certain action - Remote system is at capacity Examples of conditions that **should not** have a matching error type: - Network error - Unexpected server error - Unknown error ## Use Error Hierarchies Mule 4 Error types support the concept of hierarchies. That means that an error can stand on its own, or it can be a child of another ErrorType defined in the same module or defined as a generic Mule Error. Leveraging Mule 4 error handling allows the user to reuse generic error handlers that tackle generic problems in consistent ways. For example, imagine a flow that uses multiple connectors in the same flow and wants to trigger a specific alert when a connectivity issue appears. If all the connectors in that flow use MuleErrors.CONNECTIVITY as the parent error for all connectivity problems, then that becomes easy to accomplish. The same applies internally for any module that defines error types that hold some kind of internal relationship. Here’s an example taken from the WebSockets connector: ```java public enum WsError implements ErrorTypeDefinition { CONNECTIVITY(MuleErrors.CONNECTIVITY), CLIENT_SECURITY(MuleErrors.CLIENT_SECURITY), SERVER_SECURITY(MuleErrors.SERVER_SECURITY), BASIC_AUTHENTICATION(SERVER_SECURITY), UNAUTHORIZED(CLIENT_SECURITY), FORBIDDEN(CLIENT_SECURITY), NOT_FOUND, SERVICE_UNAVAILABLE, NO_SUCH_SOCKET, INVALID_SOCKET_ID, NON_UNIQUE_SOCKET_ID, REMOTELY_CLOSED, INTERNAL_SERVER_ERROR; private ErrorTypeDefinition> parent; WsError(ErrorTypeDefinition> parent) { this.parent = parent; } WsError() {} @Override public Optional>> getParent() { return Optional.ofNullable(parent); } } ``` In this example you can see how: - To define error types with parents - The CONNECTIVITY error extends the Mule one - The CLIENT\_SECURITY and SERVER\_SECURITY errors are defined - BASIC\_AUTHENTICATION, FORBIDDEN, and UNAUTHORIZED errors extend CLIENT\_SECURITY and SERVER\_SECURITY - The set of other errors that are domain-specific and stand on their own ## Correct Use of Module Exception The ModuleException class (or a custom subclass of it) **must** be thrown only when an error matches a defined ErrorType. Unexpected or non-handable errors that do not have a matching ErrorType **must** be thrown as regular exceptions. ## Avoid Proliferation of Error Types A common anti-pattern is to excessively apply the concept of “one error type per business error” into “one error type for every error detail”. An example of that is creating errors like INVALID\_EMAIL, INVALID\_ID, INVALID\_NAME, INVALID\_CARD, and so on, that lead to an explosion of types that makes the feature unusable. The module **must** collapse error types of similar semantics and leverage the exception message instead. So per the previous example, only one ErrorType called INVALID\_DATA **should** exist, and then the exception message can provide additional context on whether the problem was the credit card, client name, and so on. Notice that this rule is closely related to the previous concept of “the error has to be handleable” as it’s extremely unlikely that an end user will want to catch an invalid ID and invalid email differently (simply think of how many times have you actually done that in Java in your life). ## Do Not Expose Exceptions or Error Types Exception, ErrorTypeDefinition, and ErrorTypeProvider classes defined in the module **must not** be exported as part of its API. ## See Also - [Salesforce Help](https://help.salesforce.com) - [Mule SDK Development Best Practices](https://docs.mulesoft.com/mule-sdk/latest/best-practices) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: error, sdk, error handling, operations, try, catch, on error, propagate --- # Errors Definition > For the full documentation index, see: https://docs.mulesoft.com/llms.txt In Mule, errors are a way of communicating that something went wrong and providing meaningful information so that a user can take corrective action depending on the kind of error that was thrown. See [About Error Handling](https://docs.mulesoft.com/mule-runtime/latest/error-handling) for more detail. ## Defining Module Errors The first step is to define all the errors that can be thrown by a Module. To do so, you define an `enum` that implements the `ErrorTypeDefinition` interface. Each value defined in this `enum` is considered to be an Error. ```java public enum SimpleError implements ErrorTypeDefinition { INVALID_PARAMETER, TIME_OUT, NOT_ALLOWED } ``` ### Errors Hierarchy Module errors can only inherit from errors of the same module (values of the same `enum`) or Mule errors. Mule errors are defined in `org.mule.runtime.extension.api.error.MuleErrors`. Note that any error that does not define a parent error automatically inherits from `MULE:ANY`. ```java public enum HierarchalError implements ErrorTypeDefinition { INVALID_PARAMETER, TIME_OUT, NOT_ALLOWED, ILLEGAL_ACTION(NOT_ALLOWED), BAD_CREDENTIALS(MuleErrors.CONNECTIVITY); private ErrorTypeDefinition> parent; HierarchalError(ErrorTypeDefinition> parent) { this.parent = parent; } HierarchalError() { } @Override public Optional>> getParent() { return Optional.ofNullable(parent); } } ``` ### Registering Errors in the Module The `@Extension` annotated class should be annotated with `@ErrorTypes` to indicate which errors a Module handles. This annotation references an `ErrorTypeDefinition` `enum` containing the defined errors. ```java @Extension(name = "Foo") @ErrorTypes(HierarchalError.class) @Xml(prefix = "test-connector") public class FooExtension { // Content } ``` ## Specifying Errors That Can Be Thrown After declaring all the possible errors in your Module, you need to provide the information that binds the errors with the Operations that can throw them. To do that, you need to implement an `ErrorTypeProvider`, which is a class that communicates the errors that can be thrown by an Operation. ```java public class ExecuteErrorsProvider implements ErrorTypeProvider { @Override public Set getErrorTypes() { HashSet errors = new HashSet<>(); errors.add(HierarchalErrors.INVALID_PARAMETER); errors.add(HierarchalErrors.BAD_CREDENTIALS); errors.add(HierarchalErrors.ILLEGAL_ACTION); return errors; } } ``` After defining the `ErrorTypeProvider`, you must bind it with the proper Operation. You bind it using the `@Throws` annotation at the Operation level, for example: ```java @Throws(ExecuteErrorsProvider.class) public void execute(){ // operation body } ``` In the Studio, an operation that is annotated with `@Throws` annotated Operation will provide a hint to the user about the errors the operation can throw: ![Workflow on how to select the errors the operation can throw](/mulesoft/current/en-us/mule-sdk/latest/_images/studio-errors.gif) The XML looks like this: ```xml ``` ## Throwing Errors Errors are a Mule representation of a Java Exception that is bound to an specific Error. There is no static binding between Errors and Exceptions. To communicate an error, the Operation should throw `org.mule.runtime.extension.api.exception.ModuleException` or child exceptions of this class, indicating in the Constructor the desired `ErrorTypeDefinition` to throw. ```java @Throws(ExecuteErrorsProvider.class) public void execute(){ try { throw new IllegalStateException(); } catch (IllegalStateException e){ throw new ModuleException(HierarchalErrors.ILLEGAL_ACTION, e); } } ``` Also, a recommended practice is to wrap this logic inside new exception classes: ```java public final class IllegalActionException extends ModuleException { public IllegalActionException(Exception cause) { super(HierarchalErrors.ILLEGAL_ACTION, cause); } } ``` > [!WARNING] Throwing an Error that is not declared in the `ErrorTypeProvider` of an Operation will > result in an Unexpected Error Exception. Operations are not allowed to throw undeclared Errors. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, annotation, exclusive, optional --- # Exclusive Optionals > For the full documentation index, see: https://docs.mulesoft.com/llms.txt When you are developing a module that provides several ways of configuring the same thing, but only one of them should be used, you might need to set the following rule: - From a given set of parameters, only one shall be used at any time. You use `@ExclusiveOptionals` to enforce this restriction. ## @ExclusiveOptionals The `@ExclusiveOptionals` annotation can only be used on types that are used as a `@ParameterGroup` (see [Parameters](https://docs.mulesoft.com/mule-sdk/latest/parameters) for details). Example ```java @ExclusiveOptionals public class ExclusiveOptionalsExample { @Parameter private String requiredParameter; @Parameter @Optional private String optionalParameter; @Parameter @Optional private String anotherOptionalParameter; } ``` As the name of the annotation implies, the exclusivity restriction only applies to the optional parameters. So, setting `requiredParameter` at the same time as `optionalParameter` or `anotherOptionalParameter` is allowed, but setting `optionalParameter` and `anotherOptionalParameter` at the same time causes an error to be thrown. Tip: You can have as many parameters as you want inside an object that is annotated with `@ExclusiveOptionals`, but only one optional parameter is allowed to be set at any time. ## Enforcing At Least One Parameter There can be cases where exactly one (but no more than one) of the optional parameters must be set. You can configure this requirement by using `@ExclusiveOptionals(isOneRequired = true)`, for example: ```java @ExclusiveOptionals(isOneRequired = true) public class OneRequiredExample { @Parameter private String requiredParameter; @Parameter @Optional private String optionalParameter; @Parameter @Optional private String anotherOptionalParameter; } ``` So, an error is thrown if you set `requiredParameter` but do not set one of the optional parameters: `optionalParameter` or `anotherOptionalParameter`. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, extensions api, deprecated, legacy --- # Extensions API (Deprecated) > For the full documentation index, see: https://docs.mulesoft.com/llms.txt This page documents the legacy approach to SDK versioning using `mule-modules-parent`. For new modules, see [Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility) with `mule-java-extension-parent` instead. In traditional SDK development, the SDK version is tightly coupled to the Mule runtime engine version. When you develop with the SDK, you use the Extensions API. Whenever you annotate a field or method with SDK annotations, or implement any interface, you are using parts of this Extensions API. The module you ship contains only your code. The Extensions API implementation in the runtime bridges the gap between your module and the Mule runtime internals. ## SDK Version and Mule Runtime Compatibility Each Mule runtime version ships with support for one particular version of the Extensions API, plus all previous versions. For example: - Mule `4.1` ships with SDK version `1.1` - Mule `4.2` ships with SDK version `1.2` Mule `4.2` fully supports modules developed with SDK versions `1.2`, `1.1`, and `1.0`. However, a module built with SDK `1.2` requires at least Mule `4.2`, it won’t work in Mule `4.1`. This concept works both ways: - Any Mule runtime version supports all earlier SDK versions, so a module built for Mule `4.1` works with any later Mule 4.x version - If a module is built using SDK `1.2`, it only works in runtimes that support that SDK version ## Choosing a Version When using the legacy approach, use the oldest SDK version that includes all the features you need: - If you need only basic connectivity and DataSense, use version `1.0` - If you need `PollingSource`, use SDK `1.1` which introduced that feature This ensures your module is compatible with the widest range of Mule runtime versions. ## Change the Version The SDK version is specified in the `parent` element of your `pom.xml`: ```xml org.mule.extensions mule-modules-parent 1.9.0 ``` To change versions, update the `` in the parent element. Choose the `mule-modules-parent` version that corresponds to your target Mule runtime version. ## Bug Fixes The Extensions API implementation resides in Mule runtime, so most SDK-related bug fixes are released in patch releases and service packs for Mule, not through new SDK versions. In some cases where the issue is in the API definition, a patch release of the SDK is made available (for example, `1.1.2`). Always choose the higher bug fix version—between `1.0.0` and `1.0.4`, pick `1.0.4`. ## See Also - [SDK Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility) - [Choosing the SDK Version](https://docs.mulesoft.com/mule-sdk/latest/choosing-version) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, SDK, library, dependency, external, jar, maven --- # External Libraries > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Connectors and Modules sometimes depend on external libraries that cannot be packaged inside the connector due to licensing, versioning, custom libs, and so on. The Database connector is an example of a case where the same base code you can work with several different drivers and several versions of a same driver. The solution to this problem is ask users of the Database connector to identify the JDBC Driver to use for the database to which they want to connect. ## Usage To declare that the Connector or Module requires an external library to work in a Mule app, you need to annotate your `@Extension`, [Configuration](https://docs.mulesoft.com/mule-sdk/latest/configs), or [ConnectionProvider](https://docs.mulesoft.com/mule-sdk/latest/connections) class with the `@ExternalLib` annotation and provide properties that will help the Mule App developer understand how to configure these libraries. > [!TIP] To help your users configure libraries more easily, it is highly recommended that you fill > out all the properties that the annotation provides, for example: - ```java @ExternalLib(name = "MySQL JDBC Driver", description = "A JDBC driver that supports connection to the MySQL Database", nameRegexpMatcher = "(.*)\\.jar", requiredClassName = "com.mysql.jdbc.Driver", coordinates = "mysql:mysql-connector-java:5.1.44") public class MySqlConnectionProvider implements ConnectionProvider { // } ``` \+ Note that a Connector can require multiple external libraries. The `@ExternalLib` annotation can be used many times as needed. ### Properties
PropertyRequiredTypeDescription

name

true

String

Friendly name to be displayed in the UI.

description

false

String

Short description of the external library.

nameRegexpMatcher

false

String

Regular expression used to match the name of the library’s file and validate that the library specified by your user is the correct one.

requiredClassName

false

String

(Only for Java Libraries) Name of a Class that will be present in the external library. This will be used for validating whether the library specified by your user is the correct one.

type

true

Enum

  • NATIVE: Native library dependency

  • JAR: JAR file dependency

  • DEPENDENCY: External dependency (for example, an obtained dependency that is specified in the pom.xml if you are using Maven)

For custom connectors, all Maven dependencies must be accessible from Anypoint Exchange. Maven dependencies published to private or public repositories are not accessible from Anypoint Exchange.

You can publish Maven dependencies before publishing your custom connector by using one of the following options:

  • Publish the Maven dependencies on Anypoint Exchange to make them available to your Anypoint Platform organization.

  • Publish the Maven dependencies on the Maven Central repository to make them publicly available.

You cannot use MuleSoft repositories for publishing.

coordinates

false

String

Maven coordinates suggestion that indicates where the required library can be found. They should follow Maven’s convention groupId:artifactId:packaging:classifier:version.

optional

false

Boolean

Indicates whether the library is not required for the Connector or Module to work.

--- product: Mule SDK version: 1.13 is-latest-version: true --- # Field-Level Capabilities > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Java SDK includes several features that support field-level capabilities for value providers and sample data. Using these features enables you to define value providers for parameter fields, or to use parameter fields as acting parameters for either a value provider or sample data. These features are compatible with Mule 4.4.0, however, all Anypoint Connectors can use these features, regardless of their Mule version. ## Defining a Value Provider for One Part of a Parameter Define a value provider for a parameter field by using the annotation `@FieldValues`. The `@FieldValues` annotation requires: - **value** Provides values for a field. - **targetSelectors** Describes which part of a parameter contains the values supplied by the value provider. The DataWeave syntax defines this part of the annotation to describe the selector through nested fields, XML attributes, or fields that contain spaces in their names. The following example shows an operation that includes values provided for a parameter named `body`, which is a JSON input stream: public class ChatOperations { public void sendChatMessage(@Connection ChatConnection chatConnection, @FieldValues(value = WorkspacesValueProvider.class, targetSelectors = "routingInfo.workspace") @TypeResolver(ChatMessageResolver.class) @Content InputStream body) { // Do message sending } } The `body` parameter has a field named **routingInfo**, which has a field named `workspace`. **WorkspacesValueProvider** is a value provider that uses the connection and retrieves possible workspaces. ## Defining a Value Provider for Several Parts of a Parameter Parameters can have more than one part where values are provided. The `@FieldValues` annotation is a repeatable annotation, so parameters can be annotated multiple times. The following example shows an operation with a parameter named `body`, which is a JSON input stream that has two parts with value providers: public class ChatOperations { public void sendChatMessage(@Connection ChatConnection chatConnection, @FieldValues(value = WorkspacesValueProvider.class, targetSelectors = "routingInfo.workspace") @FieldValues(value = ChannelIdValueProvider.class, targetSelectors = "channelId") @TypeResolver(ChatMessageResolver.class) @Content InputStream body) { // Do message sending } } The parameter `body` defines values for two fields (**routingInfo.workspace** and **channelId**) that have values that are independent of each other. ## Defining a Multilevel Value Provider for Several Parts of a Parameter Use multilevel value providers for several parts of a parameter. For example, imagine that you want the values for the **channelId** field to depend on the values for the **routingInfo.workspace** field. To define this, the **targetSelectors** attribute from the `@FieldValues` annotation always receives an array of strings. If more than one string is given, a multilevel value provider is defined. Each given field defines the value parts of the value provider, the order in the array, and the order of the value part. The following example shows how to define multilevel value providers for parameter parts: public class ChatOperations { public void sendChatMessage(@Connection ChatConnection chatConnection, @FieldValues(value = ChatMultiLevelValueProvider.class, targetSelectors = {"workspace", "channelId"}) @TypeResolver(ChatMessageResolver.class) @Content InputStream body) {} } The multilevel value provider is included like any other value provider. The following is an example of what a multilevel value provider looks like: public class ChatMultiLevelValueProvider implements ValueProvider { @Connection private ChatConnection chatConnection; @Override public Set resolve() { Set values = new HashSet<>(); List workspaces = chatConnection.getWorkspaces(); workspaces.stream().forEach(workspace -> { ValueBuilder valueBuilder = newValue(workspace); chatConnection.getChannels(workspace).stream().forEach(channel -> valueBuilder.withChild(newValue(channel))); values.add(valueBuilder.build()); }); return values; } @Override public String getId() { return "Chat multilevel value provider"; } } ## Defining a Value Provider for XML Attributes Target selectors use the DataWeave syntax. If a parameter of an operation is an XML element, use this syntax to define a value provider for an attribute that is a part of that parameter. The following example shows how the DataWeave syntax defines a value provider for an attribute: public class ChatOperations { public void sendChatMessage(@Connection ChatConnection chatConnection, @FieldValues(value = ChannelIdValueProvider.class, targetSelectors = "message.target.@channelId") @TypeResolver(XmlChatMessageResolver.class) @Content InputStream body) {} } In this example, the `body` parameter is an XML element that has a root tag named **message**, which has a **target** that has the **channelId** attribute and an associated value provider. ## Parameters Static Metadata When resolving acting parameter values, always know the media types of the parameters from the component that executes the extraction expression and that gives the acting parameter its value. Use the static metadata of the parameter to retrieve the media type of the parameter. For example, if the parameter is a JSON input stream but the static metadata does not reflect this, the parts of the parameter cannot be used as acting parameters. ## Define Acting Parameters as Parts of Parameters To define acting parameters as parts of parameters, components must define bindings for their references for value providers and sample data providers. These bindings enable an acting parameter of the provider to take the value of the evaluated DataWeave expression. When no binding is defined for an acting parameter, the same implicit binding is applied. The name of the acting parameter must be the same as the name of the parameter in the component to take that parameter’s value. Use the `@Binding` annotation to define an acting parameter. The `@FieldValues` annotation and the `@SampleData` annotation takes an array of bindings to define the values the acting parameter takes. A defined binding declares the value that an acting parameter takes by providing an extraction expression. This extraction expression is a DataWeave expression that uses only selectors to retrieve the value for the acting parameter. The extraction expression used for the extraction of the value of the acting parameter has some limitations: - It does not aggregate any data from the context. It is limited to access only the value of a field in the parameter. - The first part of the extraction expression corresponds only to the name of a parameter from the component. - The output media type is always `application/java` when executing the extraction expression to give values to the acting parameters of a value provider or sample data provider. This means that the provider does not expect other media types, such as `application/json` or `application/xml`. The available context for the expression has the value of all the component’s parameters. The expression must start with the name of the parameter of the component. If the value comes from a part of the parameter, the expression follows the selector to the desired field. The following example shows a parameter named `body` that has a field with values that depend on the acting parameter `workspace`: public class ChatOperations { public void sendChatMessage(@Connection ChatConnection chatConnection, @FieldValues(value = ChannelsValueProvider.class, targetSelectors = "channelId", bindings = {@Binding(actingParameter = "workspace", extractionExpression = "body.routingInfo.workspace")})) @TypeResolver(ChatMessageResolver.class) @Content InputStream body) { // Do message sending } } This acting parameter is taken from the same `body` parameter. The following syntax shows the value provider used in this example: public class ChannelsValueProvider implements ValueProvider { @Connection private ChatConnection chatConnection; @Parameter private String workspace; @Override public Set resolve() { return ValueBuilder.getValuesFor(chatConnection.getChannels(workspace)); } @Override public String getId() { return "Channels value provider"; } } The `workspace` acting parameter defined in the value provider is bound to the extraction expression that takes the value of the field from the context of the component. ## Sample Data Example The following example illustrates a `@SampleData` annotation that binds an acting parameter to the value of a field of a parameter in the component: public class ChatOperations { @SampleData(value = ChatMessageSampleDatatProvider.class, bindings = {@Binding(actingParameter = "workspace", extractionExpression = "body.routingInfo.workspace")})) public Result retrieveChatMessage(@Connection ChatConnection chatConnection, @TypeResolver(ChatMessageResolver.class) @Content InputStream body) { // Do chat message retrieval } } **ChatMessageSampleDatatProvider** contains a parameter named `workspace` that provides the sample data. The value of this acting parameter is taken from the extraction expression defined in the binding. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, forward compatibility, RuntimeVersion, MuleVersion, conditional behavior --- # Forward Compatibility Patterns > For the full documentation index, see: https://docs.mulesoft.com/llms.txt When building forward-compatible modules, you often need to use newer SDK features while providing fallback behavior for older runtimes. The following patterns show how to implement conditional behavior based on the Mule runtime version. ## Check the Mule Runtime Version Use the `@RuntimeVersion` annotation to inject the current Mule Runtime version into your component. You can apply this annotation to fields of type `org.mule.runtime.api.meta.MuleVersion`. > [!NOTE] The `mule-sdk-api` artifact has a dependency on `mule-api` for basic APIs that aren’t > intended to evolve over time. `MuleVersion` is one of these stable APIs, so there’s no > `org.mule.sdk.api` counterpart for it. You can safely use `org.mule.runtime.api.meta.MuleVersion` > in forward compatible modules. ```java import org.mule.runtime.api.meta.MuleVersion; import org.mule.sdk.api.annotation.RuntimeVersion; public class MyOperations { @RuntimeVersion private MuleVersion runtimeVersion; public void myOperation() { if (runtimeVersion.atLeast("4.12.0")) { // Use newer feature } else { // Use fallback behavior } } } ``` The `MuleVersion` class provides methods to compare versions:
MethodDescription

atLeast(String version)

Returns true if the Mule runtime version is equal to or greater than the specified version.

atLeastBase(String version)

Returns true if the base version (ignoring patch level) is equal to or greater than the specified version.

The runtime version is constant across your module, so @RuntimeVersion applies only at the field level. Centralize your version-dependent logic in a shared field rather than checking in every operation. ## Call New Methods in Existing APIs SDK APIs can add new methods in later versions. If you call a method that doesn’t exist on an earlier Mule runtime version, you get a `NoSuchMethodError`. The SDK doesn’t introspect which methods your code actually calls. Even if a method has `@MinMuleVersion("4.12.0")`, using it doesn’t automatically raise your component’s Minimum Mule Version. You are able to deploy a module to earlier versions where the method doesn’t exist. For this reason, you must check the Mule runtime version before calling new methods, and provide fallback behavior for older versions. ### Example: Using a New Method with Fallback The following example shows how to conditionally call a method that exists in Mule 4.12.0 and later: ```java import org.mule.runtime.api.meta.MuleVersion; import org.mule.sdk.api.annotation.RuntimeVersion; import org.mule.sdk.api.runtime.streaming.StreamingHelper; public class MyOperations { @RuntimeVersion private MuleVersion runtimeVersion; public void processStream(InputStream stream, StreamingHelper streamingHelper) { if (runtimeVersion.atLeast("4.12.0")) { // Use new reset() method available in 4.12.0 streamingHelper.reset(stream); } else { // Implement fallback for older runtimes where reset() doesn't exist if (stream instanceof Cursor) { ((Cursor) stream).seek(0); } else { stream.reset(); } } } } ``` If no fallback is possible, explicitly annotate your operation with `@MinMuleVersion` to restrict it to compatible versions. ## Use Newer APIs Without Raising Minimum Version If you use a newer API but provide fallback behavior for older Mule runtime versions, you can prevent that API from raising your component’s Minimum Mule Version. Combine Mule version checking with `@DoNotEnforceMinMuleVersion` to keep your component available on earlier versions while using new features when available. ```java import org.mule.runtime.api.meta.MuleVersion; import org.mule.sdk.api.annotation.DoNotEnforceMinMuleVersion; import org.mule.sdk.api.annotation.RuntimeVersion; public class MyOperations { @RuntimeVersion private MuleVersion runtimeVersion; public void myOperation(@DoNotEnforceMinMuleVersion NewFeatureType input) { if (runtimeVersion.atLeast("4.12.0") && input != null) { // Use new feature when available } else { // Fallback behavior for earlier versions } } } ``` The `input != null` check is necessary because parameters with types from `org.mule.sdk.api` resolve to `null` on earlier versions that don’t recognize the type. By combining the version check with `@DoNotEnforceMinMuleVersion`, you: - Prevent the API from raising your component’s Minimum Mule Version - Safely use the feature on later Mule versions - Provide fallback behavior when the feature isn’t available ## Best Practices for Forward-Compatible Code Follow these guidelines when implementing version-dependent behavior: - Check versions before calling new methods Failing to do so causes `NoSuchMethodError` at runtime. - Centralize version checks Use a shared field annotated with `@RuntimeVersion` rather than checking in every method. - Test on multiple Mule runtime versions Verify your fallback logic works correctly on the earliest Mule version you support. - Document runtime-specific behavior If a feature behaves differently on earlier versions, document this for your users. ## See Also - [Controlling Version Propagation](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-enforcement) - [Working with Minimum Mule Versions](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-min-mule-version) - [SDK Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility) - [HTTP-Based Connectors](https://docs.mulesoft.com/mule-sdk/latest/http-based-connectors) for the forward-compatible `org.mule.sdk.api.http` client API --- product: Mule SDK version: 1.13 is-latest-version: true --- # General Coding Rules > For the full documentation index, see: https://docs.mulesoft.com/llms.txt This set of general rules applies to any component of a module or connector. These apply on all operations, sources, configs, connection providers, and functions). ## Connectors Must Closely Reflect the Target System In the context of Anypoint Platform, a connector’s purpose is to bring the target system into the Mule language. The connector **must not** add higher-level abstractions or include operations that implement specific business use cases. For example, imagine a billing API that contains resources for creating an invoice, adding items to an invoice, and submitting an invoice. The connector for such an API **must** contain one operation per each of those resources, but **must not** include an operation that combines all three calls into a single unit. The one exception is auto-paging operations, which, to achieve the paging effect, perform several calls as part of processing the output of one single Mule operation. ## Do Not Mix Protocols One connector **must** connect to only one type of protocol at a time. For example, suppose an external system exposes a REST API, a SOAP API, and an OData API. In Anypoint Platform, there are three different APIs, even if they access the same external system. Therefore, each of those APIs **must** have its own connector, instead of one that understands the three protocols. ## Exported Module Java API Before reading this section, see [Classloading isolation](https://docs.mulesoft.com/mule-sdk/latest/isolation) in the Mule SDK. The module **must** export only packages defined in your module. Packages from dependency libraries **must not** be exported. ### Java Packages Packages from the JDK are treated differently since modules don’t export them but only consume them. Mule automatically exports all available Java packages for the modules to use. However, the module **must** use only Java packages available on all of the JDKs supported by the Mule versions that the module is compatible with. Vendor-specific packages such as com.sun **must not** be used. ### Java Compatibility See [Java Support](https://docs.mulesoft.com/general/java-support) to decide which Java version to use based on the target Mule runtime. #### Java 11 Compatibility Starting with Mule 4.2.0, JDK 11 is also supported. That means that when depending on native Java APIs, the module **must** account for the fact that the needed API might not be available given Java’s new modular architecture (also known as [Project Jigsaw](https://openjdk.org/projects/jigsaw/), first introduced in Java 9). For example, if your module requires the Java Transaction API (JTA), the module **must** declare the following dependency in its pom.xml file: ```xml javax.transaction javax.transaction-api 1.2 ``` Or if the Java Mail API is used: ```xml javax.mail javax.mail-api 1.6.2 ``` ### Design for Thread Safety Mule is a reactive execution engine, designed for highly concurrent workloads. Therefore, all components **must** always be designed to support concurrent executions at minimal contention. [Thread safety](https://en.wikipedia.org/wiki/Thread_safety) is a basic concept for any Java developer familiar with concurrency. Users of the SDK are expected to be familiar with this. ## Use Immutable Payload and Attributes Objects All output types, used as either payload or message attributes, **must** be immutable objects. ## Correct Use of Content Parameters Make correct use of @Content parameters according to the MuleSoft [Content Parameters](https://docs.mulesoft.com/mule-sdk/latest/content-parameters) document. ## Never Access Flow Variables In Mule 4, variables are for exclusive access of end users only. No module **should** ever access flow variables directly. Although the SDK doesn’t provide any API for accessing them, optional parameters can be leveraged to hack this restriction. For example: ```java @Optional(defaultValue = "#[vars.foo]") String foo @Optional(defaultValue = "#[vars]") Map> vars ``` However, these statements (or any other way of getting to the vars from inside a module) are forbidden. Every piece of information required by the module **should** be made explicit through a parameter. End users decide to assign that value to a variable or not. Notice that violating this rule automatically violates position independence. ## Never Use Message Attributes as Parameters No component **should** define a parameter of a type that’s also used as message attributes, even if the attributes object is defined as part of the same module. For example, if a connector has an operation that uses the LocalFileAttributes class as output attributes, the following is illegal: ```java public void checkAccess(@Content LocalFileAttributes attributes) { doCheckAccess(attributes); } ``` Instead, add parameters that reflect what information from the attributes class you actually need: ```java public void checkAccess(String path) { ... } ``` ## Use Correct Encodings When connecting to systems that accept multiple encodings, the encoding to be used **must** be configurable through a [Configuration Override](https://docs.mulesoft.com/mule-sdk/latest/config-override). This means that the encoding to use **should** be parameterizable at the config level, but also on each component that requires it (operations, sources, functions, and so on). At the config level, the encoding parameter **must** default to the Mule default encoding, which is obtained through the @DefaultEncoding annotation. For example: ```java public class MyConfig implements Initialisable { @DefaultEncoding private String defaultEncoding; @Parameter @Optional private String encoding; public void initialise() throws InitialisationException { if (encoding == null) { encoding = defaultEncoding; } } public String getEncoding() { return encoding; } } ``` ## Duration Parameters Modules often require that you configure durations with classical examples such as timeouts and cache evictions. Durations **must** be expressed as the combination of two parameters: - An int or Integer scalar parameter with the Timeout suffix - A java.util.concurrent.TimeUnit parameter that qualifies the scalar This parameter **must** be named exactly the same as its companion scalar except for adding the TimeUnit suffix. The TimeUnit parameter **should** default to SECONDS. For example: ```java /** * The socket connection timeout value. This attribute works in tandem with {@link #connectionTimeoutUnit}. */ @Parameter @Optional(defaultValue = "5") @Placement(tab = ADVANCED_TAB, order = 1) @Summary("Socket connection timeout value") private int connectionTimeout; /** * A {@link TimeUnit} that qualifies the {@link #connectionTimeout} */ @Parameter @Optional(defaultValue = "SECONDS") @Placement(tab = ADVANCED_TAB, order = 2) @Summary("Time unit to be used in the Timeout configurations") private TimeUnit connectionTimeoutUnit; ``` ## Timeouts Must Have Configurable Configuration Overrides All components that perform operations that could be affected by timeouts **must** make those configurable through [Configuration Override](https://docs.mulesoft.com/mule-sdk/latest/config-override) parameters. Such timeouts **must** be expressed as duration parameters. ## Only Primary @Content Should Default to Payload No parameter other than a primary content **should** default to payload. This means that the following **should** never be used: ```java @Optional(defaultValue = "#[payload]") ``` ## Handle Dates and Time Parameters Date and time parameters **must** be implemented by using java.time.LocalDate, java.time.LocalDateTime, or java.time.ZonedDateTime types. Do not use java.util.Date or java.util.Calendar. ## Check the End-to-End Experience Before publishing your module, test it in Anypoint Studio. Make sure it provides good usability and a good experience. A module that works but is hard to use is not complete. You **must** check for the following: - DataSense is correctly resolved for all input parameters and output values. - Each component’s parameter [layout](https://docs.mulesoft.com/mule-sdk/latest/parameter-layout) is correct. - No typos exist in labels and descriptions. - Users can easily build an application that uses the module’s main use cases. ## Discourage the Use of POJOs The SDK allows using POJOs as both input parameters and output types. However, their use is discouraged. All modules **should** make a best effort to use JSON or XML instead. In the case of dynamic types, Java Maps may also be used. This rule doesn’t mean that there’s no use case for using POJOs, nor that you need to convert all structures to JSON or Java Maps. This rule is merely about favoring data formats that interoperate better with other components and avoid serialization issues. For example, suppose that your organization developed a Java taxes library that you want to leverage in your Mule applications. If this library returns POJOs, it is okay for you to build an SDK module that wraps this library, even if that means that this tax module will accept and produce POJOs. Instead, if what you are leveraging is a Tax API that returns JSON, then your Tax connector **should** return the same JSON as the API instead of transforming that JSON into a POJO. ## Expose POJOs All POJOs that are exposed (because they’re being used as output types, input parameters, or message attributes) **must** be DataWeave compliant. This means that all have a default constructor and a getter method for each @Parameter annotated field. ## User Must Not Be Aware of Java Types The user **must not** need to know the Java type used to implement a module in order to use it. For example, consider the following transformation: ```json { name: "Pedro", address: { x: 11, y: 12 } } as com.foo.Person ``` The module **must** be defined in a way in which explicit transformations such as this are not needed. Some ways of achieving this are: - Don’t use POJOs to represent a person (recommended) - Make com.foo.Person a DataWeave compliant concrete class - When used as an input parameter, refer to com.foo.Person explicitly instead of using an abstract type or interface ## Payload and Attributes Must Be Serializable Message attributes objects returned by either operations or message sources **must** be Java Serializable. Otherwise they cannot be used with ObjectStore, VM queues, or Mule in cluster mode. ## Don’t Access the MuleContext The MuleContext is deprecated and **must not** be accessed by any module or connector. Most of the time, the only reason for a module to access the MuleContext is to obtain certain Runtime service. For example: ```java muleContext.getRegistry().lookupByType(ObjectStoreManager.class) ``` Instead, use @Inject to obtain those services. ```java public class MyComponent { @Inject ObjectStoreManager osm; } ``` ## Log Through SLF4J All logging **must** be made through the SLF4J API. The module **must not** add any dependency with this API as it’s already provided in the parent pom file provided by MuleSoft. ## All Loggers Must Be Static Loggers are expensive to create. Therefore, all loggers **must** be private, static, and final. For example: ```java public class Operation { private static final Logger LOGGER = LoggerFactory.getLogger(Operation.class); ... } ``` ## Avoid Static State All module classes **must not** contain a static state. The only exceptions are Loggers and the serialVersionUID of Serializable classes. ## Choose the SDK Version Normally, developers tend to use the latest available version of any given product. This **should not** be the case with the SDK. To extend Mule, you should: - Identify the features you need - Choose the lower version that includes the features you need. For more on selecting the correct SDK version, see [Choosing the SDK Version](https://docs.mulesoft.com/mule-sdk/latest/choosing-version). ## Do Not Use Boxed Booleans Boolean parameters **must** be of the native boolean type. A boxed Boolean is not allowed as a parameter type. All boolean parameters are considered optional and **must** default to false. You can use the @Optional annotation to change the default of a boolean, but you can never make it a required parameter. ## Use Plural Names for List Parameters All parameters implemented as a Java Collection, Map, or Array, or any other type that represents an array of some sort, such as a JSON array, **must** have a plural name. ## Use Semver Versioning Modules **must** be versioned using the [Semantic Versioning](https://semver.org/) standard. ## Use Strict Camel Casing The entirety of the module’s source code **must** correctly apply camel casing. This is critical since otherwise the SDK will not be able to correctly generate the XML DSL or process layouts. ## Specify Java Compatibility Specify the Java compatibility of your custom module. In Mule 4.6.0 and later, custom modules that do not specify the Java compatibility are assumed to be compatible with Java 8 and Java 11 only. If your custom module is compatible with a more recent Java version, you must specify the Java compatibility to include that version. For more information, refer to [Java Version Support](https://docs.mulesoft.com/mule-sdk/latest/java-version-support). ## See Also - [Salesforce Help](https://help.salesforce.com) - [Mule SDK Development Best Practices](https://docs.mulesoft.com/mule-sdk/latest/best-practices) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, create, new, project, getting, started --- # Getting Started with the Mule SDK for Java > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Create your first Mule SDK project in just a few steps: 1. Install the required software. 2. Generate a project using Maven and explore it. 3. Set up your IDE. 4. Build the project you generated. 5. Develop a connector or module. 6. Run the connector or module in Anypoint Studio. The SDK provides a Maven plugin and a Maven archetype to create new projects. The archetype is a project template; use it to ensure your new project has a consistent structure, employs recommended practices, and contains a few elements for you to experiment with. ## Install the Required Software Install and verify that the following tools are working in your local environment: - Java Development Kit 17 (JDK 17) or earlier to compile and build your custom Java code. - For preexisting connectors, [JDK 8 might be needed](https://docs.mulesoft.com/mule-sdk/latest/java-version-support#jdk_8). - Apache Maven 3.9.8 or later to manage your project’s build. ## Generate a Project Using the Maven Plugin Run the `generate` command to create an extension project based on the Maven archetype: mvn org.mule.extensions:mule-extensions-archetype-maven-plugin:1.11.0:generate You are prompted to provide required field values, then a new project is generated. ## Explore the Generated Project The generated project contains the basic Mule [module structure](https://docs.mulesoft.com/mule-sdk/latest/module-structure) with some basic classes, a dummy implementation, and two running tests that use the generated module. Because this project is a Maven project, it contains a `pom.xml` file that contains all the information and configuration details used by Maven to perform the build. This generated POM file inherits from the module’s parent POM, which contains all the common configurations and defaults for all Mule module projects. The parent `pom.xml` file does not include any release information, so you must add the release repository to the parent `pom.xml` file manually. ```xml org.mule.extensions mule-java-extension-parent 1.11.0 ``` > [!NOTE] The generated POM defines a Minimum Mule Version by default, that version must be > compatible with the target Mule version. See [Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility) for > more information on how to configure and work with Minimum Mule Versions. ## Set Up Your IDE For the SDK to work, you must pass the command line parameter `-parameters` to the Java compiler. ### For Development in IntelliJ If you are using IntelliJ: 1. Go to **Preferences > Build, Execution, Deployment > Compiler > Java Compiler**. 2. Add `-parameters` in the **Additional command line parameters** section. ![IntelliJ menu navigation path to the command line parameter input field](/mulesoft/current/en-us/mule-sdk/latest/_images/intellij-config.png) ## Build the Project After your project is generated, build it: mvn clean install This command compiles your project, runs the tests, and installs the project in your local Maven repository. ## Develop Custom Module Code Find the `@Extension` annotated class and examine [module structure](https://docs.mulesoft.com/mule-sdk/latest/module-structure) and the different elements that you can add to a module. Use these elements to modify a connector. ## Add a Custom Icon to Your Connector Connector icons are files in the .svg format. To customize the icon of a connector, add an `icon.svg` file that contains the custom icon under the `icon` folder in the root of the connector tree. The following image shows an example of the folder structure for the icon: ![The connector root directory has a folder named icon that contains the icon.svg file](/mulesoft/current/en-us/mule-sdk/latest/_images/icon-svg-change.png) After you add the custom icon to your connector, the icon appears in Anypoint Exchange and in Anypoint Studio. ## Add Your Connector to Studio Once your connector is working, you can try it in Anypoint Studio. Go to your Mule app `pom.xml` file and add the connector as a `` with its `groupId`, `artifactId`, and `version`. Example: Dependency ```xml ... ... org.mule.connector mule-connector 1.0.0 mule-plugin ... ... ``` > [!NOTE] Different features are available in different versions of the SDK for Java. See > [Choosing the SDK version](https://docs.mulesoft.com/mule-sdk/latest/choosing-version) to pick the right version for your connector. ## Publish Your Connector or Module to Anypoint Exchange After you run your connector or module in Studio, publish your connector or module to Anypoint Exchange. For more information, refer to [Publishing Assets Using Maven](https://docs.mulesoft.com/exchange/to-publish-assets-maven). --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, security, oauth --- # Handling Expired Access Tokens > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Most services providers return (or should return) access tokens with a limited lifespan. In general, access tokens usually expire about 30 to 60 minutes after being issued, although this is not a standard that strictly enforced by providers. In the same way, providers often provide a refresh token with the access token. The refresh token is a second token that can be used to replace an expired access token with a fresh one, without the need to perform the dance again. Unfortunately, there is no enforced standard that the SDK can use to automatically detect a token expiration scenario and obtain a new one. Different APIs communicate that in different ways. That is why the `AccessTokenExpiredException` exception exists. ```java public void someOperation(@Connection SalesforceRestClient client) { (1) Response response = client.performSomeOperation(); (2) if (response.getStatusCode() == 401) { (3) throw new AccessTokenExpiredException(); (4) } } ```
1The example above uses an imaginary REST client to perform an operation. It assumes that this rest client was created through a ConnectionProvider annotated with @AuthorizationCode.
2It performs the operation and gets a Response object that contains information of the HTTP call.
3The example assumes that a status code of 401 (unauthorize) means that the token has expired.
4It throws the AccessTokenExpiredException.
When the SDK detects this exception, it automatically performs the refresh dance and retries the operation with a new access token. Note that in real life, different APIs notify you of the access token expiration in different ways. Some give you clients that throw exceptions, while others give you custom messages. You need to research the remote API to determine the best way to account for this. --- product: Mule SDK version: 1.13 is-latest-version: true --- # HTTP-Based Connectors > For the full documentation index, see: https://docs.mulesoft.com/llms.txt There is a special set of considerations for connectors that access a remote system by either issuing HTTP requests or exposing HTTP endpoints. ## Use the Mule HTTP Client The Mule HTTP Client **must** be used for making HTTP requests. Other HTTP clients (such as the Jersey client, Apache HTTP Client, Jetty, Netty, and so on) **should not** be used in favor of the Mule HTTP client. The only exception to this rule is when the remote system vendor already provides a client library that has some level of added value that would be too difficult to replicate with the use of the Mule HTTP Client. For example, the Amazon AWS connectors work by consuming an HTTP API that requires custom message signing. Amazon provides a client library that encapsulates the logic necessary to make such signing. However, a client library that does nothing but wrap an HTTP client for convenience (like the Google clients) is not a valid exception to this rule. ### Create the HTTP Client There are two APIs for creating a Mule `HttpClient`: - The `org.mule.sdk.api.http` API (recommended). Its entry point is `HttpService.client(Consumer)`. Because `mule-sdk-api` is bundled with your connector and isn’t coupled to the Mule Runtime lifecycle, a connector can use this API to consume newer HTTP features without raising its Minimum Mule Version. Use this API for new connectors and when migrating an existing connector that needs newer HTTP capabilities. See [SDK Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility) for more about forward compatibility. - The legacy `org.mule.runtime.http.api` API, whose `HttpClient` is created from an `HttpClientConfiguration` builder through `httpService.getClientFactory().create(…​)`. This API remains fully supported and is a valid choice when your connector’s Minimum Mule Version already permits it. See [Use the Legacy org.mule.runtime.http.api HTTP Client](#legacy-http-client). #### Use the org.mule.sdk.api HTTP Client Inject the `org.mule.sdk.api.http.HttpService` and call `client(…​)` with a callback that configures the client through an `HttpClientConfigurer`: ```java @Inject private HttpService httpService; HttpClient client = httpService.client(configurer -> configurer .setName("client-name") // other setters... ); ``` The `HttpService.client(…​)` method returns an `org.mule.sdk.api.http.client.HttpClient` and throws a `ClientCreationException` if the client can’t be created. #### Configure the Client The `HttpClientConfigurer` passed to the callback exposes these methods. Each one returns the same configurer, so you can chain the calls:
MethodDescription

setName(String)

Sets the name of the HttpClient. Base it on the name of the owning configuration (see [set-the-http-client-name]).

setTlsContextFactory(TlsContextFactory)

Required for HTTPS. Provides the TLS data used to establish secure connections.

setMaxConnections(int)

Maximum number of outbound connections kept open at the same time. Unlimited by default.

setUsePersistentConnections(boolean)

Whether connections are kept open after a request completes. true by default.

setConnectionIdleTimeout(int)

Number of milliseconds a connection can remain idle before being closed.

setResponseBufferSize(int)

Size, in bytes, of the buffer used to store the HTTP response. Defaults to 10 KB.

setStreaming(boolean)

Whether the HTTP response is streamed, so processing continues as the body arrives.

setDecompress(Boolean)

Whether responses are decompressed automatically. false by default.

configProxy(Consumer<ProxyConfigurer>)

Configures a proxy for the client. See [provide-http-proxy-configuration].

configHttp1(Consumer<Http1ProtocolConfigurer>)

Enables, disables, and configures HTTP/1-specific parameters.

configHttp2(Consumer<Http2ProtocolConfigurer>)

Enables, disables, and configures HTTP/2-specific parameters.

configClientSocketProperties(Consumer<TcpSocketPropertiesConfigurer>)

Configures TCP-specific properties, such as the socket connection timeout.

For the complete reference, see the `HttpClientConfigurer` interface (package `org.mule.sdk.api.http.client`) in the `mule-sdk-api` javadoc. #### Send a Request The `HttpClient` implements `Startable` and `Stoppable`, so it **must** be started before use and stopped when you no longer need it. Build requests with `HttpService.requestBuilder()` and send them without blocking through `sendAsync(…​)`, which returns a `CompletableFuture`: ```java client.start(); HttpRequest request = httpService.requestBuilder() .method(GET) .uri("https://api.example.com/resource") .build(); CompletableFuture future = client.sendAsync(request, options -> options .setResponseTimeout(5000) .setFollowsRedirect(true)); // ... use the response, then stop the client during the stop() phase client.stop(); ``` Because `sendAsync(…​)` defers response body processing, handle the response on a different thread when you need the full body right away. For example, with `CompletableFuture.whenCompleteAsync(…​)`. This avoids blocking the client’s I/O threads. #### Migrate from the Legacy HTTP Client If your connector currently creates its client with the legacy `org.mule.runtime.http.api.client.HttpClient` and the `HttpClientConfiguration` builder, you can move to the configurer-based API as follows: - Replace the injected `org.mule.runtime.http.api.HttpService` with `org.mule.sdk.api.http.HttpService`. - Replace `httpService.getClientFactory().create(new HttpClientConfiguration.Builder()…​build())` with `httpService.client(configurer → …​)`, mapping each builder setter to the equivalent configurer method (for example, `.setName(…​)`). - Update the imports of `HttpClient`, `HttpRequest`, `HttpResponse`, and related types to their `org.mule.sdk.api.http` counterparts. > [!NOTE] > > - The new API delegates to the legacy one and **does not** deprecate it. > > - The legacy API remains valid. You can keep using it when your connector’s Minimum Mule Version > already permits it. > > - Moving to the new API is the recommended path when a connector wants access to newer HTTP > features without raising its Minimum Mule Version. Because the new HTTP types come from `org.mule.sdk.api`, follow the guidance in [Forward Compatibility Patterns](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-patterns) when you need to call newer methods conditionally and provide fallback behavior for older runtimes. #### Use the Legacy org.mule.runtime.http.api HTTP Client The legacy API creates the `HttpClient` from an `HttpClientConfiguration` builder: ```java httpClient = httpService.getClientFactory() .create(new HttpClientConfiguration.Builder() .setName(configName) .build()); httpClient.start(); ``` This API is still fully supported. Use it when your connector’s Minimum Mule Version already covers it and you don’t need the newer HTTP features exposed by `org.mule.sdk.api.http`. ### Manage the Client in a Connection Provider Regardless of which API creates the `HttpClient`, the connector’s `ConnectionProvider` is responsible for managing it according to these rules. - Handle the HTTP Client Lifecycle The ConnectionProvider that creates the HttpClient is responsible for managing its lifecycle. - Use Cached Connection Providers Every ConnectionProvider that creates an HttpClient **must** implement the CachedConnectionProvider interface. Notice that this rule has implications on the connection object that the Connection Provider can yield. Because of the previously discussed Connection Object **must not** expose (nor be) the inner client rule, this means that the provided connection object needs to contain the HTTP client as part of its state and **must** be thread safe. - Use Startable and Stoppable Connection Providers The Connection Provider **must** also implement the Startable and Stoppable interfaces. The HttpClient **must** be created and started during the start() phase and **must** be stopped during the stop() phase. Lifecycle example: ```java public class HttpConnectionProvider implements CachedConnectionProvider, Startable, Stoppable { @Inject private HttpService httpService; @Parameter private TlsContextFactory tlsContext; @RefName private String configName; @Override public void start() throws MuleException { initialiseIfNeeded(tlsContext); httpClient = httpService.client(configurer -> configurer .setName(configName) .setTlsContextFactory(tlsContext)); httpClient.start(); } @Override public void stop() throws MuleException { if (httpClient != null) { httpClient.stop(); } } // rest of the implementation removed for example simplicity } ``` - Set the HTTP Client Name The HttpClient created by each ConnectionProvider **must** have a name which is based on (or simply matches) the name of the configuration that owns that ConnectionProvider. This is very important for performance troubleshooting. Each HttpClient creates a set of selector threads that it uses for I/O. Those threads are named after the client’s name. Giving those threads a meaningful name is key for diagnosing performance problems. The name of the owning config can be obtained using the @RefName annotation. Here’s an example of how to do this: ```java public class HttpConnectionProvider implements CachedConnectionProvider, Startable, Stoppable { @Inject private HttpService httpService; @RefName private String configName; @Override public void start() throws MuleException { httpClient = httpService.client(configurer -> configurer.setName(configName)); httpClient.start(); } // rest of implementation removed for example simplicity } ``` - Leverage Non-Blocking I/O in Operations The HttpClient is capable of using non-blocking I/O to make the requests. Operations **must** leverage this capability and also be defined as non-blocking. - Do not reference Elements The HttpService allows you to obtain the HttpClient associated with a separate element. Although this capability exists, it is not meant to be used by connectors. Connectors **must not** use this feature. All connectors in need of a HttpClient instance, **must** create and manage their own. - Provide HTTP Proxy Configuration Connectors using HTTP Client instances must provide the ability to configure a proxy, similar to HTTP Connector. Common reasons to use proxy configuration include security, maintenance, and proxy caching. Many organizations also require proxy configuration when working behind firewalls. The following code snippets show how to configure proxy, for example, for the OAuth endpoint in HTTP Request Connector: Use this global proxy configuration as a reference: When configuring for the Client Credentials grant type, use the following snippet: When configuring for the Authorization Code grant type, use the following snippet: ; ; ; When using the `proxyConfig` type, the connection provider must include a `@Parameter` annotation so that the connection refers to the proxy, for example: ```java @Parameter @Optional @Placement(tab = "Proxy", order = 1) private MyConnectorProxyConfiguration proxyConfig; ``` You must also set the proxy configuration in the HTTP Client: ```java .setProxyConfig(connectionParams.getProxyConfig()) ``` For more information, refer to the [Java code](https://github.com/mulesoft/mule-http-connector/blob/9914ce6aa6ceb3aa16a5b821289f15b9c6af6acc/src/main/java/org/mule/extension/http/api/request/proxy/DefaultProxyConfig.java) for how HTTP Connector handles the proxy. ## Exposing HTTP Inbound Endpoints Some connectors need to expose their own HTTP inbound endpoints. To achieve this, connectors **must** reference an external element to obtain an HttpServer instance. Connectors **must not** create their own servers. This **must** be achieved as follows: - Correctly reference the listener config The connection provider **must** expose a String parameter called listenerConfig. This parameter **must** be of type String, not accept expressions, and use the @ConfigReference annotation to indicate that it points to an element. For example: ```java @Parameter @Expression(NOT_SUPPORTED) @ConfigReference(name = "LISTENER_CONFIG", namespace = "HTTP") private String listenerConfig; ``` - Add a RequestHandler Using the listenerConfig parameter added above, a matching HttpServer instance can be obtained like this: ```java HttpServer httpServer; try { httpServer = httpService.getServerFactory().lookup(listenerConfig); } catch (ServerNotFoundException e) { throw new IllegalArgumentException( format("Connector configuration '%s' refers to an with name '%s', " + "but such element doesn't exist", configName, listenerConfigName), e); } ``` With the obtained HttpServer, a new RequestHandler can be added using the org.mule.runtime.http.api.server.HttpServer#addRequestHandler(java.lang.String, org.mule.runtime.http.api.server.RequestHandler) method. The connector **must** carefully follow the contract defined in the Javadocs for that method. Most importantly, invoking that method will return a RequestHandlerManager instance. Those instances **must** be kept by the connector as they expose two important methods: start() and stop(). The inbound endpoint will not be actually functioning until the start() method is invoked on the RequestHandlerManager. At the same time, the endpoint does not go away until the stop() method is invoked on the RequestHandlerManager. That means that when the component owning the custom endpoint is stopped, so **must** the RequestHandlerManager be stopped. Otherwise, your connector will generate a memory leak. ### Make Timeouts Configurable The connector **must** expose a parameter that controls the timeout of the HTTP requests. This **must** be done in a way that is consistent with the previously discussed rules about how to handle duration parameters and making timeouts Config Override parameters. ## HTTPS Security The connector **must** support the HTTPS scheme whenever possible and **must** consider it as the recommended option: - If the remote system supports only HTTPS, then the connector **must** have a required TlsContextFactory parameter. - If the remote system supports both HTTP and HTTPS schemes, then the connector **must** support both options but log a warning when HTTP is used. ## See Also - [Salesforce Help](https://help.salesforce.com) - [Mule SDK Development Best Practices](https://docs.mulesoft.com/mule-sdk/latest/best-practices) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Implicit Configurations > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Some configurations can be classified as implicit. A configuration can be used implicitly when none of its parameters are required. For example, suppose a `myConfig` configuration object which has a required parameter `param1`: ```java ``` Now suppose that the bar parameter were to turn optional instead: ```java ``` In this case, the entire my-config element (and subsequent config-ref) are redundant because they have no real effect. In these cases, implicit configurations can be used: ```java ``` Because `my-config` does not have required parameters, the SDK can create an implicit instance in which all parameters are automatically assigned to their default values. The implicit configuration is either static or dynamic depending on its parameter’s default values. If any of those default values is an expression, then the implicit configuration is dynamic. > [!NOTE] Using an expression as a default value violates the best practice of the > [position independence](https://docs.mulesoft.com/mule-sdk/latest/define-operations#position-independence). ## Mixing Implicit and Explicit Configurations You can use implicit and explicit configurations in tandem: ```java ``` > [!TIP] Only one implicit `config` is created per configuration type. If two operations try to use > implicit configurations of the same type, each uses the exact same instance: ```java ``` ## Forcing Explicit Configurations Available since version 1.3. There are cases in which you might want to force configurations to always be explicit, even if they contain no required parameters. Usually, such requirements revolve around clarity and usability concerns. Use the `@NoImplicit` annotation to instruct the SDK to never build implicit instances of a given configuration type: ```java @Extension(name="example") @NoImplicit @Operations(ExampleOperations.class) public class ExampleModule { @Parameter @Optional public String bar; } ``` You can also use this annotation if your configuration has been externalized into a separate class: ```java @Extension(name="example") @Configurations(MyConfiguration.class) public class ExampleModule { } @Configuration(name="myConfig") @NoImplicit @Operations(ExampleOperations.class) public class MyConfiguration { @Parameter @Optional public String bar; } ``` --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, forward compatibility, implicit parameters --- # Implicit Parameters > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Implicit parameters are parameters that the Mule runtime engine automatically injects without requiring configuration from the application developer. Examples include `StreamingHelper`, `ComponentLocation`, and `CorrelationInfo`. These parameters don’t appear in the Extension Model. ## Implicit Parameter Types from org.mule.sdk.api Starting with Mule 4.5, the runtime automatically treats parameters with types from `org.mule.sdk.api` and its subpackages as implicit, enabling forward compatibility for new implicit parameter types. When you use a new implicit parameter type that was introduced in a later Mule version: - On runtimes that support the type, the parameter is injected normally - On earlier Mule runtime versions that don’t recognize the type, the parameter resolves to `null` and the runtime treats the parameter as implicit, meaning it doesn’t appear in the Extension Model and can’t be configured by the application developer. ## Handle Null Values Implicit parameters from `org.mule.sdk.api` resolve to null on earlier Mule runtime versions. Your code must handle this possibility: ```java import org.mule.sdk.api.runtime.SomeNewHelper; public class MyOperations { public void myOperation(String input, SomeNewHelper helper) { if (helper != null) { // Use the helper on runtimes that support it helper.doSomething(); } else { // Fallback for older runtimes } } } ``` > [!IMPORTANT] If the implicit parameter is essential to your operation and no fallback is possible, > annotate the operation with `@MinMuleVersion` to restrict it to runtimes that support the > parameter type. ## See Also - [Forward Compatibility Patterns](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-patterns) - [Working with Minimum Mule Versions](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-min-mule-version) - [SDK Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: parameter, parameters, mule, sdk, dsl, xml, import, type --- # Importing Types From Other Modules > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Every module can declare a set of types that are part of the module’s API. This set of types can then be imported by other modules, while keeping the original module’s namespace definition. Importing another module’s types is useful in many cases, including: - Reusing the logic bundled inside the type, such as a specific authentication method. - Exposing the user to the same API for configuring the same properties. For example, if you want to configure the same parameters in the same order, with the same descriptions, you can enforce that by using the same types in different modules. You can reuse a module’s type in three steps: 1. Add the dependency in your `pom.xml` to the `mule-plugin` whose API you want to use: ```xml org.mule.connectors mule-sockets-connector (1) ${muleSocketsConnectorVersion} mule-plugin (2) ```
1The module where the Type is declared
2Remember to add the mule-plugin classifier in the dependency
2. Declare an `@Import` in the Extension class referencing the type you want to import: ```java @Extension(name = "HTTP") @Import(type = TcpClientSocketProperties.class) @Import(type = TcpServerSocketProperties.class) public class HttpConnector { } ``` 3. Use the types in your parameters declaration: ```java @Alias("request") public class HttpRequesterProvider implements CachedConnectionProvider { @Parameter private TcpClientSocketProperties clientSocketProperties; } ``` Notice how this changes the DSL declaration, since now your extension (in this case `http`) can receive parameters declared in another namespace (in this example, `sockets`): ```xml ``` --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, metadata, datasense, input, type --- # Input Metadata > For the full documentation index, see: https://docs.mulesoft.com/llms.txt We refer as input Metadata to the type resolution for the Parameters of a Component. Each Parameter can provide either Static or Dynamic Metadata isolated from what kind of Metadata do other Parameters of the same Component expose. Only Operations can have Parameters with dynamic metadata, while Sources, Configurations and Connections will always have static metadata for their Parameters. ## Declaring an InputTypeResolver An `InputTypeResolver` implementation handles the request for resolving a `MetadataType` based on the information provided by the `MetadataContext` and most importantly, using the `MetadataKey` to identify which is the `MetadataType` required by the app developer. Refer to [What is the Metadata of a Component](https://docs.mulesoft.com/mule-sdk/latest/metadata#component-metadata) for more information about the `MetadataType`. ```java public class InputEntityResolver implements InputTypeResolver { @Override public String getCategoryName() { return "DocEntities"; } @Override public MetadataType getInputMetadata(MetadataContext context, String key) throws MetadataResolvingException, ConnectionException { final ObjectTypeBuilder objectBuilder = context.getTypeBuilder().objectType(); switch (key) { case "Author_id": objectBuilder.addField().key("name").value().numberType(); objectBuilder.addField().key("lastName").value().stringType(); break; case "BookList_id": objectBuilder.addField().key("genre").value().stringType(); objectBuilder.addField().key("bookIds").value().arrayType().of().stringType(); break; case "Book_id": objectBuilder.addField().key("title").value().stringType(); objectBuilder.addField().key("ISBN").value().numberType(); break; default: throw new MetadataResolvingException("Unknown key:" + key, INVALID_METADATA_KEY); } return objectBuilder.build(); } } ``` In the example above, we are using the `MetadataContext` only to obtain the `typeBuilder`, but we could also use the provided Configuration and Connection elements. For example, we could fetch the Metadata from an external service using the Connection, or access an static resource present in the Configuration like an schema. ## Using the InputTypeResolver Now that we have a `TypesKeysResolver` and an `InputTypeResolver` we can add input DataSense support on our Operations and Sources. The main restriction to use an `InputTypeResolver` along with a given `TypesKeysResolver` is that both have to belong to the same `category`, so we can guarantee that the `MetadataKey` provided by the `TypesKeysResolver` can be resolved to a `MetadataType` by the `InputTypeResolver`: ```java public class DemoOperation { public void create(@Connection MyConnection connection, @MetadataKeyId(EntityKeysResolver.class) String type, @TypeResolver(InputEntityResolver.class) Map entity){ // Code that handles the creation based on the "type" of the "entity" if ("Account_id".equals(type)){ //... } else { //... } } } ``` Each Parameter can have its own `TypeResolver` each using any `InputEntityResolver`. This means that both can have the same resolver thus describing the same MetadataType for the two parameters, or using the same `MetadataKeyId` to describe two different `MetadataType`, one for each Parameter. For example, we can do an entity merge using the same resolver for two parameters: ```java public void merge(@MetadataKeyId(EntityKeysResolver.class) String type, @TypeResolver(InputEntityResolver.class) Map first, @TypeResolver(InputEntityResolver.class) Map second){ // Code that handles the merge based on the "type" of the "entity" if ("BookList_id".equals(type)){ ((List)first.get("bookIds")).addAll((List) second.get("bookIds")); } else { //... } } ``` Or we instead need to define two different `MetadataType` for each entity, but always bound to the same `MetadataKeyId` (which is resolved only once) and, again, all belonging to the same `category`: ```java public void crete(@MetadataKeyId(EntityKeysResolver.class) String type, @TypeResolver(ItemTypeResolver.class) Map item, @TypeResolver(ContainerTypeResolver.class) Map container){ // Code that handles the item based on the "type" of the "entity" if ("developer".equals(type)){ createDeveloperInTeam(item, container); } else { //... } } ``` ## Input Metadata with User defined MetadataKey In the previous section we saw that when the `MetadataKeyId` parameter doesn’t declare a `TypeKeysResolver`, then any key can be defined by the app developer when configuring the Operation. Here is a simplified example of how the `MetadataKeyId` is used without a `TypeKeysResolver`, and the value configured for that Parameter is used to resolve the input MetadataType of the `queryParameters` in the Database Connector: ```java public List> select(@MetadataKeyId String sql, @TypeResolver(DbInputMetadataResolver.class) Map queryParameters, @Config DbConnector connector){ //... } ``` ```java public class DbInputMetadataResolver implements InputTypeResolver { @Override public String getCategoryName() { return "DbCategory"; } @Override public MetadataType getInputMetadata(MetadataContext context, String query) throws MetadataResolvingException, ConnectionException { // The MetadataKey is the `query` parameter, we have to parse it to resolve // all the Metadata of its parameters QueryTemplate queryTemplate = parseQuery(query); List inputParams = queryTemplate.getInputParams(); if (inputParams.size() == 0) { // No input metadata when no input parameters return context.getTypeBuilder().nullType().build(); } PreparedStatement statement = getStatement(context, queryTemplate); return getInputMetadataUsingStatementMetadata(statement, inputParams); } } ``` ## Resolving dynamic Input Metadata without MetadataKey Many times we don’t have multiple entities whose structure is unknown but just one, that has a dynamic MetadataType depending, for example, on the credentials that the app developer is using to connect to it’s sandbox. In that case, different accounts may see different properties for the same `Organization` entity. In order to declare a `KeyLess` InputMetadata resolution, just skip the `MetadataKeyId` Parameter and use the `TypeResolver` without depending on the `MetadataKey`: ```java public void createOrg(@TypeResolver(OrganizationTypeResolver.class) Map organization){ //... } ``` ```java public class OrganizationTypeResolver implements InputTypeResolver { @Override public String getCategoryName() { return "Organization"; } @Override public MetadataType getInputMetadata(MetadataContext context, Object key) throws MetadataResolvingException, ConnectionException { // The `key` parameter will be `null` if the fetch is performed // as a `KeyLess` Metadata resolution. We'll just ignore it. DemoConnection connection = context.getConnection() .orElseThrow(() -> new MetadataResolvingException("A connection is required to resolve Metadata but none was provided", FailureCode.INVALID_CONFIGURATION)); String schema = connection.getClient().describeOrganization(); return new JsonTypeLoader(schema).load("http://demo.org") .orElseThrow(() -> new MetadataResolvingException("No Metadata is available for the Organization", FailureCode.NO_DYNAMIC_TYPE_AVAILABLE)); } } ``` --- product: Mule SDK version: 1.13 is-latest-version: true --- # Java EE Libraries > For the full documentation index, see: https://docs.mulesoft.com/llms.txt In the first Mule runtime version that exclusively supports Java 17 (Mule 4.9.0), connectors can no longer include Java EE libraries. To ensure backward compatibility, Mule versions 4.6.0 through 4.9.0 continue to export Java EE libraries as part of the Container API only when Mule runtime runs on Java 8 or Java 11. However, if Mule runtime runs on Java 17 or higher, you must include the Java EE libraries manually. For connectors that support multiple Java versions, such as Java 11 and Java 17, the Java EE libraries must match the versions included in Mule runtime. Because Mule 4.6.x exposes Java EE libraries differently than earlier Mule versions, you must add the BOM dependency to your connector and, if applicable, exclude the provided conflicting library. You can also add the libraries in the BOM dependency separately. ```java 4.6.0 org.mule mule-javaee-runtime-bom ${muleJavaEeBomVersion} pom import ``` ## See Also - [Upgrading Java for Custom Connectors (Customers)](https://docs.mulesoft.com/general/customer-connector-upgrade) - [Upgrading Java for Custom Connectors (Partners)](https://docs.mulesoft.com/general/partner-connector-upgrade) - [Java Support](https://docs.mulesoft.com/general/java-support) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Java Version Support > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Specify the Java compatibility of your custom module. In Mule 4.6.0 and later, custom modules that do not specify the Java compatibility are assumed to be compatible with Java 8 and Java 11 only. If your custom module is compatible with a more recent Java version, you must specify the Java compatibility to include that version. Communicating the Java compatibility of your custom module also ensures: - Safe upgrades Users can determine whether their Mule applications can safely upgrade to run on a given Java version, ensuring a smooth transition and avoiding potential issues. - User experience Providing clear information about Java compatibility enhances the user experience, especially for Anypoint Exchange users who rely on this information for selecting modules. ## Specify Java Compatibility To specify Java compatibility of your custom module: 1. Generate metadata to specify Java compatibility of your custom module by adding or upgrading the custom module `mule-sdk-api` dependency to the latest version, for example: ```xml org.mule.sdk mule-sdk-api 0.11.0 ``` 2. Add the `@JavaVersionSupport` annotation in the same class as the `@Extension` annotation and include the supported Java versions as values, for example: ```java import org.mule.sdk.api.annotation.JavaVersionSupport; import org.mule.sdk.api.meta.JavaVersion; @Extension(name = "Database") @Operations(...) @JavaVersionSupport({JavaVersion.JAVA_8, JavaVersion.JAVA_11, JavaVersion.JAVA_17}) public class DatabaseConnector { .. } ``` > [!NOTE] If the `mule-modules-parent` version is earlier than 1.9.0, all code, including > third-party dependencies, **must** be compiled for Java 8, and you must use Java 8 to build the > connector. You can mark your custom module as compatible with a single Java version only; however, you must ensure that no adoption or backward compatibility issues exist. When you deploy a Mule app, Mule verifies that all modules in the Mule app are compatible with the Java version. If Mule finds an incompatibility, Mule throws an error and the application does not deploy. If your code is compatible with a given Java version but you do not declare that compatibility, you can still get a successful test run (only when running a test on Java 8). ## See Also - [Upgrading Java for Custom Connectors (Customers)](https://docs.mulesoft.com/general/customer-connector-upgrade) - [Upgrading Java for Custom Connectors (Partners)](https://docs.mulesoft.com/general/partner-connector-upgrade) - [Java Support](https://docs.mulesoft.com/general/java-support) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, module, extension, components, structure --- # Learning the Module Model > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The entry point for building a Mule module is the `@Extension` annotated class. This class is used to export all the module functionality and all the elements associated with it. > [!WARNING] This class **must** be a public, non-abstract class. The `@Extension` annotation cannot > be used on interfaces, abstract classes, private/protected classes, or typed classes (with > generics). ```java @Extension(name = "basic") public class BasicModule { } ``` When using the `@Extension` annotation, you need to provide a descriptive and concise name for your module. You should not use `extension`, `module`, or `connector` in the module name. If you do, the SDK will trim them. Optionally, you can specify the attribute `vendor` and the attribute `category` (See [Licensing](https://docs.mulesoft.com/mule-sdk/latest/license)). Once you have created an empty module, you can start adding elements to it. ![Model of a basic module](/mulesoft/current/en-us/mule-sdk/latest/_images/model.png) Figure 1. Basic Module Structure At a high level, the elements are: - [Components](#components-2) - [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations) - [Sources](https://docs.mulesoft.com/mule-sdk/latest/sources) - [Functions](https://docs.mulesoft.com/mule-sdk/latest/functions) - [Routers](https://docs.mulesoft.com/mule-sdk/latest/routers) - [Scopes](https://docs.mulesoft.com/mule-sdk/latest/scopes) - [Connection Providers](https://docs.mulesoft.com/mule-sdk/latest/connections) - [Configurations](https://docs.mulesoft.com/mule-sdk/latest/configs) - [Parameters](https://docs.mulesoft.com/mule-sdk/latest/parameters) ## Components Components are the key to the functionality of a module. Given a set of parameters, they can be executed to produce or alter messages in a flow. Sources and Operations are the most common components in a module: - [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations) are components that process an incoming message and generate a result. - [Sources](https://docs.mulesoft.com/mule-sdk/latest/sources) are components that receive or generate new messages to be processed by the Mule Runtime. ## Configuration Configurations are a set of configurable parameters that affect the behavior of the Module. [Sources](https://docs.mulesoft.com/mule-sdk/latest/sources) and [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations) typically depend on a Configuration for parameters that determine their behavior. However, some can work on their own without a Configuration. There is no requirement to declare a Configuration for a Module, but you can declare as many as you need. Different configurations might provide a different set of parameters and specify a set of [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations) and [Sources](https://docs.mulesoft.com/mule-sdk/latest/sources) that are only available when using that configuration. ## Connection Providers A [Connection Provider](https://docs.mulesoft.com/mule-sdk/latest/connections) is the Module element in charge of handling connections, which are created from a [Configuration](https://docs.mulesoft.com/mule-sdk/latest/configs). They centralize the logic and parameterization to provision and release connections of some specific type. Like the [Configurations](https://docs.mulesoft.com/mule-sdk/latest/configs), [Sources](https://docs.mulesoft.com/mule-sdk/latest/sources), and [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations), Connection Providers typically depend on a connection in order to be executed. A connection is not required but is common. Connections used by [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations) and [Sources](https://docs.mulesoft.com/mule-sdk/latest/sources) are provided by the [Connection Provider](https://docs.mulesoft.com/mule-sdk/latest/connections). [Connection Providers](https://docs.mulesoft.com/mule-sdk/latest/connections) are bound to [Configurations](https://docs.mulesoft.com/mule-sdk/latest/configs). [Configurations](https://docs.mulesoft.com/mule-sdk/latest/configs) can have none, one, or many [Connection Providers](https://docs.mulesoft.com/mule-sdk/latest/connections) that create different types of connections, for example, one `ConnectionProvider` for HTTPS and another for (non-secured) HTTP connections. ## Parameters `Parameters` are the most granular elements on the module model, and they are used in all these elements: [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations), [Sources](https://docs.mulesoft.com/mule-sdk/latest/sources), [Configurations](https://docs.mulesoft.com/mule-sdk/latest/configs), and [Connection Providers](https://docs.mulesoft.com/mule-sdk/latest/connections). They represent configurable elements that accept the values for elements to execute, or they hold the configured values. ## Other Elements A module can have other element types, for example, [Routers](https://docs.mulesoft.com/mule-sdk/latest/routers), [Scopes](https://docs.mulesoft.com/mule-sdk/latest/scopes), [Functions](https://docs.mulesoft.com/mule-sdk/latest/functions), or [Errors](https://docs.mulesoft.com/mule-sdk/latest/errors). The ones described here are the core elements that most modules are likely going to have. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, sources, listener, triggers, lifecycle --- # Lifecycle of a Message Source > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Unlike operations, where the lifecycle is tied to the owning Flow, sources can be started or stopped separately through Runtime Manager. To support that, the `Source` class forces you to implement the `onStart()` and `onStop()` methods. ## On Start This example from the VM connector shows the `onStart()` method: ```java @Override public void onStart(SourceCallback sourceCallback) throws MuleException { } ``` In this method, you have to initialize all the resources (such as threads, schedulers, connections, and so on) that you need so that you can produce messages. If an exception or error condition is found trying to do that, then you have to: - Release any resources you did got to allocate. - Throw an exception with a meaningful message. If the source fails to start, the app that contains it will fail to start. > [!TIP] All of the source parameters values that defined through the `@Parameter` annotation are > resolved and available at this point. ### SourceCallback The method receives a SourceCallback object. The `SourceCallback` is arguably the most important piece of the Source, since it is involved in pretty much every aspect of the source’s functionality. We will elaborate on the uses and responsibilities of the `SourceCallback` as this documentation progresses, but in a nutshell, the `SourceCallback` is what the source uses to communicate with the runtime. It is used for: - Pushing messages into the Flow - Communicating connection issues and triggering reconnection - Passing state between the push phase of the Source into the response handling part Because the `SourceCallback` plays such an important role, it is very important that the provided instance is always available. Depending on how your source’s complexity and code organization, you may accomplish this by simply holding it as a method argument. More complex sources might need to keep a reference to it through an instance field. If you decide to do this, then you need to make sure that the reference is cleared when the `onStop()` method is executed ## On Stop This method will be invoked by the runtime to make the source stop producing messages. **This method should not fail**. Any exceptions found during the stop process should be logged and correctly handled by the source, but after invoking this method the source must: - Stop producing messages - Free any allocated resources - Be capable of being restarted ## Next step - [Obtain configuration and connections from a Message source](https://docs.mulesoft.com/mule-sdk/latest/sources-config-connection) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, metadata, datasense, keys, type --- # Metadata Keys Parameter > For the full documentation index, see: https://docs.mulesoft.com/llms.txt To describe a dynamic metadata structure, you need to know what type to represent. This type reference is done by defining a `@MetadataKeyId` parameter in the operation that contains the ID of the type (for example, Account) that will be passed to the metadata resolver defined for that parameter. This means, for example, that if your operation can save a generic record to a remote system, but you want to provide a better design-time experience by describing the supported types `Account` and `Organization`, then one of your operation parameters will be a type reference containing either the ID `Account` or the ID `Organization`. This parameter will be used to describe either the Account structure or the Organization structure for the record to be saved into the bucket, depending on what the end user decides to use. ## Providing a List of Metadata Keys The most common case when resolving dynamic metadata is to have a dynamic set of keys that identify each of the possible types to be resolved. At design time, the end user will configure the connector, select an operation, and choose one of those keys from the set that gets populated dynamically, and the selected key will become the `MetadataKeyId` for the metadata resolution. Implementing the dynamic key’s resolution starts with a `TypeKeysResolver`: ```java public class RecordKeysResolver implements TypeKeysResolver { @Override public String getCategoryName() { return "Records"; } @Override public Set getKeys(MetadataContext context) throws MetadataResolvingException, ConnectionException { // First we need to obtain the key IDs that will be used. // This is where you'd normally use `context.getConfig()` // or `context.getConnection()` to obtain the entity id's // from a remote service, for this demo the keys will be fixed List keyIds = Arrays.asList("Author_id", "BookList_id", "Book_id"); HashSet keys = new HashSet<>(); for (String id: keyIds){ // For each possible if, we create a new MetadataKey with that ID MetadataKeyBuilder builder = MetadataKeyBuilder.newKey(id); // Then, we add a DisplayName to that MetadataKey, // this will be the name shown in the UI builder.withDisplayName(StringUtils.removeEnd(id, "_id")); //finally, add the key to the Set of known MetadataKeys keys.add(builder.build()); } return keys; } } ``` Above, all the elements that are required to create a dynamic set of MetadataKeys. The method to implement is `getKeys`, which outputs all the keys available to the user. The `id` element, like `Author_id` is used internally and must be unique for each structure that you want to represent across all the resolvers. The `displayName` is the name to show to the user for that key, so it is expected to be more user friendly and readable. After the resolver is defined, you can use it as an operation or a source by adding the `@MetadataScope` annotation with its `keysResolver` value at the class definition level and annotating the parameter that is the key with `@MetadataKeyId`. All metadata resolvers must belong to the same category in an operation or a source. ```java @MetadataScope(keysResolver = RecordKeysResolver.class) public class OperationWithMetadataKey { public void create(@MetadataKeyId String type, @Config MyConfig config){ //... } } ``` ```java @MetadataScope(keysResolver = RecordKeysResolver.class) public class DocsMetadataSource extends Source, Void> { @MetadataKeyId @Parameter public String type; //... } ``` > [!NOTE] The previous example is the only way to support metadata keys for sources. Do not set the > resolver at the `@MetadataKeyId` annotation when defining sources. Setting the `@MetadataScope` annotation at the operation class definition level indicates that all of its operations provide metadata and that they must all have a parameter annotated with `@MetadataKeyId` because they inherit the resolver from the scope. You can also set a key resolver for a single operation: ```java public class MyOperations { public void operationWithMetadataKey(@MetadataKeyId(RecordKeysResolver.class) String type, @Config MyConfig config){ //... } public void plainOperation(@Config MyConfig config){ //... } } ``` In addition, if there is a `@MetadataScope` annotation at the operation class definition level, you can set a value for the `@MetadataKeyId` annotation in a parameter to override the resolver defined at the scope level for a single operation: ```java @MetadataScope(keysResolver = ClassLevelResolver.class) public class MyOperations { public void operation(@MetadataKeyId String type, @Config MyConfig config){ // This operation will use the ClassLevelResolver } public void operationWithAnotherResolver(@MetadataKeyId(OverridingResolver.class) String type, @Config MyConfig config){ // This operation will use the OverridingResolver } } ``` > [!IMPORTANT] The two previous examples (setting the resolver at the parameter level and overriding > the inherited resolver from the scope) work only when defining operations. These features are not > available for sources. ## User-Defined MetadataKey An user-defined `MetadataKey` is one that is provided freely by the end user and not defined by you as one of the possible elements of a List. For example, this is a very common case in queries, where you use the whole query that the end user wants to execute as a `MetadataKey`. To declare a user-defined `MetadataKey`, annotate a parameter with `@MetadataKeyId` without providing a `TypesKeyResolver` for the parameter annotation or for the `@MetadataScope` annotation if it is present at the class definition. ```java public class MyOperations { public void query(@MetadataKeyId String query){ //... } } ``` ## Optional MetadataKey Parameters Available since version 1.3. You can make `MetadataKey` parameters optional like other parameters, by adding the `@Optional` annotation: ```java public void query(@MetadataKeyId @Optional String query){ //... } ``` When an optional MetadataKey parameter is not assigned a value, metadata calls are made using `null` as the value of `MetadataKey`. This means that any `MetadataResolver` parameter associated with this `MetadataKey` parameter must be prepared to receive a null value. --- product: Mule SDK version: 1.13 is-latest-version: true --- # Module Licensing > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Modules written with the SDK have three possible kinds of licensing, each setting different requirements on how the application can be executed. ## Community Module The default for all Modules, apps that use a Community Module can be executed in all Mule runtimes with no further requirements. ## Enterprise Module Only Mule EE Runtimes can be used to execute the apps that use the Enterprise Modules, which means that the user of the Module needs a MuleSoft Enterprise License. Declaring a Module to be EE-only is very easy. You simply need to add the `@RequiresEnterpriseLicense` annotation: ```java @Extension(name = "enterprise") @RequiresEnterpriseLicense public class EnterpriseConnector { } ``` By default, `RequiresEnterpriseLicense` forces the user to have a full MuleSoft Enterprise License, but you can also declare that the Module allows an evaluation license to be used for the Runtime. This can be configured using the `allowEvaluationLicense` flag in the `RequiresEnterpriseLicense` annotation: ```java @Extension(name = "enterprise") @RequiresEnterpriseLicense(allowEvaluationLicense = true) public class EnterpriseConnector { } ``` ## Custom Module License MuleSoft Certified Connectors can request a _certified license generation key_ from MuleSoft that allows them to create their own Custom License. This ensures that those who use the Certified Connector contact the Partner that developed the Module to obtain their Custom License. Partners who have built a MuleSoft Certified Connector can give potential customers the chance to build a Mule app with their connector and test it at design time. When attempting to deploy the Mule app with the connector, customers are prompted for the Partner’s Custom License. ### Obtaining a License Generation Key for your Connector Before submitting the developed connector to MuleSoft for certification, the prospective certified connector partner must complete these steps: 1. Include the following information for MuleSoft to use before generating a partner key and keystore: - Company Name (also known as "Vendor Name") - Contact Email - Contact Message - Connector Name 2. Request the key and keystore from MuleSoft: 1. The public `.key` is the file that you store in the connector. 2. The `.jks` keystore is for you as the partner to generate licenses with. ## Adjusting the Connector Code and Storing the Public Key After obtaining the key and keystore: 1. The partner adjusts the connector project code by applying the `@RequiresEntitlement` annotation and entering values for all requested attributes. - `name` must be the name of the connector. - `vendor` attribute must be the part before the `.key` suffix. For example, if MuleSoft generated a vendor key `prov-name.key`, the partner puts `prov-name` as the vendor. ```java @Extension(name="connector-name", vendor="prov-name") @RequiresEntitlement(name="connector-name") public class LicensedConnector { ... } ``` - Put the MuleSoft-provided `.key` file under `src/main/resources` or `src/main/resources/license` in the connector’s project directory. ### Sample Partner Connector You can download and import the sample connector to Studio to see how the license management annotations are configured in the connector code. The customer `.key` license file should reside in `src/main/resources`. ## Producing a License File MuleSoft partners use the `.jks` file and the license generation plugin to generate a license to send to the connector customer. From the command line, where `vendor.keystore` should be replaced with the path to your `.jks` file, execute the command in the same directory where the `.jks` file is: mvn org.mule.tools.devkit:mule-devkit-license:license -Dvendor.keystore=betaprovider-license-demo.jks > [!NOTE] Optionally, you can specify the expiration date of the license by appending > `-Ddays.to.expire=` to the above command. To add the customer’s > name to the license, append `-Dclient=`. If the license is successfully created, the output looks something like this: \[INFO\] Scanning for projects... \[NOTE\] \[INFO\] ------------------------------------------------------------------------ \[INFO\] Building Maven Stub Project (No POM) 1 \[INFO\] ------------------------------------------------------------------------ \[NOTE\] \[INFO\] --- mule-devkit-license:license (default-cli) @ standalone-pom --- \* Enter the name of the client whom will receive this license: Bob \* Enter the name of the provider of the Entitlement as it appears in the @RequiresEntitlement annotation: betaprovider \* Enter the name of the Entitlement as it appears in the @RequiresEntitlement annotation: lic-demo \* Enter the name of the Connector as it appears in the @Connector "name" attribute: license-demo \* Enter keystore password: \[INFO\] ------------------------------------------------------------------------ \[INFO\] BUILD SUCCESS \[INFO\] ------------------------------------------------------------------------ \[INFO\] Total time: 18.151s \[INFO\] Finished at: Thu Dec 10 09:34:41 ART 2015 \[INFO\] Final Memory: 6M/287M \[NOTE\] ## Developing a Mule Application with a Licensed Connector For customers to validate that their connector license is working, they need to make the license (`.lic` file) available through either the Mule app or the Mule server. ### License File in the Mule Application Once the customer has bought a license for the connector, the `.lic` file provided to the customer by the partner or vendor can be placed in one of these directories: - Under `src/main/resources` in the app (developed in Studio). - Under `app/classes` if the app has already been unpacked and deployed on the server. ### License File in the Mule Server Instead of storing the license in the app, you can be place in the `conf/` folder of the Mule server. > [!NOTE] Storing the license in the Mule server is the recommended approach if multiple apps > require the same license. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, connection, connectivity, management, pooling, cached, provider, connection-provider --- # Modules Connectivity Framework > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Connected modules (connectors) are the most common type of module. They provide a connection between a Mule flow and an external resource. Configure these connections with the `ConnectionProvider` in a connectivity framework to create, disconnect, and describe the connection for Mule runtime engine to handle. For more information about how to use connections, refer to [Using Connections](#use-connections). ## Declaring a Connection Provider To declare a new `ConnectionProvider` you should create a new class that implements the `ConnectionProvider` interface. The `ConnectionProvider` interface is a typed interface that receives one type argument that indicates the class of the instances that the `ConnectionProvider` handles. This example declares a `MyConnectionProvider` class that implements the `ConnectionProvider` interface: ```java public class MyConnectionProvider implements ConnectionProvider { @Parameter private String password; public MyConnection connect() throws ConnectionException { return new MyConnection(password); } public void disconnect(MyConnection connection) { connection.invalidate(); } public ConnectionValidationResult validate(MyConnection connection) { return connection.isValid() ? ConnectionValidationResult.success() : ConnectionValidationResult.failure("Connection is not valid", new InvalidConnectionException()); } } ``` > [!NOTE] Each defined `ConnectionProvider` must be instantiable, public, and have a default > constructor. Other components (such as an operation or source) that require access to the connection will gain access to the connection through instances of the `MyConnection` class. The above example contains a `@Parameter` and these methods: `connect()`, `disconnect()`, and `validate()`. The next sections describe each of them. ### Connection Provider Parameters Like any other `Module` element, a `ConnectionProvider` can have `@Parameters`. Each `ConnectionProvider` `@Parameters` must be a configurable element that is required to instantiate the actual connection, in the `connect()` method. > [!WARNING] Common `@Parameters` that are required by more than one operation must **not** be part > of the `ConnectionProvider`. That’s a common case for [Configurations](https://docs.mulesoft.com/mule-sdk/latest/configs) `@Parameter`. See > [`@Parameter` section](https://docs.mulesoft.com/mule-sdk/latest/parameters). ### Connect The `connect()` method is in charge of instantiating the actual connections with all the configured parameters. It’s **very important** to know that the returned connection is expected to be ready to use. The returned **connection** does not need to implement an interface or contract. It doesn’t necessarily reference the socket connection. It can simply be a client or something of that sort. ### Disconnect The `disconnect(Connection)` method receives a live connection and disposes it, freeing all it’s allocated resources. ### Validate As mentioned [above](#_connections_connect), the `connect()` method in the `ConnectionProvider` **must** return a valid, fully functional connection. Because connections might become stale while sitting idle, the `ConnectionProvider` provides the `validate(Connection)` method. This method is invoked to check the connection before using it. If the validation fails, the connection will be invalidated and a new one will be taken. #### Connection Validation Result The `validate(Connection)` method will return an object of type `ConnectionValidationResult`, which can result in two types: a `ConnectionValidationResult.success()` or a `ConnectionValidationResult.failure()`. When a `failure()` is returned, a descriptive message and a failing `Exception` (which provides context about why the connection is invalid) should be provided to the new `failure()`. Optionally, an `ErrorType` can be provided to add the [error handling](https://docs.mulesoft.com/mule-sdk/latest/errors) capabilities. - `failure(String, Exception)` - `failure(String, ErrorType, Exception)` ## Adding a Connection Provider Each `ConnectionProvider` is bound to a Configuration that can have none, one, or more than one `ConnectionProvider`. To add a `ConnectionProvider` implementation to a Module Configuration, you need to use a `@ConnectionProviders` annotation. This annotation declares each `ConnectionProvider` that a `Module` defines. It can be used at the `@Extension` or `@Configuration` level. If an extension contains more than one `ConnectionProvider`, you need to use the `@Alias` to provide each with a significant name. By convention, the name of that connection provider adds the `` `-connection `` suffix to the provided name. Example of a `ConnectionProvider` declared at `@Extension` level: ```java @Extension(name="xyz") @Operations(Operations.class) @ConnectionProviders(MyConnectionProvider.class) public class MyModule { @Parameter private String someParameter; public String getSomeParameter() { return someParameter; } } ``` Example of the XML for this config with the example `MyConnectionProvider`: ```xml ``` ## Connection Management You can specify a few connection management strategies for a `ConnectionProvider`. ### Pooling Strategy The connections are pooled. A cache of connections is maintained so that they can be reused when future requests require one. This is useful when connections are expensive to create or when concurrent access to a connection instance is not supported. For this pooling strategy, a pooling profile configuration is automatically added to the connection. The following table shows the default values for the pooling profile of type **Use default connection pool configuration**:
NameDefault Value

Max Active

5

Max Idle

5

Max Wait

5

Exhausted Action

GROW

Initialisation Policy

INITIALIZE_ONE

Min Eviction Millis

1800000

Eviction Check Interval Millis

-1

You can enable pooling connection management by implementing the `PoolingConnectionProvider` interface. Refer to [Pooling Profiles](https://docs.mulesoft.com/mule-runtime/latest/tuning-pooling-profiles) for more information. ### Cached Strategy For each configuration instance, a connection is established and cached so that one specific configuration always gets the same connection. The connection is maintained until the configuration itself is stopped. You can enable cached connection management by implementing the `CachedConnectionProvider` interface. Refer to [Caching](https://docs.mulesoft.com/mule-runtime/latest/tuning-caching) for more information. ### No Strategy The runtime adds no additional infrastructure similar to the pooling cases. The `ConnectionProvider` is responsible for providing the totality of the connection-handling behavior. An operation that requires a connection calls the `connect()` method each time it needs a connection, and it destroys the connection once the operation is complete. Implementing the `ConnectionProvider` interface means no strategy. ## Using Connections Using connections in Operations and Sources is quite different, see: - [Using Connections in Operations](https://docs.mulesoft.com/mule-sdk/latest/operations#_using_connections) - [Using Connections in Sources](https://docs.mulesoft.com/mule-sdk/latest/sources-config-connection) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Mule SDK Development Best Practices > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The purpose of this documentation is to provide users of the Mule SDK with best practices for building modules and connectors in a way that makes the best use of Mule runtime engine (Mule) functionality and is consistent with MuleSoft’s UX and quality guidelines. ## Terminology This documentation uses these special terms: Connector A module that establishes connections to an external system. A connector is a module. Large String A String larger than 4 KB in size. Module A plugin built through the Mule SDK that extends the functionality of Mule 4. Mule language The XML-based domain-specific language (DSL) used to write Mule applications and through which a module is used inside an operation. Must The word "must" denotes a hard rule. This means that to be considered compliant with best practices, a module has to comply with all the practices described with this term. Should This word "should" denotes a rule that, although recommended, could be skipped in certain cases and the module would still be compliant with best practices. Target system The resource that a connector accesses. User This refers not to the user of the Mule SDK (the one coding the module), but to the end user who consumes the module that a developer produces. All best practices are ultimately aimed at improving the end user’s experience. ## MuleSoft Coding Practices Using the SDK to create a new module or connector **should not** be approached as simply wrapping an API, library, or piece of functionality into a component that Mule can understand. Instead, the problem **should** be approached as “extending a programming language called Mule.” If you were to create a new library for the C programming language or your own Java framework, you would design library APIs, syntax, and semantics so that a C or Java developer consuming that library finds it consistent and compliant with all the rules and guidelines inherent to that ecosystem. Failing to do so results in unhappy consumers with unmet expectations for consistency and clarity. The same applies to a custom Mule module or connector, which needs to be approached not as just creating a Mule bridge, but as an extension to the Mule language that provides additional functionality in a consistent, usable, and organic way. ## See Also - [Salesforce Help](https://help.salesforce.com) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk --- # Mule SDKs Overview > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Use the Mule SDK for Java or XML to extend the Mule 4 Runtime by creating new modules that you can install in Mule apps. Examples of modules include connectors, such as HTTP, or modules with custom functionality, such as the Validations Module. The SDK provides an API that: - Facilitates faster development of Mule modules than its predecessor, DevKit. - Decouples modules from the Mule Runtime internals. - Assures consistency across Mule components and other modules, such as cloud connectors. You can use either the advanced, feature-rich Java Mule SDK or the simpler XML SDK, which provides only outbound operations and doesn’t support recursive calls. ## Differences from DevKit The Mule SDK for Java is the Mule 4 evolution of DevKit. It replaces Devkit and provides the canonical way of extending the Mule Runtime. DevKit is not available for Mule 4. Although the SDK builds on the same idea of annotated Java classes, internally it works differently: - The SDK is not a code generator. The built module does not contain any generated code (not even the XSD schema). Your module interacts with Mule Runtime through a clearly defined _Extensions API_. However, you do not need to learn the details of this API because the SDK framework takes care of that for you. - Compared to DevKit, the SDK is more powerful and supports more features: - Transactions - Request-response message sources - Dynamic configurations - Routers - Non-blocking operations - Classloading isolation - Enhanced interoperability with runtime services ## See Also - [Mule SDK for Java](https://docs.mulesoft.com/mule-sdk/latest/getting-started) - [XML SDK](https://docs.mulesoft.com/mule-sdk/latest/xml-sdk) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, dependency, inject, registry, service --- # Mule Services Injection > For the full documentation index, see: https://docs.mulesoft.com/llms.txt ## Using Mule Services When implementing your module, you might need to use some of the services that Mule provides. Examples of these services include `ObjectStoreManager`, `LockFactory`, and `HttpService`. To achieve this, there is no need to get them from the registry of the `MuleContext`. You can simply inject them into your component (such as an operation, source, or configuration) through the `@Inject` annotation. Here is a simplified example that injects a service from the registry into a component: Example: Using the @Inject Annotation ```java public class ObjectStoreOperations { // ... @Inject private ObjectStoreManager runtimeObjectStoreManager; // ... @Throws(StoreErrorTypeProvider.class) public void store(String key, @Content TypedValue value, @Optional @ParameterDsl(allowInlineDefinition = false) ObjectStore objectStore) { ObjectStore os = objectStore == null ? runtimeObjectStoreManager.getDefaultPartition() : objectStore; os.store(key, value); } // ... } ``` Here are some common Mule services: - `LockFactory`: All Mule components that require synchronization to access shared data must be synchronized using locks from the default implementation of this interface. For more information regarding `LockFactory`, refer to [Distributed Locking](https://docs.mulesoft.com/mule-runtime/4.4/distributed-locking). - `ExpressionManager`: Provides universal access for evaluating expressions embedded in Mule configurations, such as XML, Java, scripting, and annotations. - `TransformationService`: Service that tries to transform a value into another given type. - `HttpService`: Provides both HTTP server and client implementations. Whenever using an HTTP connection on your connector, you should try to use this service. - `SchedulerService`: Provides access to the different schedulers and thread pools that exist in the Mule runtime, allowing you to schedule tasks on those. - `SoapService`: Service that creates SOAP clients to consume SOAP services. - `ObjectStoreManager`: Manager in charge of creation and management of `ObjectStore` instances. --- product: Mule SDK version: 1.13 is-latest-version: true --- # Multi-Level Metadata > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Imagine the case where your end user will use an operation that invokes an action provided by a service. To know the metadata that the action requires as arguments or the metadata of what it returns, you need to differentiate the service from the action to be executed. You do this with a metadata key id. For these cases, the metadata key id does not need to be a single `String`. It can instead contain multiple parts bundled within a complex parameter that holds the information needed to resolve the metadata. ## Implementing a Multi-Level Metadata Key It is necessary to set the order in which parameters of the metadata key are specified, starting with `1`. Returning to the example above, the end user will first specify the service field, then the action to invoke. So, the service parameter needs to set `order = 1` and the action parameter `order = 2`. All the parameters of the POJO representing the `MetadataKeyId` must be annotated with a `@MetadataKeyPart` annotation that sets the `order` value for it. These annotated fields must be of type `String`. Here is an example of a metadata key id class: ```java public class ActionIdentifier { @Parameter @MetadataKeyPart(order = 1) (1) private String service; @Parameter @MetadataKeyPart(order = 2) (1) private String action; public String getService() { return service; } public String getAction() { return action; } public void setService(String service) { this.service = service; } public void setAction(String action) { this.action = action; } } ``` The next example shows an operation that uses a `MetadataKeyId` that is a complex type: ```java @OutputResolver(output = OutputOperationTypeResolver.class) public Object invoke( @ParameterGroup(name = "Operation") @MetadataKeyId(OperationTypeKeysResolver.class) ActionIdentifier identifier, @TypeResolver(InputOperationTypeResolver.class) Map args){ return invokeAction(identifier.getService(), identifier.getAction(), args); } ``` Next is an example of a `TypeKeysResolver` that returns each available `MetadataKey`: ```java public class OperationTypeKeysResolver implements TypeKeysResolver { @Override public Set getKeys(MetadataContext context) throws MetadataResolvingException, ConnectionException { Set keys = new HashSet<>(); for(String service : getServices()){ MetadataKeyBuilder key = MetadataKeyBuilder.newKey(service); for(String action : getActions(service)){ key.withChild(MetadataKeyBuilder.newKey(action).build()); } keys.add(key.build()); } return keys; } } ``` ```java public class OutputOperationTypeResolver implements OutputTypeResolver{ @Override public MetadataType getOutputType(MetadataContext context, ActionIdentifier key) throws MetadataResolvingException, ConnectionException { // Resolve the output metadata using the ActionIdentifier } } ``` ```java public class InputOperationTypeResolver implements InputTypeResolver { @Override public MetadataType getInputMetadata(MetadataContext context, ActionIdentifier key) throws MetadataResolvingException, ConnectionException { // Resolve the input metadata using the ActionIdentifier } } ``` ## Partial Fetching **Available since version 1.1** The process of getting each `MetadataKey` can be very time consuming. In the example that gets all the possible services and the actions of each service, the information you are fetching might be on another server. Partial fetching enables you to resolve one level of the `MetadataKey` tree at a time. The `getKeys` method from `TypeKeysResolver` will only return the first level of each tree, not the complete tree of each `MetadataKey`. So, the amount of information needed is greatly reduced because you only need to get the possible services. Once a service is selected, you need to provide the next level of the `MetadataKey` tree. To make this possible, your key resolver must implement `PartialTypeKeysResolver`, for example: ```java public class OperationTypeKeysResolver implements PartialTypeKeysResolver { @Override public Set getKeys(MetadataContext context) throws MetadataResolvingException, ConnectionException { Set keys = new HashSet<>(); for(String service : getServices()){ (1) MetadataKeyBuilder key = MetadataKeyBuilder.newKey(service); keys.add(key.build()); } return keys; } @Override public MetadataKey resolveChilds(MetadataContext metadataContext, ServiceOperation key) throws MetadataResolvingException, ConnectionException { if(key.getService() == null){ throw new MetadataResolvingException("Missing Service name. Cannot resolve Actions without a service", FailureCode.INVALID_METADATA_KEY); } MetadataKeyBuilder key = MetadataKeyBuilder.newKey(key.getService()); (2) for(String action : getActions(key.getService())){ key.withChild(MetadataKeyBuilder.newKey(action).build()); (3) } return key; } } ```
1Only the services are retrieved. The actions of a service will be retrieved on demand.
2Build a single MetadataKey tree with a new, complete level of metadata, in this case, the actions level.
3Add the actions of that service as children.
## Using User Input As Partial Level **Available since version 1.1** In some cases, you might not be able to provide the end user with a hint about part of your MetadataKey. For example, the universe of options might be too big (a dropdown with all the classes in a classpath makes not sense) or when the starting point of the ID is a free input (for example, a query). Imagine a `MetadataKeyId` that has a part that is a `String` representing a Java class. It can be very time consuming to retrieve all the classes, and it is complicated for the user to have so many options on a dropdown. So, you can signal that a `MetadataKeyPart` will not be provided by the resolver and must be inserted by the user. You do this by setting the `providedByKeyResolver` to `false` value on the `MetadataKeyPart` annotation. Here is an example where the POJO representing the `MetadataKeyId` represents a Java method: ```java public class MethodIdentifier{ @Parameter @Alias("class") @MetadataKeyPart(order = 1, providedByKeyResolver = false) (1) private String clazz; @Parameter @Alias("method") @MetadataKeyPart(order = 2) private String methodId; @Override public String getClazz() { return clazz; } @Override public String getMethodId() { return methodId; } @Override public void setClazz(String clazz) { this.clazz = clazz; } @Override public void setMethodId(String methodId) { this.methodId = methodId; } } ```
1The clazz field must be inserted by the end user without hints.
In this case, it also means that the `getKeys` method cannot return all the possible classes: ```java public class MethodTypeKeysResolver implements PartialTypeKeysResolver { @Override public Set getKeys(MetadataContext context) throws MetadataResolvingException, ConnectionException { return emptySet(); (1) } @Override public MetadataKey resolveChilds(MetadataContext metadataContext, MethodIdentifier key) throws MetadataResolvingException, ConnectionException { if(key.getClazz() == null){ throw new MetadataResolvingException("Missing Class name. Cannot resolve Methods without a target Class", FailureCode.INVALID_METADATA_KEY); } MetadataKeyBuilder key = MetadataKeyBuilder.newKey(key.getClazz()); (2) for(String methodId : getMethodIds(key.getClazz())){ key.withChild(MetadataKeyBuilder.newKey(methodId).build()); (3) } return key; } } ```
1Return an empty set of MetadataKey because the end user will provide this information.
2Build a single MetadataKey tree with a new, complete level of metadata, in this case, the methodIds level.
3Add the methodIds of that class as children.
--- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, operation, non, blocking, non-blocking --- # Non-Blocking Operations > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The Mule 4 execution engine is based on reactive streams. That means there’s top level support for non-blocking operations. By default, all operations are blocking. As you probably noticed, the semantics of all the operation examples you have seen [before](https://docs.mulesoft.com/mule-sdk/latest/operations) are inherently blocking: the runtime invokes a method and gets a value in response. That is fine for operations that are more CPU intensive than they are IO intensive, or for operations that consume APIs that cannot be consumed in a non-blocking way, such as JDBC (the API that powers the Database connector). HTTP is one of the protocols that does supports this. Being able to perform non-blocking HTTP request is key for scalability of gateway proxies. Any connector consuming a REST API can also benefit from this. This simplified HTTP request example shows how the SDK allows you to develop non-blocking operations: ```java public void request(String url, @Connection HttpClient client, @Content InputStream body, (1) CompletionCallback callback ) { (2) client.sendAsync(HttpRequest.builder() .uri(url) .method("POST") .entity(new InputStreamHttpEntity(body)) .build()).whenComplete((response, e) -> { if (e != null) { callback.error(e); (4) } else { callback.success(Result.builder() (3) .output(response.getEntity().getContent()) .attributes(toAttributes(response)) .build()); } }); } ```
1Unlike blocking operations, the return type is not specified through the method’s return type. Non-blocking operations always need to be specified through void methods.
2An operation becomes non-blocking when it has an argument of type CompletionCallback. This interface uses generics to specify the operation’s return type. The first generic is the type of the output payload, and the second one the type of the output attributes. These generics are mandatory.
3The return value is passed through the success(Result) method of the CompletionCallback. See Result for more information on the Result object.
4Non-blocking operations should not throw exceptions. Any errors should be channeled through the error() method in the callback.
> [!TIP] Notice that in this example, the `CompletionCallback` is being consumed from between an > `HttpResponseCallback`. This second callback is provided by an `HttpClient` that supports > asynchronous responses. As stated previously, in order to do non-blocking, you need to be > consuming an API which allows that. The result of this code is a non-blocking operation that returns an `InputStream` as payload and an `HttpAttributes` object as message attributes. ## Void Operations It is possible to have a void non-blocking operation. The use case for that is an IO operation that does not return anything, such as writing to a file: ```java public void write(String path, @Content byte[] bytes, CompletionCallback callback) { writeAsync(result -> callback.success()); } ``` As you can see here, you can use Java’s `Void` type to indicate that the operation is void. ### Gotcha You can also have an operation that sets the payload (but **NOT** the attributes) like this: ```java public void foo(CompletionCallback callback) { ... } ``` ## Execution Types Mule 4 now has a reactive execution engine. The runtime has a few global executors through which all tasks are run. To schedule tasks correctly, the runtime needs to know which kind of processing each operation is going to perform. The different processing types are: - CPU\_INTENSIVE: Intensive processing, such as a complex, time-consuming calculations or transformations. Note that the SDK will never infer a `CPU_INTENSIVE` type. See [Specifying Execution Types](#specify_execution_type). - CPU\_LITE: Processing that neither blocks nor is CPU intensive, such as message passing, filtering, routing, or non-blocking IO. - BLOCKING: Blocking processing that performs blocking IO operations, `Thread.sleep(long)`, `Lock.lock()`, or any other technique that blocks the current thread during processing. Although this type includes `Thread.sleep`, using it is a bad practice that you should avoid. ### Inferring Execution Types Automatically The SDK follows these rules to make a best guess about the execution type so that you don’t have to specify it explicitly: - Operation requires connection and is blocking: `BLOCKING` - Operation requires connection and is non-blocking: `CPU_LITE` - None of the above: `CPU_LITE` If the execution type to which your operation corresponds does not match the best guess, you should specify the correct one. Failing to do so will negatively impact the performance of any app that uses your module. ### Specifying Execution Types You can manually specify an operation’s execution type through the `@Execution` annotation, for example: ```java @Execution(CPU_INTENSIVE) public void computeFlightPlan() { ... } ``` --- product: Mule SDK version: 1.13 is-latest-version: true keywords: notification, sdk, operations, sources --- # Notifications > For the full documentation index, see: https://docs.mulesoft.com/llms.txt **Available since version 1.1** Notifications in Mule are a way of communicating actions to other components. They are mostly used for monitoring. ## Defining Notifications The first step is to define all the notifications that can be fired by a Module. You do this by defining an `Enum` that implements the `NotificationActionDefinition` interface. Each value defined in the `Enum` is considered to be notification action. Since you can include data within a notification, you must provide each associated `DataType`. In the example below, `NEW_EVENT` and `FINISHED_EVENT` actions are declared. They have an `Integer` and a custom object (`SimpleObject`) as data. ```java public enum SimpleNotificationAction implements NotificationActionDefinition { NEW_EVENT(DataType.fromType(Integer.class)), FINISHED_EVENT(DataType.fromType(SimpleObject.class)); private final DataType dataType; SimpleNotificationAction(DataType dataType) { this.dataType = dataType; } @Override public DataType getDataType() { return dataType; } } ``` ### Registering Notifications in the Module To identify the notifications that a Module fires, the `@Extension` annotated class should be annotated with `@NotificationActions`, which references a `NotificationActionDefinition` enum that contains the defined notifications. Any custom data types you define there will need to be exported, as well. ```java @Extension(name = "Foo") @NotificationActions(SimpleNotificationAction.class) @Export(classes = {SimpleObject.class}) @Xml(prefix = "test-connector") public class FooExtension { // Content } ``` ## Communicating Which Notifications are Fired After declaring all the possible notifications in a Module, you need to provide the information that binds the notifications with the Operations and Sources that fire them. To do that, you need to implement a `NotificationActionProvider` class that identifies the notifications that are fired by an Operation or Source. ```java public class ExecuteNotificationActionProvider implements NotificationActionProvider { @Override public Set getNotificationActions() { HashSet errors = new HashSet<>(); errors.add(SimpleNotificationAction.NEW_EVENT); errors.add(SimpleNotificationAction.FINISHED_EVENT); return errors; } } ``` After defining the `NotificationActionProvider`, you can bind it to the proper Operation or Source using the `@Fires` annotation: ```java @Fires(ExecuteNotificationActionProvider.class) public void execute(){ // operation body } ``` ## Firing Notifications > [!WARNING] Firing a notification action that is not declared in the `NotificationActionProvider` > of an Operation or Source will result in a failure. Firing a notification using data that does not > match the declared type will also result in a failure. ### Operations To fire a notification from an Operation, you must add a `NotificationEmitter` parameter. You can then use it to fire notifications with a given `NotificationActionDefinition` and the associated data, as a `TypedValue`. ```java @Fires(ExecuteNotificationActionProvider.class) public void execute(NotificationEmitter notificationEmitter){ notificationEmitter.fire(NEW_EVENT, TypedValue.of(4)); // operation body notificationEmitter.fire(FINISHED_EVENT, TypedValue.of(new SimpleObject())); } ``` ### Sources Unlike Operations, Sources not only execute when receiving or generating a new message to be processed but also when one completes, whether successfully or with a failure. You can fire notifications from `@OnSuccess`, `@OnError`, and `@OnTerminate` methods by adding a `NotificationEmitter` parameter, just like you can in Operations. ```java @OnSuccess public void onSuccess(@ParameterGroup(name = "Success Info", showInDsl = true) PersonalInfo successInfo, NotificationEmitter notificationEmitter) { // on success body notificationEmitter.fire(FINISHED_EVENT, TypedValue.of(successInfo.getSimpleObject())); } ``` When receiving or generating a message, however, you can only indicate that you want certain notifications to be fired once the message is handled by using the `SourceCallbackContext`. ```java @Override public void onStart(SourceCallback sourceCallback) throws MuleException { // source body context.fireOnHandle(NEW_EVENT, TypedValue.of(7)); sourceCallback.handle(result, context); } ``` --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, annotations, null, safe --- # Null Safe > For the full documentation index, see: https://docs.mulesoft.com/llms.txt In some cases, you might want to instantiate a `@Parameter` even though the end user has not provided a value, for example: ```java @Parameter @Optional private List numbers; ``` While interacting with `numbers` in your code, you will need to check if `numbers == null` at some point to avoid a `NullPointerException`. The SDK supports the use of annotations like `@NullSafe`, which can provide an empty list instead of `null`. When you use it, the SDK can automatically create a value for you if the value was not configured. There are many "flavors" of `@NullSafe` annotations. When used with `List` and `Map` interfaces, `@NullSafe` will provide a default implementation for those collections. This example shows a POJO as a null safe type: ```java @Parameter @Optional @NullSafe private Foo foo; ``` As you might expect, the SDK will instantiate `Foo` and assign it to your `foo` parameter so that you do not have to manually check for null. Note that if you use `@NullSafe` with a POJO, all the parameters declared inside the POJO must all be _optional_. If a parameter is required, there is no default value for the SDK to use. So, using `@NullSafe` to automatically instantiate required parameters makes no sense. `@NullSafe` can be configured to instantiate a certain class instead of using the parameter’s type. This can be helpful in the cases where the parameter’s type cannot be instantiated (like an abstract class or an interface). You specify the class to be instantiated by setting the `defaultImplementingType` attribute in the annotation, for example: ```java @Parameter @Optional @NullSafe(defaultImplementingType = Foo.class) private AbstractFoo foo; ``` --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, security, oauth --- # OAuth > For the full documentation index, see: https://docs.mulesoft.com/llms.txt OAuth 2.0 is the industry-standard protocol for authorization. OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications, desktop applications, mobile phones, and livingroom devices. For more information about the OAuth 2.0 protocol, see [https://oauth.net/2/](https://oauth.net/2/). The SDK provides support for building modules that are OAuth enabled, which means that they establish connections that are authorized through the OAuth protocol. This enables you to hide most of the complexities of the OAuth protocol from the end user and to spare them the requirement of learning OAuth. OAuth support provided by the SDK is limited to: - OAuth version 2. - [Authorization Code](https://docs.mulesoft.com/mule-sdk/latest/authorization-code) grant type - [Client Credentials](https://docs.mulesoft.com/mule-sdk/latest/client-credentials) grant type (available since version 1.3) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, streaming, objects, pagination --- # Object Streaming (Pagination) > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Object streaming is similar to [binary streaming](https://docs.mulesoft.com/mule-sdk/latest/binary-streaming), except that it streams Java objects, not a raw byte stream. Here is an example that use object streaming through an oversimplified Select operation from the Database connector: ```java public PagingProvider> list(@Text String sql) { (1) return new PagingProvider>() { private ResultSet resultSet; @Override public List> getPage(DbConnection connection) { (2) if (resultSet == null) { resultSet = executeQuery(connection, sql); (3) } return extractPageFrom(resultSet); } @Override public java.util.Optional getTotalResults(DbConnection connection) { return java.util.Optional.empty(); (4) } @Override public void close(DbConnection connection) throws MuleException { resultSet.close(); (5) } } } ```
1Two things to notice about the signature of this method:
  • The method returns a PagingDelegate object. PagingDelegate requires two generic types. The first one is the type of connection the delegate will use to get the next pages, and the second is the type of the elements in the page.

  • The List operation receives the parameters. However, notice that is does not receive a connection object. Paged operations cannot receive connections. Instead, they must always use the connections that the runtime passes to the PagingProvider.

    So, this signature means that it will return the results of the query in pages, using a DbConnection object, and each item in the stream will be a Map<String, Object> that represents each row in the result.

2getPage() fetches the next page and returns it. It receives the connection to use to fetch that page. When the stream is over, this method needs to return an empty List. Implementations should ensure that no empty pages are returned before the stream is finished.
3Depending on the operation’s logic, you might need logic to obtain the first page versus logic to obtain the next pages.
4Another feature of object streaming is the ability to tell how many objects are actually in the streams. To determine that, you need to implement the getTotalResults() method. The problem is that not all APIs support that. For example, the Salesforce API supports that, but JDBC (the API powering the Database connector) does not. That is why this method returns a Java Optional, in this case, an empty one. Users of your paginated operation can obtain this value through an expression similar to #[payload.size].
5Finally, the close() method will be invoked when no more objects are in the stream. This method IS NOT for closing the connection. The SDK will do that automatically. The method is for releasing any resources that this paging delegate might be holding. For example, in this case, the paging delegate has a ResultSet from which each of the page values are extracted. That ResultSet needs to be released.
## Sticky Connections By default, there is no guarantee that the connection that `getPage()` receives will be the same one on each invocation. And that is fine. For most APIs (especially Rest APIs), this is not only _not_ a problem, but in some cases, it is a feature. However, there are other cases (like Database) in which all pages **MUST** be obtained using the **SAME** connection. This was impossible to do with Devkit. Mule SDK adds sticky connections, which signal that the runtime must guarantee that the `getPage()` method is always invoked with the same transaction, for example: ```java public PagingProvider> list(String sql) { return new PagingProvider>() { private ResultSet resultSet; @Override public List> getPage(DbConnection connection) { ... } @Override public java.util.Optional getTotalResults(DbConnection connection) { ... } @Override public void close(DbConnection connection) throws MuleException { ... } @Override public boolean useStickyConnections() { return true; } } } ``` ## Configuring Streaming Just like with binary streaming, auto-paged operations automatically get the streaming configuration through ``, ``, and ``. For details on these elements, see [binary streaming](https://docs.mulesoft.com/mule-sdk/latest/binary-streaming). Here is an example that configures custom file store streaming for the select example above: ```xml ``` --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, sources, listener, triggers, response, output --- # Obtaining Flow Output and Sending Responses from a Message Source > For the full documentation index, see: https://docs.mulesoft.com/llms.txt One of the main properties of a Source indicates whether it generates a response or not. Examples of sources that do not emit a response: - A File listener - Salesforce streaming API - A Scheduler element - A Source that listens for asynchronous push notifications Sources that do emit a response: - A TCP server - An HTTP server - A JMS server As you can see, this classification is highly non-deterministic, for example: - Although an HTTP server supports sending a response, the user might choose not to do so. - Although a JMS listener might respond by sending a message to a reply queue, it might not, depending on the message headers. Even if a Source can send a response, the Source might not send a response even if the message was processed successfully. There are logic or configuration circumstances that might skip the process of sending the response. To specify that your source can generate the response, it must be annotated with `@EmitsResponse`. Notice that this simply tells the runtime and tooling that the source has the capability of doing so. However, note that the neither the SDK nor the runtime will: - Validate that a source that emits a response actually emits a response. - Prevent a source from sending a response even when the source does not declare that it emits responses. ## Callback Parameters Each time the flow finishes processing a message generated by the source, the source might need to obtain the response that the flow has generated. You do this through _callback methods_. Callback methods are used to tap into the responses generated by the flow that owns the source. You can do this by declaring methods annotated with `@OnSuccess` , `@OnError`, and `@OnTerminate`. These methods are declared in a similar fashion as operations, but they need to be void because they listen for the response but cannot alter it. Each of the parameters in these callback methods is evaluated in the same manner as operations (including auto-transformation). Notice, however, that because these callbacks are executed in response to the runtime processing a message, the callbacks can accept expressions (the default). > [!TIP] Even if the source does not emit responses, it might need callback methods for releasing > resources, logging, auditing, and so on. ## Listening for Successful Outputs The source can declare a method with the `@OnSuccess` annotation so that the method is executed when the flow successfully processes the message (either because no errors were thrown or because they were all handled gracefully). This method can have arguments that are treated as source parameters. Note that the user of the connector cannot (and does not need to) tell the difference between a regular parameter and a callback one. For that use, it is all the same, and Studio shows which parameters accept expressions and which ones do not. The user should not need to worry about anything else. Another important thing to keep in mind is that the processing of the message once the source has pushed it to the flow is asynchronous. So the `@OnSuccess` method will also be invoked asynchronously. Here is another oversimplified example from the HTTP connector: ```java @EmitsResponse (1) public class HttpListener extends Source { @Override public void onStart(SourceCallback sourceCallback) throws MuleException { httpServer = serverProvider.connect(); httpServer.listen(path).onRequest(request -> { SourceCallbackContext ctx = sourceCallback.createContext(); (2) ctx.setVariable("responseContext", new HttpResponseContext(request.getResponseSender())); (3) Result result = requestToResult(request); sourceCallback.handle(result, ctx); (4) }); } @OnSuccess public void onSuccess(@Content String responseBody, @Optional String responseStatusCode, SourceCallbackContext callbackContext) (5) throws Exception { if (hasResponse(responseStatusCode)) { (6) HttpResponseContext responseContext = callbackContext.getVariable("responseContext"); (7) responseContext.sendResponse(responseBody, statusCode); (8) } } } ```
1The example annotates the source with @EmitsResponse because this HttpListener might send a response.
2Because the @OnSuccess method is invoked asynchronously (and most likely in parallel), it is necessary to communicate state to the callback method. More precisely, it is necessary to provide some kind of reference regarding the actual request to reply into. To accomplish that, the example creates a new SourceCallbackContext using the SourceCallback.
3The example adds a variable into the context that holds an HttpResponseContext. This is a custom object defined in the connector that knows how to send responses.
4The example generates and pushes the message through the SourceCallback, using both the Result object and the newly create context.
5The callback method has two parameters, a @Content one for the body of the response to be sent and the HTTP status code of the response. There is a third argument of type SourceCallbackContext. This is not a parameter but a way to tell the SDK that to recover the same context that was passed to the handle() method.
6Because the HTTP protocol does not always send a response, the example checks to see if the response status code requires the response to be sent or not.
7If a response is needed, the example recovers the response context that was originally set on the context.
8The response is sent.
## Listening for Error Outputs Similar to `@OnSuccess`, the `@OnError` is executed when an unhandled error is thrown by the flow, for example: ```java @OnError public void onError(@Optional @DisplayName(ERROR_RESPONSE_SETTINGS) @NullSafe HttpListenerErrorResponseBuilder errorResponseBuilder, SourceCallbackContext callbackContext, Error error) { } ``` In this case, the method is very similar to the success example. It also receives parameters and the callback context, and it sends an error response back to the requester. It can also receive an Error object. This is the Mule Error object that contains information about the obtained failure. > [!TIP] The `Error` object is not a Java `java.lang.Error` object but an SDK > `org.mule.runtime.api.message.Error`. ## Content Parameters Callbacks accept content parameters just like any operation. In the case of the HTTP connector, the `HttpListenerResponseBuilder` uses them so that DataWeave can be used to transform the flow’s result into whatever format the connector intends to return. All the concepts of content and primary contents apply, as well as their associated restrictions. ## On Terminate A source does not need to provide `@OnSuccess` or `@OnError` methods. A source can declare the ones it needs or none at all. However, if at least one of them is defined, then you also need to define an `@OnTerminate` method. This is required because the `@OnSuccess` or `@OnError` might fail. If either of them throws an exception, a remote system might hang as it waits for a response, resources might leak, audit logs might be left uncompleted, and so on. Here is an example of an `onTerminate` method: ```java public void onTerminate(SourceResult sourceResult) { Boolean sendingResponse = (Boolean) sourceResult.getSourceCallbackContext().getVariable(RESPONSE_SEND_ATTEMPT).orElse(false); if (FALSE.equals(sendingResponse)) { sourceResult .getInvocationError() .ifPresent(error -> sendErrorResponse(new HttpListenerErrorResponseBuilder(), sourceResult.getSourceCallbackContext(), error, null)); } } ``` The method receives a `SourceResult` object that contains optional errors that might have occurred in either the `onSuccess` or `onError` methods and the associated `SourceCallbackContext`. In the example above, the method sends a generic error response in case an error was found. ## Next Step - [Sending asynchronous responses from a Message Source](https://docs.mulesoft.com/mule-sdk/latest/sources-async-response) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, sources, listener, triggers, config, connection --- # Obtaining the Config and Connection in a Message Source > For the full documentation index, see: https://docs.mulesoft.com/llms.txt ## Obtaining the Config Object Just like operations, a source might reference a configuration to obtain parameters about its behavior. To obtain such a configuration, a field must be annotated with the `@Config` annotation (the same used in operation arguments): ```java public class HttpListener extends Source { @Config private HttpListenerConfig config; } ``` ## Obtaining a Connection If the source requires a connection (and 99% of connectors do unless you are implementing a Scheduler), you need to obtain a connection. As with operations, the `@Connection` annotation is used but with two major differences: - It is used in a field instead of a method argument. - Instead of the connection object, a `ConnectionProvider` is injected. ```java public class HttpListener extends Source { @Config private HttpListenerConfig config; @Connection private ConnectionProvider serverProvider; @Parameter @Optional(defaultValue = "/") private String path; private HttpServer httpServer; @Override public void onStart(SourceCallback sourceCallback) throws MuleException { httpServer = serverProvider.connect(); (1) httpServer.listen(path).onRequest(request -> { (2) processRequest(request, sourceCallback); (3) }); } @Override public void onStop() { if (httpServer != null) { (4) serverProvider.disconnect(httpServer); (5) } } } ```
1The example uses the ConnectionProvider to obtain an HttpServer. The typical connection provider has already configured the server with the proper host and port.
2HTTP requests are pushed into the server by the remote clients, and the HttpServer component notifies the source of every request. The source then uses the SourceCallback to push the message into the flow.
3The source should define the processRequest method to transform the HTTP request into a message that can be pushed to the flow. The documentation will show how to do that later, but to keep the example simple, you can assume that this works.
4The onStop() method releases resources, in this case, the HttpServer. It checks for null in case the onStart() method fails before the server is created.
5The example uses the disconnect() method of the ConnectionProvider to release the server.
> [!TIP] The example above is pseudocode. It oversimplifies the real HTTP connector to keep the > example clear and concise. ## Next Step - [Pushing Messages to a Flow from a Message Source](https://docs.mulesoft.com/mule-sdk/latest/sources-push-message) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, operation, processor, result, execution, void, payload, --- # Operations > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Operations are one of the most important Mule concepts because they represent the actions your module can perform inside a flow. You use them to process incoming messages through business logic that is implemented in the module. ## Declaring Operations for a Config Operations must be declared inside a separate class. See [Structure](https://docs.mulesoft.com/mule-sdk/latest/module-structure) to learn how to correctly organize your module’s code. > [!TIP] A class is either a Config class or an Operations class. You cannot have both things mixed > together in the same class. ```java @Extension(name = "Foo") @Operations(FooOperations.class) public class FooModule { @Parameter private String fooModuleParameter; public String getFooModuleParameter() { return fooModuleParameter; } } ``` All the public methods defined in `FooOperations` are going to be considered as our operations. If you want to have a public method inside your operation class but you don’t want it to be consider as an operation, you just need to annotate that method with `@Ignore`. Like this: ```java public class FooOperations { public String foo(@Config FooModule config) { return config.getFooModuleParameter(); } private String thisIsNotAnOperation() { return "bar"; } @Ignore public String thisIsNotAnOperationEither() { return "foobar"; } } ``` In the example above, the operation class is bound to the only config (which is the same as the extension class). This means that the operations defined in `FooOperations` are only available to be used with a certain type of configuration (`FooModule`, in this case). > [!TIP] Remember that you can use your extension as a config _if and only if_ it is the only > configuration you have. So, an operation named `foo` is defined. Because **it belongs to the configuration** `FooModule`, it can receive the configuration class as an argument annotated with the annotation `@Config`. You cannot annotate the argument annotated as `@Config` with the annotations you use for any [Parameter](https://docs.mulesoft.com/mule-sdk/latest/parameters) because the config is not a parameter. ## Global Operations Besides adding operations that are exclusive to a particular config, you can also add operations at extension level, that is, operations that are global to the module. Operations at extension level cannot not have a config unless the extension class is the only config. This means that they are not bound to a config. To define operations at extension level, you need to annotate the extension class with your `@Operations` while having configurations defined, rather than using the extension class as your only config. Take into account that this only makes sense if you have more than just one config. That’s because if we only have the extension class (our only config), then all the operations we define are bound to it. The next example adds two configs and defines operations for each of them. Then it adds some operations that are global to the module. ```java @Extension(name = "Foo") @Operations(GlobalOperations.class) @Configurations({FooConfig.class, BarConfig.class}) public class FooModule { } ``` ```java public class GlobalOperations { public String fooModuleOperation() { return "this operation is global to the module!"; } } ``` The example above defines a module with two configurations, `FooConfig` and `BarConfig`, and a set of operations defined in `GlobalOperations`. Now, see the configs and their operations: ```java @Operations({FooConfigOperations.class}) public class FooConfig { @Parameter private String fooParameter; public String getFooParameter() { return fooParameter; } } ``` ```java public class FooConfigOperations { public String fooConfigOperation(@Config FooConfig config) { return "this operation receives the FooConfig which has a fooParameter with value: " + config.getFooParameter(); } } ``` The example above defines a new configuration named `FooConfig` with some exclusive operations defined in `FooConfigOperations`. Then it defines an operation named `fooConfigOperation`. Because it belongs to the configuration `FooConfig`, it can receive the configuration class as an argument annotated with the special annotation `@Config`. ```java @Operations({BarConfigOperations.class}) public class BarConfig { @Parameter private String barParameter; public String getBarParameter() { return barParameter; } } ``` ```java public class BarConfigOperations { public String barConfigOperation(@Config BarConfig config){ return "this operation receives the BarConfig which has a barParameter with value: " + config.getBarParameter(); } } ``` Defining an operation at the extension level (as with the `GlobalOperations` methods above) represents that the defined operations do not receive (and therefore do not need) any configuration to operate. For more information about configurations, see [Configs](https://docs.mulesoft.com/mule-sdk/latest/configs). ## Connected Operations In addition to receiving a configuration as an argument, you can receive the connection as a _special_ argument. You do this by defining a [Connection Provider](https://docs.mulesoft.com/mule-sdk/latest/connections) for the configuration to which this operation belongs. The next example adds a connection to a [previous example](#example_1). Suppose that you define a `FooConnectionProvider` elsewhere that provides connections of type `FooConnection`. (To learn how to define a Connection Provider, see [Connection Provider](https://docs.mulesoft.com/mule-sdk/latest/connections).) ```java @Operations({FooConfigOperations.class}) @ConnectionProviders(FooConnectionProvider.class) public class FooConfig { @Parameter private String fooParameter; public String getFooParameter() { return fooParameter; } } ``` ```java public class FooConfigOperations { public String fooConfigOperation(@Config FooConfig config){ return "this operation receives the FooConfig!"; } public String fooConnectedOperation(@Connection FooConnection connection){ return "this operation receives FooConnection!"; } public String fooConnectedOperation(@Config FooConfig config, @Connection FooConnection connection){ return "this operation receives both config and connection!"; } } ``` Now, there are three different operations: - One that receives the config `FooConfig`. - One that receives the connection `FooConnection`. - And one that receives the config `FooConfig` and gets a connection of type `FooConnection` provided by the `FooConnectionProvider` provider class. ## Operation Structure An operation is defined from a Java method, which makes the mapping of the method’s argument and the operation parameters straightforward. Following the same logic, the operation’s output type is taken from the method’s return type. ### Output The next sections explain how the output of an operation affects what happens in the flow when you invoke the operation. #### Payload The common behavior is to define an operation that returns any type that will be set in the operation output message’s payload. ```java public String outputStringPayload(){ return "this string is going directly into the payload!"; } ``` \[TIP\] If an operation specifies **only** a payload, then the message attributes will be set to `null`. #### Void A void operation is an operation that was created from a method that returns `void`. This operation does not modify the message received but does pass that message to the next component in the flow, for example: ```xml ``` Here, the message received in `void-operation` is the same as the one received in `other-operation`. #### Result If you want to output something more than a simple payload, such as information about the payload (attributes of the Mule message), you need to use a `Result` as the your method’s return type. See [Result](https://docs.mulesoft.com/mule-sdk/latest/result-object) for more information. ### Special Arguments So, you can receive the a configuration and a connection in an operation. Are there other special arguments other than regular parameters? Yes. Here are some special things you can receive as an argument in an operation, and Mule will automatically inject the right value there for you: - `AuthenticationHandler`: An argument of type `AuthenticationHandler` that allows you to configure the current context’s authentication. This is used for encryption and inbound authentication. - `StreamingHelper`: An argument of type `StreamingHelper` with utilities for working with streams. See [Streaming](https://docs.mulesoft.com/mule-sdk/latest/streaming) for more information. - `DefaultEncoding`: An argument of type `String` annotated with `@DefaultEncoding` with information about the Runtime’s default encoding. ```java public String foo(@DefaultEncoding String encoding) { return "Mule default encoding is " + encoding; } ``` ## Aliasing an Operation You can change the name of an operation without having to change the name of the method by using `@Alias`. You can also alias the operation’s parameters as explained in [Parameters](https://docs.mulesoft.com/mule-sdk/latest/parameters). ```java @Alias("fooOperation") public void thisWillNotBeTheOperationName(@Alias("fooParameter") String aliasedParameter) { } ``` --- product: Mule SDK version: 1.13 is-latest-version: true --- # Output Metadata > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Output metadata is the type resolution for the result of a component. Each component can provide either static or dynamic metadata for the payload and attributes, isolated from each other. ## Declaring an OutputTypeResolver and AttributesTypeResolver Both `OutputTypeResolver` and `AttributesTypeResolver` implementations handle the requests for resolving a `MetadataType` based on the information provided by the `MetadataContext` and most importantly, using the `MetadataKey` to identify the `MetadataType` required by the end user (who is creating a Mule app). Refer to [What is the Metadata of a Component](https://docs.mulesoft.com/mule-sdk/latest/metadata#component-metadata) for more information about the `MetadataType`. The `OutputTypeResolver` and `AttributesTypeResolver` interfaces are parameterized with the generic `T`, which must match the type of the `MetadataKeyId` parameter. The examples here use type `String` for the generic because it is the most common `MetadataKeyId` type. (Other documentation will revisit this generic and go in detail about when the type should change to something other than `String`.) ```java public class OutputEntityResolver implements OutputTypeResolver, AttributesTypeResolver { @Override public String getCategoryName() { return "Records"; } @Override public String getResolverName() { return "OutputEntityResolver"; } @Override public MetadataType getOutputType(MetadataContext context, String key) throws MetadataResolvingException, ConnectionException { switch (key) { case "Author_id": return context.getTypeLoader().load(AuthorResult.class); case "BookList_id": return context.getTypeLoader().load(BookListResult.class); case "Book_id": return context.getTypeLoader().load(BookResult.class); default: throw new MetadataResolvingException("Unknown key:" + key, INVALID_METADATA_KEY); } } @Override public MetadataType getAttributesType(MetadataContext context, String key) throws MetadataResolvingException, ConnectionException { if ("Book_id".equals(key)){ return context.getTypeLoader().load(BookAttributes.class); } // Only Books have Attributes information return context.getTypeBuilder().nullType().build(); } } ``` The example above uses the `MetadataContext` only to obtain the `typeLoader` for describing a MetadataType based on a Java class, but it could also use the provided Configuration and Connection elements. ## Using the OutputTypeResolver After you have a `TypeKeysResolver` and an `OutputTypeResolver`, you can add output DataSense support to our Operations and Sources. The main restriction to using an `OutputTypeResolver` with a given `TypeKeysResolver` is that both must belong to the same `category`, which guarantees that the `MetadataKey` provided by the `TypeKeysResolver` can be resolved to a `MetadataType` by the `OutputTypeResolver`: ### Adding DataSense for the Result Payload ```java public class FetchOperations { @OutputResolver(output = OutputEntityResolver.class) public Map get(@Connection MyConnection connection, @MetadataKeyId(EntityKeysResolver.class) String entityKind){ return connection.getClient().fetch(entityKind); } } ``` Above, the MetadataType for the output is resolved dynamically based on the `entityKind` that is being fetched, so if the `entityKind` is configured with the `Author_id` key provided by the `EntityKeysResolver`, then the `OutputEntityResolver` will be invoked with that `MetadataKey`, and the output for the `get` operation will be resolved to an `AuthorResult` ObjectType. A source can use the same output type resolution for describing the MetadataType of the object being dispatched to the flow, and its declaration is similar: ```java @MetadataScope(keysResolver = EntityKeysResolver.class, outputResolver = OutputEntityResolver.class) public class ListenerSource extends Source, Void> { @MetadataKeyId @Parameter public String type; @Connection private ConnectionProvider connection; @Override public void onStart(SourceCallback, Void> sourceCallback) throws MuleException { //... } @Override public void onStop() { //... } } ``` Both sources and operations have the same lifecycle for resolving the output type. The first step is to configure the `MetadataKeyId`, and then the `OutputTypeResolver` is invoked with that key in order to resolve the resulting entity MetadataType. ### Adding DataSense for the Result Attributes Dynamic metadata resolution takes into account that a component’s full output is a `Result`, not just the payload. When an operation or source output has a dynamic attributes structure, you can resolve its MetadataType by declaring an `AttributesTypeResolver` (already implemented in the `OutputTypeResolver.java` example) and then adding a reference to it in the operation or source declaration. In the next example, the `Book` entity can be divided into the content and attributes of the Book, each with its own structures. This division is done so the end user can better understand and use the result of the operation. They can think about the data (which is the content of the Book that goes in the payload) and the metadata that characterizes the payload (the attributes of the Book): ```java public class FetchOperationsWithAttributes { @OutputResolver(output = OutputEntityResolver.class, attributes = OutputEntityResolver.class) public Result get(@Connection MyConnection connection, @MetadataKeyId(EntityKeysResolver.class) String entityKind){ if ("Book_id".equals(entityKind)){ Book book = (Book)connection.getClient().fetch(entityKind); return Result.builder() .output(book.content()) .attributes(book.attributes()) .build(); } return Result.builder() .output(connection.getClient().fetch(entityKind)) .build(); } } ``` Sources have a declaration similar to the one used for the payload, but it adds an `attributesResolver` reference: ```java @MetadataScope(keysResolver = EntityKeysResolver.class, outputResolver = OutputEntityResolver.class, attributesResolver = OutputEntityResolver.class) public class ListenerSource extends Source, Object> { @MetadataKeyId @Parameter public String type; //... } ``` ## Output Metadata with a User-Defined MetadataKey The case for user-defined MetadataKeys also applies for the output of a component. In the case of a query, you do not have a predefined set of possible MetadataKeys. Instead, you have a parameter with a value that characterizes the output type or structure. For example, the Database connector has the `select` operation. Its output depends on what entities are queried: ```java @OutputResolver(output = SelectMetadataResolver.class) public List> select(@MetadataKeyId String sql, @Config DbConnector connector){ // ... } ``` With the `SelectMetadataResolver` declared as: ```java public class SelectMetadataResolver extends BaseDbMetadataResolver implements OutputTypeResolver { @Override public String getCategoryName() { return "DbCategory"; } @Override public String getResolverName() { return "SelectResolver"; } @Override public MetadataType getOutputType(MetadataContext context, String query) throws MetadataResolvingException, ConnectionException { if (isEmpty(query)) { throw new MetadataResolvingException("No Metadata available for an empty query", FailureCode.INVALID_METADATA_KEY); } ResultSetMetaData statementMetaData = getStatementMetadata(context, parseQuery(query)); if (statementMetaData == null) { throw new MetadataResolvingException(format("Driver did not return metadata for the provided SQL: [%s]", query), FailureCode.INVALID_METADATA_KEY); } ObjectTypeBuilder record = context.getTypeBuilder().objectType(); Map recordModels = resolveRecordModels(statementMetaData); recordModels.entrySet() .forEach(e -> record.addField().key(e.getKey()).value(e.getValue())); return record.build(); } } ``` ## List Metadata Automatic Wrapping In the `select` example, you can see that the operation returns a `List`. This makes sense because the result of a select query is multiple record entries, but the `SelectMetadataResolver` does not describe an ArrayType in the `getOutputType` method. Instead the returned MetadataType represents a single `record` structure. Why is that? As you might know already, the operation is returning an ArrayType (List, PagingProvider, and so on), so you only need to describe the `generic` type of the array. The output and attributes TypeResolvers always resolve the MetadataType of _the elements of the collection_ and not the _collection_ type itself. This allows for greater reuse of the MetadataType resolvers and reduces the amount of code needed. Take into account that the resolved attributes will _also_ be the attributes of the _elements_ of the collection, _not_ attributes of the operation’s `List` output. ## Resolving Dynamic Output Metadata without MetadataKey As with input, the output of an operation can be resolved without a specific `MetadataKey`, being the dynamic type affected by the Configuration or Connection of the Component. Again, to declare a keyless resolver, you simply skip the `MetadataKeyId` parameter and ignore the MetadataKey in the TypeResolvers: ```java public class UserTypeResolver implements OutputTypeResolver, AttributesTypeResolver { @Override public String getCategoryName() { return "User"; } @Override public MetadataType getOutputType(MetadataContext context, Object key) throws MetadataResolvingException, ConnectionException { // The `key` parameter will be `null` if the fetch is performed // as a `KeyLess` Metadata resolution. We'll just ignore it. String schema = getUserSchema(context); return new JsonTypeLoader(schema).load("http://demo.user") .orElseThrow(() -> new MetadataResolvingException("No Metadata is available for the User", FailureCode.NO_DYNAMIC_TYPE_AVAILABLE)); } @Override public MetadataType getAttributesType(MetadataContext context, Object key) throws MetadataResolvingException, ConnectionException { // The `key` parameter will be `null` if the fetch is performed // as a `KeyLess` Metadata resolution. We'll just ignore it. String schema = getUserSchema(context); return new JsonTypeLoader(schema).load("http://demo.attributes") .orElseThrow(() -> new MetadataResolvingException("No Metadata is available for the User Attributes", FailureCode.NO_DYNAMIC_TYPE_AVAILABLE)); } private String getUserSchema(MetadataContext context) throws MetadataResolvingException, ConnectionException { return context.getConnection() .orElseThrow(() -> new MetadataResolvingException("A connection is required to resolve Metadata but none was provided", FailureCode.INVALID_CONFIGURATION)) .describeUser(); } } ``` ```java public class UserOperations { @OutputResolver(output = UserTypeResolver.class, attributes=UserTypeResolver.class) public Result, Object> getUser(@Connection DemoConnection connection){ User user = connection.getUser(); return Result., Object>.builder() .output(user.personalInfo()) .attributes(user.accountInfo()) .build(). } } ``` --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, parameter --- # Parameters > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Parameters are configurable arguments that belong to a given component. [Configurations](https://docs.mulesoft.com/mule-sdk/latest/configs), [Components](https://docs.mulesoft.com/mule-sdk/latest/module-structure#components), and [Connection Providers](https://docs.mulesoft.com/mule-sdk/latest/connections) are called _parameterizables_ because they can be configured through their parameters. Parameters provide the information that the component needs so that it can be configured to work properly. Each parameter contains the following information: - A type that restricts the kind of value the parameter can have. - A name that uniquely identifies it. Here is a simple example: ```java @Parameter private String foo; ``` A parameter can be defined as a field of a class or as a method’s argument, depending on the kind of component you are dealing with. For example, an [operation](https://docs.mulesoft.com/mule-sdk/latest/operations) parameter is defined like any argument of the method that declares the operation. It does not need to be annotated with `@Parameter`. Take a look at `foo` below. ```java public void method(String foo){} ``` ## Using Complex Types This example uses more complex parameters instead of simple types like strings and integers. ```java @Parameter @Optional private Options options; ``` Where `Options` is a `POJO Object` defined as: ```java public class Options { @Parameter public String color; @Parameter @Optional public String mode; public String getColor(){ return color; } public void setColor(String color){ this.color = color; } public String getMode(){ return mode; } public void setMode(String mode){ this.mode = mode; } } ``` A few notes about this example: - The entire parameter `Options` is used as an optional parameter, so if you decide to use it, you need to ensure that all the required parameters inside `Options` have been set. - If `Options` is not configured, its value will be `null` (see [NullSafe](https://docs.mulesoft.com/mule-sdk/latest/null-safe) for altering this behavior). - `Options` parameters must be Java bean properties (that is, they need to have setters and getters that match the fields names). ### Complex Type DSL Complex types should be separated from regular parameters. It does not make sense to merge all the complex type attributes with the element-specific parameters. That is why a child definition is created for Complex Types. This example shows the complex `Options` type in a [Configuration](https://docs.mulesoft.com/mule-sdk/latest/configs): ```xml ``` Note that `module:options` is separated from the parameters `name` and `someConfigAttribute`. ## Modifiers Parameters are highly customizable. This section identifies a few modifiers you can use when defining a parameter to specify behavior or restrictions. ### Expression Support An `ExpressionSupport` describes the parameter’s behavior when the configured value is a DataWeave expression. There are three possible values: - `REQUIRED`: The parameter only accepts expressions. - `SUPPORTED` (default): The parameter can handle both expressions and simple values. - `NOT_SUPPORTED`: The parameter requires static values. Expressions are not supported. These values can be configured simply by adding the `@Expression` annotation. ```java @Parameter @Expression(SUPPORTED) private String foo; ``` The example above defines a parameter named `foo` of type `String` that can have either a static value (such as `"bar"`) or an expression (such as `"\#[vars.foo]"`). ### Optionality Parameters can be required or optional. Sometimes a parameter specifies information that is so crucial to what you are trying to do (for example, the host name for an HTTP Request) that there is no way to continue without setting it. Other times, you can operate without that information or simply use a default value (for example, where a port is 80 in an HTTP listener). A parameter is required by default unless you annotate it with `@Optional`, for example: ```java @Parameter @Optional(defaultValue="80") private Integer port; ``` Optional parameters can have a default value. Note that `@Optional` only accepts `String` values, but the SDK will auto-convert it to your parameter’s type. Additionally, if the parameter supports expressions, you can use one as the default value. ### Using @Alias The name of each parameter will be the name given to the field in which it is defined. You can override this name by using the `@Alias` annotation. ```java @Parameter @Optional @Alias("class") private String clazz; ``` Notice that the use of `@Alias` makes it possible to use a Java reserved term (such as `class`) to name the parameter. You cannot use a reserved term to name the field. ### Customizing the Parameter DSL It is possible to customize how the Parameter looks and behaves in the DSL using the `@ParameterDsl` annotation. This annotation allows you to set directives regarding syntax and semantics of the generated XML DSL, and it can be applied to any Parameter in the model. The `@ParameterDsl` annotation has two configurable elements: - `allowInlineDefinition`: Indicates whether the associated Parameter should support inline definition as child element or not. This is used for customizing the DSL of Complex Type Parameters, especially when you need to avoid the inline definition because it has illegal fields (like a `name` field) or because the structure is so complex that makes no sense to represent it inline. For example, if you use the `Options` type in a [Configuration](https://docs.mulesoft.com/mule-sdk/latest/configs) and declare the Parameter with `@ParameterDsl(allowInlineDefinition=false)`, you no longer have the child element: ```xml ``` - `allowReferences`: Indicates whether the associated Parameter should support registry references when receiving an `String`. This is useful for controlling how the Parameter will behave upon configuration if the type of the Parameter doesn’t provide enough information to automatically configure the references support. For example, in the JMS Connector, if you want to avoid making users inject an object from the registry as a `ConsumerType` configuration, you declare it as not supporting references: ```java @Parameter @Optional @Expression(NOT_SUPPORTED) @ParameterDsl(allowReferences = false) private ConsumerType consumerType; ``` ## Parameter Groups The `@ParameterGroup` annotation allows you to define a group of parameters that share some kind of special relationship where it makes sense for them to belong to the same group. How do you spot them? Parameters that should go inside a `@ParameterGroup` are the ones that **travel together** all the times. Using `@ParameterGroup` also makes a UI contribution rendering all the `@Parameters` inside the group together in a box, separating them from other `@Parameters` that don’t belong to that group, for example: ![Example of a parameter group](/mulesoft/current/en-us/mule-sdk/latest/_images/group.png) As you can see in the image above, the `Connection` group contains 4 parameters, and there are others like `Soap Version` that are not in that group. That is a nice way to separate the concerns of the `Parameters`. An simple example could be a parameter group defined as follows: ```java public class ConnectionProperties { @Parameter public String host; @Parameter @Optional(defaultValue="80") public Integer port; public String getHost(){ return host; } public void setHost(String host){ this.host = host; } public Integer getPort(){ return port; } public void setPort(Integer port){ this.port = port; } } ``` The example above could be used like this: ```java @ParameterGroup("Connection") private ConnectionProperties properties; ``` `"Connection"` is the name of the `@ParameterGroup` and is the name that will be used in the UI. The class defining the `properties` `@ParameterGroup` will not have a parameter named `properties`. Instead, it will contain the parameters that are defined inside `ConnectionProperties`: - A required parameter named `host`. - An optional parameter named `port`. In other words, the class defining the parameter group is augmented with these extra parameters and will contain the parameters defined inside of `ConnectionProperties` plus all the other parameters it has declared. Note that each field annotated with `@ParameterGroup` must be a Java bean property (that is, it needs to have setters and getters matching the field name). ## POJO vs Parameter Group `@ParameterGroup` can be configured so it can be written as a child element in the DSL instead of being spread around the component that declared it. In other words, assume an operation like this: ```java public void execute(@ParameterGroup ConnectionProperties properties) { ... } ``` This is the DSL for the operation above: ```xml ``` However, if you configure the parameter group using `@ParameterGroup(showInDsl=true)` instead, the DSL now looks like: ```xml ``` When a POJO `@Parameter` is defined, you get the same DSL, so you might ask when to use a POJO instead of using a POJO annotated with `@ParameterGroup`? There are some subtle differences between these two concepts. You should definitely use a POJO if you care about the type as a whole and the structure it provides. For example, `ConnectionProperties` (above) keeps two parameters together because they are always configured together. In this case, the parameters do not represent an entity in the module’s domain. The `@ParameterGroup` is simply a handy way of keeping things that are related together and showing them together in the UI. However, the `Options` POJO defined above concerns the object itself, not just the parameters it contains. For instance, it would make sense to have a list of `Options` because each object is something on its own (in the OOP sense). On the other hand, if you are using a class simply for the sake of grouping things together, without any particular meaning for your module’s domain, then it makes sense to have a parameter group. ## Parameter Type Restrictions You cannot add parameters or POJOs with types `org.mule.runtime.core.api.source.scheduler.Scheduler` or `org.mule.runtime.api.scheduler.SchedulingStrategy`. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, security, oauth --- # Performing the OAuth Dance with an OAuth-Enabled Connector or Module > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The final step for using the module is to trigger the OAuth dance. [Configuring an OAuth-Enabled Connector or Module](https://docs.mulesoft.com/mule-sdk/latest/oauth-configuring) includes a parameter called `authorizePath` through which the end user configures the path of an automatically created HTTP endpoint that is needed to hit a browser and start the OAuth dance. Tip: Remember that the Authorization Code grant type that the SDK supports requires human interaction through a web browser. ## Multi-Tenancy Multi-Tenancy for OAuth means being able to perform the OAuth dance multiple times and to associate each obtained token with a different resource owner ID. The end user needs to specify the `resourceOwnerId` to assign to the token when the dance is initiated. For example, if `authorizePath` is set to `/authorize`, and you want to perform a dance for the user `sdk_demo`, you should hit the following HTTP path: `/authorize?resourceOwnerId=sdk_demo`. After the dance is complete, you execute an operation using the access token obtained for that user. You do that by using an expression on the `resourceOwnerId` parameter of your module’s configuration. Here is a complete example: ```xml (1) (2) ```
1The resourceOwnerId parameter is set to an expression. Each time an OAuth-protected operation is executed, that expression will be evaluated, and the token associated with that value will be used.
2The example sets the authorization endpoint authorizePath.
### Invalidating Access Tokens Multi-tenancy implies the ability to invalidate the access token of a particular `resourceOwnerId`. Invalidating the token causes the associated token information to be deleted. To invalidate an access token, the SDK automatically adds an operation called `unauthorize` to every OAuth-enabled connector or module. Returning to the example above, you might invalidate the obtained token with any of the following statements: ```xml ``` ## Next Step [Handling Expired Access Tokens](https://docs.mulesoft.com/mule-sdk/latest/oauth-token-expiration) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Polling Sources > For the full documentation index, see: https://docs.mulesoft.com/llms.txt **Available since version 1.1** Instead of getting triggered by an external action, Polling Sources are sources that poll items periodically depending on a given `scheduling strategy` and automatically handle idempotency and watermarking. On every poll action, many items can be dispatched to the flow, each as a single `Message`. ## Creating a Polling Source To create a Polling Source, you need to add the `Source` class to the `@Sources` annotation on the extension class, just like regular Sources. For a Source to behave as a `PollingSource`, instead of extending the class `Source`, the class needs to extend from `PollingSource`. When extending from `PollingSource`, you need to implement the methods `poll` and `onRejectedItem`. - `poll` is responsible for obtaining the items to be dispatched and communicating with them through the `pollContext` using the `accept` method. - `onRejectedItem` is called when one of the dispatch items was rejected (for example, by watermarking idempotency or server overload). This method is called to release any resources associated with the result set for the `PollItem`. Here is an example of the declaration of these methods on the directory listener implementation for the FTP connector: ```java public class FtpDirectoryListener extends PollingSource { // ... @Override public void poll(PollContext pollContext) { // ... } @Override public void onRejectedItem(Result result, SourceCallbackContext callbackContext) { // ... } // ... } ``` ## Implementing the Poll Method This method gathers all the items to be dispatched and processes them one by one. The `accept` method from the `PollContext` is in charge of dispatching the polled items into the flow. It receives `Consumer` as a parameter. This example shows how to set the Message to dispatch to the flow: ```java public class FtpDirectoryListener extends PollingSource { // ... @Override public void poll(PollContext pollContext) { if(pollContext.isSourceStopping()){ (1) return; } List attributesList = listFilesAttributes(); for (FtpFileAttributes attributes : attributesList) { if(pollContext.isSourceStopping()){ (1) break; } pollContext.accept(item -> { (2) Result result = read(attributes.getPath()); item.setResult(result); (3) }); } } // ... } ```
1Note that the pollContext provides information on whether the Source is stopping. This must be taken into account before any time-consuming task.
2accept dispatches an item to the flow.
3setResult is used to set the Message that is dispatched.
### Idempotency Idempotency prevents items from getting polled twice. The SDK ensures that no other thread or cluster node is processing an item of the same ID, which provides processing idempotency. No two items with the same ID will be processed at the same time. If a node polls an item with the ID of an item that is being processed, the item will be dropped, and the `onRejectedItem` method will be called. ```java public class FtpDirectoryListener extends PollingSource { // ... @Override public void poll(PollContext pollContext) { if(pollContext.isSourceStopping()){ return; } List attributesList = listFilesAttributes(); for (FtpFileAttributes attributes : attributesList) { if(pollContext.isSourceStopping()){ break; } pollContext.accept(item -> { Result result = read(attributes.getPath()); item.setResult(result); item.setId(attributes.getPath()); (1) }); } } // ... } ```
1setId is used to set the ID used for idempotency.
### Watermarking You can use watermarking when you are polling an incremental source of data and you only want the new data instead of manually dividing the new data from the old data on each call. By setting an incrementing value to the item’s watermark, every other poll will drop and call `onRejectedItem` on any of the item’s watermark values that are lower than the current watermark value. In the example above, the chosen watermark is the timestamp of the file. What criteria does the SDK use to compare watermarks? If the watermarks that you use implement `Comparable`, the natural order of those values will be used. There is no need to set a criterion. ```java public class FtpDirectoryListener extends PollingSource { // ... @Parameter @Optional(defaultValue = "false") private boolean watermarkEnabled = false; (1) // ... @Override public void poll(PollContext pollContext) { if(pollContext.isSourceStopping()){ return; } List attributesList = listFilesAttributes(); for (FtpFileAttributes attributes : attributesList) { if(pollContext.isSourceStopping()){ break; } pollContext.accept(item -> { Result result = read(attributes.getPath()); item.setResult(result); item.setId(attributes.getPath()); if (watermarkEnabled) { (1) item.setWatermark(attributes.getTimestamp()); (2) } }); } } // ... } ```
1A typical best practice is to let the user decide whether to use watermarking or not.
2setWatermark sets the file timestamp as watermark.
What if the watermark values do not implement `Comparable`, or what if I want to use another criteria that is not the natural order? You can simply set a `Comparator` to the `PollContext` by calling its method `setWatermarkComparator`. > [!NOTE] If the watermark updates are not set before polling is complete, you can add a latch to > avoid updating the watermark in two iterations. ### PollItemStatus If you call the `accept` method, you get a `PollItemStatus` in return. This is an `Enum` that can take the following values: - ACCEPTED: The item was accepted and has been scheduled for execution. - FILTERED\_BY\_WATERMARK: The item was rejected because watermarking was enabled, so it was filtered. - ALREADY\_IN\_PROCESS: The item was rejected because idempotency was enabled and another thread or node is already processing this item. - SOURCE\_STOPPING: The item was rejected because the source has received the stop signal. You could use this to log the status of each item polled: ```java public class FtpDirectoryListener extends PollingSource { // ... @Parameter @Optional(defaultValue = "false") private boolean watermarkEnabled = false; // ... @Override public void poll(PollContext pollContext) { if(pollContext.isSourceStopping()){ return; } List attributesList = listFilesAttributes(); for (FtpFileAttributes attributes : attributesList) { if(pollContext.isSourceStopping()){ break; } PollItemStatus status = pollContext.accept(item -> { (1) Result result = read(attributes.getPath()); item.setResult(result); item.setId(attributes.getPath()); if (watermarkEnabled) { item.setWatermark(attributes.getTimestamp()); } }); if(!status.equals(PollItemStatus.ACCEPTED)){ LOGGER.debug("Item rejected with code: " + status.name()); } } } // ... } ```
1Get the PollItemStatus that is logged later.
### Getting the Source Callback Context If you need to save data to be used on the callback methods, `item` provides the `SourceCallbackContext` for it. This example shows how to get the context and then save data in it: ```java public class FtpDirectoryListener extends PollingSource { // ... @Parameter @Optional(defaultValue = "false") private boolean watermarkEnabled = false; // ... @Override public void poll(PollContext pollContext) { if(pollContext.isSourceStopping()){ return; } List attributesList = listFilesAttributes(); for (FtpFileAttributes attributes : attributesList) { if(pollContext.isSourceStopping()){ break; } PollItemStatus status = pollContext.accept(item -> { SourceCallbackContext context = item.getSourceCallbackContext(); (1) context.addVariable("attributes", attributes); (2) Result result = read(attributes.getPath()); item.setResult(result); item.setId(attributes.getPath()); if (watermarkEnabled) { item.setWatermark(attributes.getTimestamp()); } }); if(!status.equals(PollItemStatus.ACCEPTED)){ LOGGER.debug("Item rejected with code: " + status.name()); } } } // ... } ```
1Gets the context from item.
2Saves data on the SourceCallbackContext.
## Implementing the OnRejectedItem Method This method is called when one of the dispatched items was rejected. It should release any resource associated with the result set for the `PollItem`. This example shows how to implement it: ```java public class FtpDirectoryListener extends PollingSource { // ... @Override public void onRejectedItem(Result result, SourceCallbackContext callbackContext) { closeFileStream(result); } // ... } ``` ## How to Use Polling Sources in a Flow The SDK automatically adds a `scheduling strategy` parameter to the source, enabling the user to provide any strategy (like `fixed-frequency`) to handle the polling executions. The runtime will use that strategy to automatically schedule executions of the `poll` method. Here is an example of the FTP directory listener that uses the polling source: ```xml ``` --- product: Mule SDK version: 1.13 is-latest-version: true --- # Publishing Connectors to Exchange > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Starting August 1, 2026, publishing connectors and Mule plugins to Exchange requires Java compatibility metadata in `mule-artifact.json` when publishing new versions that change supported Java compatibility. Exchange reads Java compatibility from `mule-artifact.json` during publication. > [!NOTE] Update your connector configuration before publishing a new connector version. This change > doesn’t affect connectors that are already published to Exchange. ## Update Your Connector Use the requirements that match your connector type: - Java connectors: Include the `@JavaVersionSupport` annotation when changing supported Java compatibility. - XML connectors: Update to `mule-extensions-maven-plugin` so Java compatibility metadata is generated at build time. For XML connectors, choose the approach that matches your development setup: - If you use the parent POM, update to a parent version that includes `mule-extensions-maven-plugin` 1.9.0 or later. ```xml org.mule.connectors mule-modules-parent 1.9.0 ``` - If you use Mule SDK, upgrade to a version that includes `mule-extensions-maven-plugin` 1.9.0 or later. - If you configure Maven plugins manually, update your POM file to reference `mule-extensions-maven-plugin` 1.9.0 or later. ```xml org.mule.runtime.plugins mule-extensions-maven-plugin 1.9.0 ``` ## Build Prerequisites - Maven 3.8.0 or later (3.9.8+ recommended) - JDK 17+ in the build pipeline ## Verify Your Configuration After updating your connector configuration: 1. Build your connector locally. ```console mvn clean install ``` 2. Verify that `mule-artifact.json` contains Java compatibility information. In your connector JAR file, confirm that `mule-artifact.json` includes the `supportedJavaVersions` attribute. 3. Test publishing to Exchange. Publish to a test environment before publishing to production. ## Troubleshooting Publishing Fails After August 1, 2026 If Exchange rejects your connector with an error about missing Java compatibility information, verify your `mule-extensions-maven-plugin` version: 1. Check your POM file for `mule-extensions-maven-plugin`. 2. Ensure the version is 1.9.0 or higher. 3. Rebuild and republish your connector. The Build Fails After You Update the Plugin If the Maven build fails after you update to `mule-extensions-maven-plugin` 1.9.0 or later, use Maven 3.8.0 or later (3.9.8+ recommended) and JDK 17+ in your build pipeline. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, sources, listener, triggers, generate, push, message --- # Pushing Messages to a Flow form a Message Source > For the full documentation index, see: https://docs.mulesoft.com/llms.txt After generating a piece of information for the flow, the source uses the `SourceCallback` to send the information to the flow. Returning to the minimalistic HTTP listener example: ```java public class HttpListener extends Source { @Override public void onStart(SourceCallback sourceCallback) throws MuleException { httpServer = serverProvider.connect(); (1) server.listen(path).onRequest(request -> { (2) Result result = requestToResult(request); (3) sourceCallback.handle(result); (4) }); } } ```
1The example uses the ConnectionProvider to obtain an HttpServer. The typical connection provider most already configured the server with the proper host and port.
2HTTP requests are pushed into the server by the remote clients, and the HttpServer component will notify the source of every request. The source then uses the SourceCallback to push the message into the flow.
3The message is represented by leveraging the same Result object that operations use to communicate complex outputs. See Result for more information.
4The example uses the handle(Result) method in the SourceCallback to pass the message.
## Threading Model Once the `Result` object is passed to the `SourceCallback` through the `handle(Result)` method, message processing occurs asynchronously in a different thread than the one invoking the method. The next step explains how to catch the result of the Flow processing and optionally send a response. ## Next Step - [Obtaining Flow output and Sending responses from a Message Source](https://docs.mulesoft.com/mule-sdk/latest/sources-response) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: anypoint, studio, reconnection strategies, reconnection strategy, retry policies, retry --- # Reconnection on Connectors > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The SDK and Mule provide out-of-the-box reconnection strategies to be applied to all connectors that work with connections. This support is generic, so the experience of configuring and adjusting reconnection rules is the same across connectors. > [!IMPORTANT] HTTP Requester does not use the reconnection strategy for retries. See > [HTTP Request Configuration Reference](https://docs.mulesoft.com/http-connector/1.5/http-request-ref#retry-mechanisms) > for additional information. ## How the SDK Knows When to Trigger a Reconnection The SDK and Mule provide the reconnection engine, but the connector is responsible for communicating when a reconnection should occur, when a connection is down, and when a new connection is required. ## What is a Connection Error? A reconnection mechanism can obtain a new, valid connection when the existing one does not work anymore and does not allow the connector to execute its business logic. These errors are connection or connectivity errors. It is very important to understand when an error is a connectivity one and when it is not. Some examples follow below. ### Examples of Connection Errors - Connection Timeout - Invalid Credentials - Token Expired ### Examples of Non-connection Errors - Missing required HTTP Query Param when doing a HTTP request - Directory does not exist when trying to copy a file - Publish a Null message to Slack - Syntax error when executing a Database Query ## How to Trigger a Reconnection The main thing on reconnection is to be able to communicate correctly when a connectivity error occurred and is required to reconnect. How to communicate the error might differ depending on the component where the error has been detected. The sections below explain how to trigger a reconnection for each component of a connector. ### Connection Provider #### Creating Connection Connection Providers are the first moments of a connection life. At this moment, the connection provider can fail to create a connection because of several problems, so in this moment, the Connection Provider is able to communicate the connectivity error and trigger a reconnection, which means try to create the connection again. When creating the connection, `connect()` method, to declare a connection problem and is required a reconnection a [`ConnectionException()`](#connection-exception) should be thrown. ```java @Override public Connection connect() throws ConnectionException { Connection connection; try { connection = new Connection(); } catch (Exception e) { throw new ConnectionException("Error occurred trying to connect.", e); } return connection; } ``` #### Validating Connection To check that a connection is still valid, the SDK can grab a live connection at any time and validate it against the Connection Provider that created it. To validate the connection, `validate(Connection)` is invoked, and the connection provider should check if the given connection is valid or not. If the Connection Provider detects that the Connection is not valid anymore, it should communicate a [connection validation result](https://docs.mulesoft.com/mule-sdk/latest/connections#connection-validation-result) with a failure result `ConnectionValidationResult.failure(Cause, Exception)`. This will disconnect and invalidate the current connection, if possible, and a new connection will be created again. Example: Validation Over Connection ```java @Override public ConnectionValidationResult validate(Connection connection) { if(connection.isValid()){ return ConnectionValidationResult.success(); } else { return ConnectionValidationResult.failure("Connection Problem", new RuntimeException()); } } ``` ### Operations Operations can fail due to multiple causes, such as bad parameters values and unexpected errors. Errors can also be due to connectivity errors. To indicate that the error comes from a non-working connection, a [`ConnectionException()`](#connection-exception) is thrown. This exception **triggers the reconnection, and the operation is executed again**. #### Operation Example Example ```java public void operationTriggersReconnection(@Connection Connection connection, boolean connectionException){ if(connectionException){ throw new ConnectionException("Connection Error"); (1) } else { throw new RuntimeException(); (2) } } ```
1If a ConnectionException is thrown, the used connection will be invalidated. The connection provider will try to create a new valid connection, and if a connection is available, the operation will be executed again.
2If any other exception is thrown, the error will be considered as a business error, the connection will not be invalidated, and the operation will not be executed again.
### Sources Because a [Sources are responsible of creating their own Connections](https://docs.mulesoft.com/mule-sdk/latest/sources-config-connection#obtaining-a-connection), the way a Source triggers reconnections differs somewhat from Operations. If a successfully created connection is later disconnected, the source should communicate the connection failure: [`ConnectionException(Exception, Connection)`](#connection-exception) . Sources have two stages, when they are [starting](#starting-source) and when they are [running](#running-source). Each moment has to communicate their connectivity failures in different ways. > [!WARNING] Not communicating the Connection failure in the `ConnectionException` could make the > reconnection mechanism fail to work properly. #### Starting Source A source is in the starting source stage when the `onStart()` method executes. Connection errors must be communicated through the `SourceCallback` using the `onConnectionException()` method. The source stops and attempts to reconnect. Example ```java @Override public void onStart(SourceCallback sourceCallback) throws MuleException { Connection connection = connectionProvider.connect(); try { connection.startStreaming(); } catch(Exception e){ sourceCallback.onConnectionException(new ConnectionException(e, connection)); } } ``` #### Running Source When the Source starts correctly, it means that the `onStart()` method finishes correctly, and the remaining logic is running on other threads. When running on other threads, the SDK is unable to detect any kind of error raised with a `throw` statement. So the errors must be communicated through the `SourceCallback`, using the `onConnectionException()`. Example ```java @Connection ConnectionProvider connectionProvider; @Inject SchedulerService schedulerService; private Scheduler scheduler; @Override public void onStart(SourceCallback sourceCallback) throws MuleException { Connection connection = connectionProvider.connect(); scheduler = schedulerService.ioScheduler(); (1) scheduler.execute(() -> { while (true) { try { connection.receiveMessage(); } catch (Exception e){ sourceCallback.onConnectionException(new ConnectionException(e, connection)); (2) } } }); } ```
1Creating a scheduler to run the Source logic in a new Thread.
2Communicating to the SourceCallback the ConnectionException containing the failure connection that should be replaced.
#### Accessing the Retry Policy Template from Within an Operation Available since version 1.3. There are advanced cases in which an operation might need to access the `RetryPolicyTemplate` object that has been configured for it. For example, an operation might be leveraging an existing library that handles retries independently, or your use case might require applying retries without necessarily reconnecting. To support these cases, operations can request that the effective `RetryPolicyTemplate` instance be used by adding it as an operation parameter. Consider this example from Anypoint Connector for WebSockets (WebSockets Connector): ```java public void send(String socketId, @Content TypedValue content, @Connection WebSocketConnection conn, CompletionCallback callback, RetryPolicyTemplate retryPolicyTemplate) { } ``` The retryPolicyTemplate argument is not translated into an operation parameter, but it is injected with the effective value upon execution. > [!NOTE] This feature is intended for advanced scenarios only. Typically, you should throw a > ConnectionException and allow Mule to handle reconnection and retries automatically. Use this > feature only when absolutely necessary. ## Connection Exception A `ConnectionException` can be built with the following properties: - **Message** : String that describes the current error. - **Cause** : If present, Throwable indicates the cause the current error. - [**ErrorType**](https://docs.mulesoft.com/mule-sdk/latest/errors) : If present, ErrorType describing the current error. - **Connection** : If present, Object representing the connection failure. This will be used to disconnect and destroy it. ### See Also - [Sources Configuration and Connections](https://docs.mulesoft.com/mule-sdk/latest/sources-config-connection) - [Sources Lifecycle](https://docs.mulesoft.com/mule-sdk/latest/sources-lifecycle) - [Errors on Modules](https://docs.mulesoft.com/mule-sdk/latest/errors) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Sample Data > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Sample data enables operations and sources to provide examples of the output they produce. These examples show information obtained from a live system through a connection or the output format from a connectionless example. Sample data is supported by Mule 4.4.0 or later. However, all Anypoint Connectors can use this feature, regardless of their Mule version. With sample data, you can: - Implement SDK APIs - Provide examples using operation parameters - Provide examples using configuration and connection ## SDK APIs Use SDK APIs to provide sample data, communicate errors, and enhance components. ### SampleDataResolver Implement this API to enhance operations and message sources with sample data that is representative of the data the component outputs in real production scenarios. Implementations must: - Provide a default constructor. - Provide samples for both the payload and the message attributes, which return a Result object. Generic types always match the return types of the enhanced component. For components that return dynamic types, each implementation returns instances of the correct dynamic type. If the operation does not return attributes, use Result. - Guarantee that fetching sample data does not generate any significant side effects on the target system. - Not be used to return large objects or data streams. The data should fit into the memory of a small worker instance. Implementations can access parameters, configurations, and connections to generate the sample data through the `@Parameter`, `@Config`, and `@Connection` annotations. For connectors, implementations are most valuable when returning real data from the system they connect to instead of returning hard-coded values. The following example illustrates a SampleDataResolver API: ```java /** * [...] * * @param the generic type of the output value * @param the generic type of the message attributes * @since 1.4 */ @MinMuleVersion("4.4") public interface SampleDataProvider { /** * An ID that identifies each implementation of this interface. It must be unique among implementations in the same * extension. Subsequent invocations must always return the same fixed value. * * @return the resolver's id */ String getId(); /** * Obtains and returns the sample information. * * @return a {@link Result} object * @throws SampleDataException if the sample cannot be obtained */ Result getSample() throws SampleDataException; } ``` Streaming is not supported for: - Operations or sources that return an InputStream value. The resolver always returns implementations that do not require a connection, such as a ByteArrayInputStream value or any other offline implementations that have already procured the entire data stream before returning the getSample() method, because the InputStream value is fully consumable even after the underlying connection is closed. Avoid using temporal buffer files as well. - Operations that return a PagingProvider value, because they are considered streaming. The limitations explained in the previous bullet apply. In these cases, a List value of the same item type is returned instead. ### SampleDataException This class communicates errors while fetching sample data so that the runtime can handle those errors accordingly. Use the predefined failure code messages described in this class if necessary. The following example illustrates a SampleDataException class: ```java /** * {@link Exception} to indicate that an error occurred fetching sample data through a {@link SampleDataProvider}. * * @since 1.4.0 */ public class SampleDataException extends MuleException { /** * Signals an unexpected exception */ public final static String UNKNOWN = "UNKNOWN"; /** * Indicates that the {@link SampleDataProvider} didn't return any information */ public final static String NO_DATA_AVAILABLE = "NO_DATA_AVAILABLE"; /** * Indicates that a connection couldn't be established */ public final static String CONNECTION_FAILURE = "CONNECTION_FAILURE"; /** * Indicates that Sample Data resolution was attempted on a {@link Location} that doesn't exist */ public final static String INVALID_LOCATION = "INVALID_LOCATION"; /** * Indicates that the Sample Data resolution was attempted but the underlying {@link SampleDataProvider} has required parameters * that weren't supplied */ public final static String MISSING_REQUIRED_PARAMETERS = "MISSING_REQUIRED_PARAMETERS"; /** * Indicates that Sample Data resolution was attempted on a component that doesn't support that capability */ public final static String NOT_SUPPORTED = "NOT_SUPPORTED"; /** * Indicates that Sample Data resolution was attempted on an unregistered Extension */ public final static String INVALID_TARGET_EXTENSION = "INVALID_TARGET_EXTENSION"; private final String failureCode; public SampleDataException(String message, String failureCode) { super(createStaticMessage(message)); this.failureCode = failureCode; } public SampleDataException(String message, String failureCode, Throwable cause) { super(createStaticMessage(message), cause); this.failureCode = failureCode; } public SampleDataException(I18nMessage message, String failureCode) { super(message); this.failureCode = failureCode; } public SampleDataException(I18nMessage message, String failureCode, Throwable cause) { super(message, cause); this.failureCode = failureCode; } /** * @return The failure code of the error that produced the error */ public String getFailureCode() { return failureCode; } } ``` ### @SampleData This annotation associates a resolver to the enhanced component. It is applied at the type level in sources and at the method level for operations. The following example illustrates a `@SampleData` annotation: ```java /** * Associates a {@link Source} class or operation method with a {@link SampleDataProvider}. * * @since 1.4.0 * @see SampleDataProvider */ @Target({TYPE, METHOD}) @Retention(RUNTIME) @Documented public @interface SampleData { /** * @return the associated {@link SampleDataProvider} */ Class value(); /** * @return bindings between parameters in the {@link SampleData#value()} to extraction expressions. */ Binding[] bindings() default {}; } ``` ## Sample Data Examples Obtain sample data through an operation or through configuration and connection. ### Obtain Sample Data Through a Direct Operation Use this example to provide static sample data through an operation without using a connection. Imagine there is a new GitHub connector that contains an operation that retrieves information in JSON format. However, you do not want to consume an API call while providing sample data to consume one of a limited number or costly API calls, so you directly implement the operation. The following example illustrates a definition of the operation with the provider attached through the `@SampleData` annotation: ```java @SampleData(JsonSampleDataProvider.class) public Result getUserInfo(String username, @Connection GitHubConnection connection) { // operation code... } ``` The following example illustrates the sample data provider implementation, which uses the implementation shown previous to this to inject parameters. The `@Parameter` annotation has the same name and type as the operation’s parameter, which is `username`: ```java public class JsonSampleDataProvider implements SampleDataProvider { private static JsonObject sampleJson = new JsonObject(); public JsonSampleDataProvider() { sampleJson.add("company", "Mulesoft"); sampleJson.add("email", "maxmule2007@mulesoft.com"); sampleJson.add("repositories", new JsonArray("mule-api", "data-weave-2")); } @Parameter private String username; @Override public Result getSample() throws SampleDataException { sampleJson.addFirst("username", username); String body = sampleJson.getBody(); return Result.builder() .output(new ByteArrayInputStream(body.getBytes())) .mediaType(MediaType.APPLICATION_JSON) .length(body.length()) .build(); } @Override public String getId() { return "jsonResolver"; } } ``` Imagine the value for the `username` parameter is `John Doe`. The provider returns the following JSON: ```json5 { username: "John Doe", company: "Mulesoft", email: "maxmule2007@mulesoft.com", repositories: ["mule-api", "data-weave-2"], } ``` ### Obtain Sample Data Using Configuration Values and a Connection Use this example to provide dynamic sample data according to parameter and configuration values, while also requiring a connection. Imagine there is a new GitHub connector that contains both an operation and source that: - Have an **objectType** parameter that controls the type of entity retrieved, with possible values such as `pullRequest`, `issue`, or `commit` - Have an optional **status** parameter that filters the results - Use a connection for the GitHub API - Return a Result object The following example illustrates the operation definition: ```java @SampleData(ObjectSampleDataProvider.class) public Result getObject(String objectType, @Optional String status, String anotherParameter, @Connection GitHubConnection connection, @Config GitHubConfig config) { // operation code... } ``` The following example illustrates the source definition: ```java @SampleData(ObjectSampleDataProvider.class) public class OnNewThingTrigger extends Source { @Parameter private String objectType; @Parameter @Optional private String status; @Parameter private String extraParameter; @Config private GitHubConfig config; @Connection private ConnectionProvider connection; // source code... } ``` The following example illustrates the SampleDataProvider implementation that serves both cases. It uses the **objectType** and **status** parameters, as well as connection and configuration: ```java public class ObjectSampleDataProvider implements SampleDataProvider { @Parameter private String objectType; @Parameter @Optional private String status; @Connection private GitHubConnection connection; @Config private GitHubConfig config; @Override public Result getSample() throws SampleDataException { String query = "objectType=" + objectType; if (status != null) { query += "; status=" + status; } query += ";organization=" + config.getOrganization(); List jsonDocs = connection.query(query); if (jsonDocs.isEmpty()) { throw new SampleDataException("No data available", NO_DATA_AVAILABLE); } GitHubObject sample = jsonDocs.get(0); String body = sample.getBody(); return Result.builder() .output(new ByteArrayInputStream(body.getBytes())) .mediaType(MediaType.APPLICATION_JSON) .length(body.length()) .attributes(sample.getMetadata()) .attributesMediaType(MediaType.APPLICATION_JAVA) .build(); } @Override public String getId() { return "objectResolver"; } } ``` Both components have the same type signature as the sample data provider. They define the same parameters required by the resolver, but each has its own parameters that are not required by the resolver. The resolver accesses the parameters, connection, and configuration the same way a value provider does. If no data is available, the resolver throws a `SampleDataException` error with a defined failure code. ## OAuth Token Refresh If your operation or source uses an OAuth connection, trigger a token refresh in the provider by throwing an `AccessTokenExpiredException` error inside the getSample() method, as done for [operations or sources](https://docs.mulesoft.com/mule-sdk/latest/oauth-token-expiration). The following example illustrates a token refresh: ```java public class RefreshedOAuthSampleDataProvider implements SampleDataProvider { public static final String SAMPLE_PAYLOAD_VALUE = "Sample payload!"; public static final String SAMPLE_ATTRIBUTES_VALUE = "Sample Attributes!"; @Connection private TestOAuthConnection testOAuthConnection; @Override public String getId() { return "OAuth sample data"; } @Override public Result getSample() throws SampleDataException { if (!testOAuthConnection.getState().getAccessToken().contains("refresh")) { throw new AccessTokenExpiredException(); } return builder() .output(SAMPLE_PAYLOAD_VALUE) .attributes(SAMPLE_ATTRIBUTES_VALUE) .build(); } } ``` ## Aliases and Acting Parameters You can resolve parameters in the same way you resolve value providers. You can define aliases similar to defining an acting parameter as part of a parameter. ## Error Conditions During compilation, a series of validations check that the sample data provider complies with some of the restrictions described previously: - Output and Attributes Type Check The SDK validates that the provider is applied over components whose output and attributes types match the ones declared on its generic tuple. A validation error is thrown otherwise. - Parameters Usage Check The SDK validates that if a sample data provider uses a parameter, the related component also declares it as a parameter, while also checking that in both declarations the parameters are of the same type. A validation error is thrown otherwise. - Connection and Configuration Usage Check The SDK validates that if a sample data provider uses a connection or configuration, the related component also uses them. A validation error is thrown otherwise. - Instantiation Check The SDK validates that each provider is instantiable, which is a required condition. A validation error is thrown otherwise. - No Void A sample data provider cannot be placed on a void operation. A validation error is thrown otherwise. - Streaming Although streaming is not supported, this is not enforced. Your extension developer should follow best practices and comply with the contract. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, forward compatibility, minMuleVersion, versioning --- # SDK Forward Compatibility > For the full documentation index, see: https://docs.mulesoft.com/llms.txt In traditional SDK development, each SDK version is strictly tied to a specific Mule runtime version. Using any new SDK feature requires bumping the SDK version, which automatically increases your module’s minimum Mule version (`minMuleVersion`). This forces application developers to upgrade their entire Mule runtime engine just to use a newer version of your module. Starting with Mule 4.11.0 and using the new `mule-java-extension-parent`, the SDK introduces forward compatibility, which decouples the SDK version you compile against from the minimum Mule version your module requires. This enables your module to offer new features for later runtimes while continuing to support earlier runtimes with existing functionality. ## How Forward Compatibility Works Forward compatibility relies on a new artifact: `org.mule.sdk:mule-sdk-api`. This artifact can be bundled as a dependency of your module, unlike the traditional `org.mule.runtime:mule-extensions-api` and `org.mule.runtime:mule-api`. The new artifact contains all SDK annotations, interfaces, and classes, each annotated with the minimum Mule version it requires. When you build your module, the SDK tooling analyzes which APIs each component uses and calculates the minimum Mule version for each operation, source, and configuration. This information is stored in the Extension Model, making it available to design-time tools like Anypoint Studio and Anypoint Code Builder. These tools can then filter components or display warnings based on the application’s target Mule version. ## Benefits of Implementing Forward Compatibility Forward compatibility provides the following advantages: - Flexibility Introduce new features leveraging newer runtime capabilities while maintaining existing functionality on older runtimes. Application developers who don’t need the new features can continue using your module on their current runtime, while those who want them can target a newer runtime. - Simplified maintenance No need to maintain separate module versions for different runtimes—a single release serves all supported Mule versions. ## Getting Started With Forward Compatibility You can enable forward compatibility when creating a new module or by upgrading an existing one. ### Create a New Module With the Maven Archetype To create a new module with forward compatibility enabled, use the Maven archetype: mvn org.mule.extensions:mule-extensions-archetype-maven-plugin:generate When prompted, select a parent POM version of 1.11.x or later. The generated project will be configured for forward compatibility. ### Upgrade an Existing Module To enable forward compatibility in an existing module, change the parent POM in your `pom.xml` to use `mule-java-extension-parent` version 1.11.0 or later. See [Upgrading Existing Modules](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-setup#upgrading-existing-modules) for detailed instructions and considerations. ## Next Steps - See [Setting Up Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-setup) for instructions to configure your project for forward compatible development. - See [Working with Minimum Mule Versions](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-min-mule-version) to understand how minimum versions are determined and controlled. - See [Forward Compatibility Patterns](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-patterns) to learn common patterns for writing forward compatible code. ## See Also - [Choosing the SDK Version](https://docs.mulesoft.com/mule-sdk/latest/choosing-version) - [Module Structure](https://docs.mulesoft.com/mule-sdk/latest/module-structure) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Security > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The module **must** follow all standard security best practices. In particular, the following **should** be given special consideration: - Protect against XXE attacks - Always sanitize information from untrusted sources - When reading files from the local file system, make sure not to access secrets of the host environment nor enable path traversal - When possible, use CSRF tokens - When executing queries to a database, make correct use of PreparedStatement class to avoid SQL injection attacks. ## See Also - [Salesforce Help](https://help.salesforce.com) - [Mule SDK Development Best Practices](https://docs.mulesoft.com/mule-sdk/latest/best-practices) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, security, tls, oauth --- # Security > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The service to which the connector you are creating will connect probably enforces some kind of security for apps to talk to on behalf of a user. The SDK provides different two ways for end users of your connector to secure their connections. - [Adding TLS](https://docs.mulesoft.com/mule-sdk/latest/tls) - [Adding OAuth Authentication](https://docs.mulesoft.com/mule-sdk/latest/oauth) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, sources, listener, triggers, response, output, asynchronous --- # Sending Asynchronous Responses from a Message Source > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Sources can send responses in an asynchronous way. Some of the main uses cases for asynchronous sources are: - The response operation is non-blocking. - The response operation is blocking, but it is asynchronously executed in a thread other than the one that initiates it (most typically, the thread executing a method annotated with `OnSuccess` or `OnError`). - The source will perform asynchronous auditing for which it needs to consume a response stream in a separate thread. In these cases: - The runtime needs to know that the response will be asynchronous. Otherwise, there will be a race condition between the thread emitting the response and the runtime that is trying to free up resources associated with the message that is getting a response. - Methods annotated with `OnSuccess` or `OnError` must have an argument of type `SourceCompletionCallback`. That is enough to signal the runtime that the source is asynchronous. The runtime will not finish the associated event until either the `success()` or `error(Throwable)` methods are invoked. Note that this is a very strong contract. A source that requests a `SourceCompletionCallback` without properly notifying the runtime is one likely to freeze the entire runtime eventually. Here is an example: ```java @OnSuccess public void onSuccess(@Content String response, SourceCompletionCallback callback) { asyncResponder.sendResponse(response, new ResponderCallback() { void onSuccess() { callback.success(); } void onFailure(Throwable t) { callback.error(t); } };) } ``` ## Next Step [Transactional Message Sources](https://docs.mulesoft.com/mule-sdk/latest/sources-transactions) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, forward compatibility, setup, mule-sdk-api, parent pom --- # Setting Up Forward Compatibility > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Configure your module project for forward compatibility by updating the parent POM and using the new SDK API artifact. ## Configure the Parent POM To enable forward compatibility, your module must use the new `mule-java-extension-parent`: ```xml org.mule.extensions mule-java-extension-parent 1.11.0 ``` This parent POM includes the `mule-sdk-api` artifact and configures the build tooling for forward compatibility. ## Upgrade Existing Modules > [!IMPORTANT] All forward compatible APIs in `org.mule.sdk.api` require Mule 4.5.0 or later. Before > upgrading, consider whether having 4.5.0 as your module’s Minimum Mule Version is acceptable. If > your module must support runtimes older than 4.5.0, you can’t use forward compatibility. To upgrade an existing module, change the parent POM in your `pom.xml` to use `mule-java-extension-parent` version 1.11.0 or later. The new parent POM removes provided-scope dependencies on `org.mule.runtime` modules. Possible issues appear as compilation errors, resolve them by replacing `org.mule.runtime` imports with their `org.mule.sdk.api` counterparts. ## Include the SDK API Artifact in Your Module The `org.mule.sdk:mule-sdk-api` artifact provides a new package namespace: `org.mule.sdk.api`. This package contains all the SDK annotations, interfaces, and classes needed for module development. The key difference from traditional `org.mule.runtime` artifacts is that `mule-sdk-api` can be bundled with your module. This enables your module to compile against newer SDK features while still supporting older Mule Runtime versions. When writing new code or migrating existing code, use APIs from the `org.mule.sdk.api` package. For example: - `org.mule.sdk.api.annotation.param.Parameter` - `org.mule.sdk.api.runtime.streaming.StreamingHelper` - `org.mule.sdk.api.metadata.MetadataContext` ## Configure the Minimum Mule Version Unlike previous SDK versions, the parent POM version and the `mule-sdk-api` version don’t directly determine your module’s minimum Mule version. Instead, the minimum Mule version is calculated per component based on the APIs it uses. ### Baseline Minimum Mule Version When creating a new module with the archetype, the `min.mule.version` property is added to your POM with `4.11.0` as the default: ```xml 4.11.0 ``` See [Working with Minimum Mule Versions](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-min-mule-version) for details on how versions are calculated. ## See Also - [SDK Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility) - [Working with Minimum Mule Versions](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-min-mule-version) - [Getting Started with the Mule SDK for Java](https://docs.mulesoft.com/mule-sdk/latest/getting-started) - [HTTP-Based Connectors](https://docs.mulesoft.com/mule-sdk/latest/http-based-connectors) for the forward-compatible `org.mule.sdk.api.http` client API --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, operation, source, MIME, MIME Type, Encoding, Typed Value, Parameter Resolver, literal --- # Special Parameters > For the full documentation index, see: https://docs.mulesoft.com/llms.txt ## TypedValue A common use case for [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations) and [Sources](https://docs.mulesoft.com/mule-sdk/latest/sources) is to receive Media Type information along with the value of the parameter, for example: Example: Operation ```java public void receiveContent(InputStream content) { // operation logic } ``` In the Mule app example below, you can see that you are receiving a `JSON` value, but the operation’s code above does not indicate the MIME Type or encoding of the `content` parameter. Example: Mule app ```xml ``` To retrieve Media Type information (MIME Type and encoding), you simply need to wrap the parameter type into a `TypedValue`. This example use the same Mule app above with the `TypedValue` parameter: Media-Type-Friendly Parameter ```java public void receiveContent(TypedValue content) { DataType dataType = content.getDataType(); MediaType mediaType = dataType.getMediaType(); boolean streamType = dataType.isStreamType(); Long length = content.getLength().orElse(-1L); InputStream theContent = content.value(); // The proper value System.out.println(mediaType); // application/json; UTF-8 System.out.println(streamType); // true System.out.println(length); // 18 } ``` Use of `TypedValue` parameters is highly recommended for use cases where the `MIME Type` information is required to communicate with other systems that rely on this or to be able how to handle the parameter value. ## Literal `Literal` parameters allow you to get values as they were written in the Mule app. This means that if the end user writes an expression such as `#[attributes.queryParams]`, the operation will able to retrieve a `String` with the expression `#[attributes.queryParams]`. This prevents Mule runtime from resolving expressions or applying any transformations. This is useful when the evaluation needs to be performed by a third party. In the case of configurations and connection providers, using literal parameters [allows the configuration to be treated as a static one, instead of a dynamic one](https://docs.mulesoft.com/mule-sdk/latest/static-dynamic-configs). This is because the literal value is constant even if that literal is an expression. The runtime will not evaluate it automatically, so it is treated a static value by the extension. If the value was provided as an expression, the class will provide the expression but offer no way to evaluate it. You need to use [parameter resolvers](#parameter-resolver) for that. ```xml ``` Literal Parameter ```java public void literalValue(Literal literal) { System.out.println(literal.getLiteralValue().get()); // #[payload] System.out.println(literal.getType()); // java.io.InputStream } ``` > [!IMPORTANT] You need to consider the generic type. A parameter derived from uses of this > interface will have its type set according to the generic type. The generic is not optional. ## ParameterResolver `ParameterResolver` allows you to defer the resolution of an expression on an operation. This is useful when a parameter that takes an expression as a value is not required. You can improve performance by resolving such an expression on demand from within the connectors’s code instead of resolving it automatically. Compared to [`Literal`](#literal) parameters, a `ParameterResolver` parameter with an expression continues to be treated as a dynamic parameter whether the value is resolved or not. > [!NOTE] You cannot use `ParameterResolver` inside of configurations or connection providers. ### Resolving Expressions When a `ParameterResolver` is received to resolve the expression value, the `resolve()` method must be called to return the result of the expression. > [!WARNING] > > - Expression resolution is deferred until it is required, so `resolve()` can fail due to an > invalid expression. > > - Parameter resolvers are allowed only in operations. As such, you cannot use parameter resolvers > in configurations. ### Getting the Expression Value `ParameterResolver` parameters can communicate the expression used for the parameter. It returns an `Optional` because the parameter can be configured with an expression or not. If it is not configured with an expression, `Optional.empty()` will be returned. #### Example: With Expression Configured With an Expression ```xml ``` Example: Source ```java public void addProducts(ParameterResolver> products) { System.out.println(products.getExpression().get()); // #[{Potatoes : 2 , Oranges : 4}] System.out.println(products.resolve()); // {Potatoes=2, Oranges=4} } ``` #### Example: Without Expression Configured Without an Expression ```xml ``` ```java public void addProducts(ParameterResolver> products) { System.out.println(products.getExpression().isPresent()); // false System.out.println(products.resolve()); // {Potatoes=2, Oranges=4} } ``` ## Stacking Types You might need to use more than one of the parameters described above at the same time. For example, you might want to receive the Media Type of a given value and defer its resolution. To do this, you simply stack the generics for the capabilities you want. ```xml ``` ```java public void addProducts(ParameterResolver> products) { System.out.println(products.getExpression().get()); // #[output application/json --- {Potatoes : 2 , Oranges : 4}] TypedValue content = products.resolve(); System.out.println(content.getValue()); // {"Potatoes" : "2", "Oranges" : "4"} } ``` --- product: Mule SDK version: 1.13 is-latest-version: true keywords: stereotype, mule, sdk, types --- # Stereotypes > For the full documentation index, see: https://docs.mulesoft.com/llms.txt A stereotype is a loose way to classify a component. By assigning a stereotype to a component, you are not implying that it has any specific return type or structure. Instead, you are indicating that it holds something in common with the rest of the components in the same stereotype. You can assign a stereotype to make a parameter belong to the stereotype. ## Stereotype Annotation To assign a stereotype to a component, you must use the `@Stereotype` annotation along with a `StereotypeDefinition` class. You can use the `@Stereotype` annotation at these levels: - At the method level to affect a particular operation. - At the class level to assign the stereotype to all the operations defined in that class. A component can only belong to one stereotype. This means that if the `@Stereotype` annotation is used on a component, no other annotations can assign a stereotype to it. For example, a `@Validator` annotation, which is used to signal that an operation does not change the message and only performs some kind of validation, assigns a stereotype to the operation automatically. This example uses `@Stereotype` on operations: ```java @Stereotype(JenkinsStereotypeDefinition.class) public final class CommonJenkinsOperations { public void triggerBuild(String name, Map arguments) throws Exception { // ... } public void createJobs(String name, Map arguments) throws Exception { // ... } public Result ActiveJobs() throws Exception { // ... } } ``` In this case, all the operations in the class `CommonJenkinsOperations` will be of the stereotype defined in `JenkinsStereotypeDefinition`. ## StereotypeDefinition This interface provides the stereotype name, namespace, and parent. By default, the namespace is assigned to the extension’s namespace. Stereotypes have a hierarchy. By default, they do not extend from another stereotype unless specified. This example show how to define a `StereotypeDefinition`: ```java public final class JenkinsStereotypeDefinition extends MuleStereotypeDefinition { (1) @Override public String getName() { return "JENKINS_OPERATION"; } @Override public Optional getParent() { return Optional.of(PROCESSOR_DEFINITION); (2) } } ```
1The JenkinsStereotypeDefinition class extends from a StereotypeDefinition that knows about more than one StereotypeDefinition like PROCESSOR_DEFINITION.
2Selects PROCESSOR_DEFINITION that is a StereotypeDefinition defined in MuleStereotypeDefinition as this `StereotypeDefinition’s parent.
## AllowedStereotypes Annotation on Parameters This annotation identifies every `StereotypeDefinition` that is accepted for a parameter. The value of this annotation is a single value or array of `StereotypeDefinition`. It can only be applied to parameters of type `Chain` or `Route`. This example enforces a certain stereotype as a parameter: ```java public class JenkinsScope { private static final Logger logger = getLogger(JenkinsScope.class); public void jenkinsGroupExecution(@AllowedStereotypes(JenkinsStereotypeDefinition.class) Chain operations, CompletionCallback callback) throws Exception { LOGGER.debug("Invoking Jenkins Operations.") operations.process( result -> { // Log information about the execution of the operations callback.success(result); }, (error, previous) -> { LOGGER.error(error.getMessage()); callback.error(error); }); } } ``` The `Chain` that is received can only contain operations with the stereotype defined in `JenkinsStereotypeDefinition` class. ## Configuration Reference Every configuration has a unique stereotype that is assigned automatically. This stereotype has the following characteristics: - Both the name and the namespace are made uppercase, and words are separated by underscores. - The namespace is the namespace of the extension, for example, "HTTP" for the HTTP Connector or "JMS" for the JMS Connector. - The name is the name of the configuration. For example, the stereotype name of the "requestConfig" configuration will be "REQUEST\_CONFIG". This default stereotype can be overwritten using the `@Stereotype` annotation. You can use this stereotype to restrict a `String` parameter to a name of certain type of configuration by using the `@ConfigReference` annotation. This annotation requires the name and namespace of the configuration stereotype. A usage example of this feature: Developing an extension that uses the HttpService, which requires a HttpRequesterConfig for its use. Then, with the use of the Extension Client, the HttpService will use the actual Configuration. For example, if the HTTP Connector declares this `HttpRequesterConfig`: ```java @Configuration(name = "requestConfig") @ConnectionProviders(HttpRequesterProvider.class) @Operations({HttpRequestOperations.class}) public class HttpRequesterConfig { // Parameters and getters for the Configuration } ``` And the Web Service Consumer connector is defined like this: ```java @ErrorTypes(SoapErrors.class) @Operations(ConsumeOperation.class) @ConnectionProviders(SoapClientConnectionProvider.class) (1) @SubTypeMapping(baseType = CustomTransportConfiguration.class, subTypes = CustomHttpTransportConfiguration.class) @Extension(name = "Web Service Consumer") @Xml(prefix = "wsc") public class WebServiceConsumer { } ```
1This ConnectionProvider holds a parameter that is using the @ConfigReference annotation. Note that the default config is used because the connector does not declare any configuration.
This is part of the declaration of the ConnectionProvider: ```java public class SoapClientConnectionProvider implements CachedConnectionProvider { // ... @Inject private HttpService httpService; // ... @Placement(tab = "Transport") @Parameter @Optional @Expression(NOT_SUPPORTED) @DisplayName("Transport Configuration") private CustomTransportConfiguration customTransportConfiguration; // ... } ``` Here is where the `@ConfigReference` annotation is used: ```java @Alias("http-transport-configuration") public class CustomHttpTransportConfiguration implements CustomTransportConfiguration { @ConfigReference(namespace = "HTTP", name = "REQUEST_CONFIG") (1) @Parameter private String requesterConfig; @Override public MessageDispatcher buildDispatcher(ExtensionsClient client) { return new HttpConfigBasedMessageDispatcher(requesterConfig, client); (2) } @Override public TransportResourceLocator resourceLocator(ExtensionsClient client) { return new HttpResourceLocator(requesterConfig, client); (2) } } ```
1The String parameter requesterConfig must take the value of the name of a HttpRequesterConfig.
2The name of the configuration is used along with the ExtensionsClient
--- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, streaming, data, pagination --- # Streaming > For the full documentation index, see: https://docs.mulesoft.com/llms.txt In Mule 4, streaming allows automatic and transparent repeatable streams (see About Streaming below for details). There are two types of streaming: - [Binary Streaming](https://docs.mulesoft.com/mule-sdk/latest/binary-streaming) - [Object Streaming](https://docs.mulesoft.com/mule-sdk/latest/object-streaming) ## See Also [About Streaming](https://docs.mulesoft.com/mule-runtime/latest/streaming-about#repeatable-streams) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: connector, certification, sdk, program guidelines --- # Technical Guidelines for Connector Certifications > For the full documentation index, see: https://docs.mulesoft.com/llms.txt 1. Set up your development environment using these sections from _Getting Started with the Mule SDK_: - [Setting Up Your Development Environment](https://docs.mulesoft.com/mule-sdk/latest/getting-started#setup) - [Setting Up Your IDE](https://docs.mulesoft.com/mule-sdk/latest/getting-started#setup_ide) 2. Create or migrate your connector: - If you are a partner or developer, create a Mule 4 connector. 1. Run this command: `mvn org.mule.extensions:mule-extensions-archetype-maven-plugin:generate` 2. Complete the configuration through the console: - Provide the name of your extension, for example: `cookbook` - When you receive a request for the `groupId`, accept the default. - Specify the `artifactId` of _your_ extension, for example: `cookbook-connector` - Specify the version of your extension. If this is your first connector, use `1.0.0`. If not, create the major version from your last connector. For example, the connector version is `v2.0.0`. - When you receive a request for the main package of your extension, accept the default. 3. Update your `pom.xml` file: - Locate these `` settings: org.mule.extensions mule-modules-parent 1.9.0 - Change the settings to this: org.mule.connectors mule-certified-parent 1.1.0 4. Add one of the following sets of annotations to your `Extension.java`: - For MuleSoft Certified Connectors (Free): Example: Annotations for Azure Storage ```annotations @Extension(name = “Azure Storage”, category = CERTIFIED, vendor = “Whishworks") @RequiresEnterpriseLicense(allowEvaluationLicense = true) ``` - For MuleSoft Certified Connectors (Additional Fee): Example: Annotations for SAP Hybris ```annotations @Extension(name = “SAP Hybris”, category = CERTIFIED, vendor = “Admios") @RequiresEnterpriseLicense @RequiresEntitlement(name = “sap-hybris-connector") ``` 5. Set the connector icon. See [Add a Custom Icon to Your Connector](https://docs.mulesoft.com/mule-sdk/latest/getting-started#set-custom-icon). 6. Add a folder for `demo`. 7. Add `License.md` if it is not in the root folder already: ```text About MuleSoft Certified Connectors MuleSoft Certified Connectors are developed by MuleSoft’s partners and developer community. These connectors have been reviewed and certified by MuleSoft. To purchase the [Connector Name] Connector or to receive assistance or support for it, contact [Partner Name] directly at [Contact Information]. MuleSoft disclaims any support obligation for MuleSoft Certified Connectors. By installing this connector, you consent to MuleSoft sharing your contact information with the developer of this connector so that you can receive more information about it directly from the developer. Your license terms ``` You can develop or add operations to your connector by following the [Mule SDK documentation](https://docs.mulesoft.com/mule-sdk/latest/). After you complete the preceding steps, the structure of your connector is ready for Mule 4. 8. Add unit, functional, and integration test cases for your connectors by following [Testing Your Module](https://docs.mulesoft.com/mule-sdk/latest/testing). Note that your connector needs test coverage of at least 65%. 9. Add your connector to Connectivity Assets Manager. You can use your existing account for Connectivity Assets Manager. If you do not have an account, contact MuleSoft. ## FAQ **Is there a kit or tool for migrating DevKit to Mule 4 SDK?** - Yes. The migration tool will be available for its GA in Q2, 2018. You can use the beta version in the meantime. org.mule.tools.dmt mule-dmt 1.0.0 **Where is a sample SDK connector project?** - The steps described in this guide let you create a structure of a connector project. **How can I import a project into Studio 7, and how can I test and add it to Studio?** - Functionality for importing a connector project into Studio 7 is not available at this time. You can install your connector to Studio 7 using this command to create a connector for use in Studio 7: `mvn clean install` See [Installation Example](#installation_example) for detailed steps. **Can a module recognize that it is being executed from Studio as opposed to on premises or in Runtime Manager, or are there any related concerns?** Background: If a connector has a custom license code based on the target AS/400 server serial numbers, the license check is performed dynamically as the connection is established. MuleSoft allows customers to evaluate the connector within Studio without a license. However, the connector does not work when running on premises or in Runtime Manager. Answer: - Custom license code will not be enforced in Runtime Manager. MuleSoft recommends that you use the license validation supported natively by Mule 4 SDK. You can find more information in [Module Licensing](https://docs.mulesoft.com/mule-sdk/latest/license). - In your `xxxExtension.java`, add the following annotations to use the license validation from Mule 4 SDK. This example uses SAP Hybris Connector built by Admios: Example: MuleSoft Certified Connectors (Additional Fee) ```text @RequiresEnterpriseLicense @RequiresEntitlement(name = “sap-hybris-connector") ``` ## Installation Example This example supplements the FAQ, [How can I import a project into Studio 7, and how can I test and add it to Studio?](#question_3). You can install your connector to Studio 7 using this command to create a connector for use in Studio 7: 1. Run `mvn clean install`. This example shows the `cookbook` connector installation to a local Maven repository: ```console [INFO] No primary artifact to install, installing attached artifacts instead. [INFO] Installing /Users/me/Downloads/mule4SampleConnector/cookbook-connector/pom.xml to /Users/me/.m2/repository/org/mule/extension/cookbook-connector/1.0.0/cookbook-connector-1.0.0.pom [INFO] Installing /Users/me/Downloads/mule4SampleConnector/cookbook-connector/target/temporal-extension-model.json to /Users/me/.m2/repository/org/mule/extension/cookbook-connector/1.0.0/cookbook-connector-1.0.0-extension-model-4.0.0.json [INFO] Installing /Users/me/Downloads/mule4SampleConnector/cookbook-connector/target/cookbook-connector-1.0.0-mule-plugin.jar to /Users/me/.m2/repository/org/mule/extension/cookbook-connector/1.0.0/cookbook-connector-1.0.0-mule-plugin.jar [NOTE] --- [INFO] BUILD SUCCESS [NOTE] --- [INFO] Total time: 35.057 s [INFO] Finished at: 2018-01-11T17:10:50-08:00 [INFO] Final Memory: 49M/476M [NOTE] --- ``` 2. Create a Mule project in Studio 7, and open `pom.xml` for this project in Studio 7. ![Folder structure of the pom xml file](/mulesoft/current/en-us/mule-sdk/latest/_images/pom-file.png) 3. Add a dependency setting (``) for your connector to the `pom.xml`. For example, the following `` enables Studio 7 to find the `cookbook-connector` installed in a local Maven repository: org.mule.extension cookbook-connector 1.0.0 mule-plugin Your connector will now appear the palette. This example shows a Cookbook connector in the Studio palette: ![Example of Cookbook Connector in the Studio palette](/mulesoft/current/en-us/mule-sdk/latest/_images/ex-connector-in-palette.png) ## See Also [About Mule Runtime](https://docs.mulesoft.com/mule-runtime/latest/) [Mule SDKs](https://docs.mulesoft.com/mule-sdk/latest/) [Mule 4 SDK](https://www.youtube.com/watch?v=qdOXKj8V9Lc&feature=youtu.be) (video) [MuleSoft Trailblazer Community](https://trailhead.salesforce.com/trailblazer-community/neighborhoods/mulesoft) [Best Practices](https://docs.mulesoft.com/mule-sdk/latest/best-practices) [Slack Connector](https://github.com/enterprises/salesforce-emu/sso?return_to=https://github.com/mulesoft-emu/mule4-slack-connector) [HTTP Connector](https://github.com/mulesoft/mule-http-connector/tree/1.2.0) [Database Connector](https://github.com/mulesoft/mule-db-connector/tree/1.3.1) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Testing Your Module > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Testing ensures that a piece of code works correctly. Testing a module is more than simply creating test cases for the business logic. A lot of testing of the interaction between the Module and Mule is required to ensure compatibility with the Mule ecosystem. Here are a series of articles that explain in detail how to test your module. It provides a step-by-step introduction on how to test a module. It explains the basis of a Module Test Case and how to execute a simple operation from a Module. - [Writing your first test case](https://docs.mulesoft.com/mule-sdk/latest/testing-writing-your-first-test-case) (with [FlowRunner](https://docs.mulesoft.com/mule-sdk/latest/testing-writing-your-first-test-case#testing-flowrunner)) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, result, output, mimeType, attributes, operation, source --- # The Result Object > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The `Result` object represents the result of a component’s execution. It is used for cases in which the component not only needs to return a value to be set on the message payload but also will specify message attributes and/or a mime type. The class has two generic types, one for the type of the value that goes into the payload and another for the type of the returned message attributes. For example, consider an oversimplified `read` operation from the File connector. This operation sets the payload to an `InputStream` with the file’s content. It also sets the attributes to a `FileAttributes` object that contains information, such as file name, size, timestamp, permissions, and so on. ```java public Result read(String path) { (1) InputStream content = getContent(path); FileAttributes attributes = getAttributes(path); return Result.builder() (2) .output(content) (3) .attributes(attributes) (4) .build(); (5) } ```
1The operation returns a Result<InputStream, FileAttributes>. This tells the runtime that the operation returns an InputStream as payload and FileAttributes as attributes.
2Once it obtains the result values, it uses Result.builder() to create a new instance. Note that this is strongly typed, so you need to specify the generics when creating the builder.
3The output(Object) method in the builder is used to set the payload.
4The attributes(Object) is used to set the attributes.
5The example calls the build() method and returns the created Result.
## Setting the Mime Type The `Result` object can be used to set the output `mimeType`. Building on the previous example, the operation here makes a best guess about the file’s `mimeType` based on its file extension: ```java public Result read(String path) { InputStream content = getContent(path); FileAttributes attributes = getAttributes(path); MediaType guessedMediaType = MediaType.ANY; if (path.endsWith(".json")) { guessedMediaType = MediaType.APPLICATION_JSON; } else if (path.endsWith("*.xml")) { guessedMediaType = MediaType.APPLICATION_XML; } else if (path.endsWith("*.bin")) { guessedMediaType = MediaType.BINARY; } return Result.builder() .output(content) .attributes(attributes) .mediaType(guessedMediaType) .build(); } ``` > [!TIP] In the example above, the `MediaType` class is not the `@MediaType` annotation but Mule > API’s `org.mule.runtime.api.metadata.MediaType`. ## Void Attributes For a use case where you want to set the payload and the `mimeType` but do not want to set attributes, you use the `` type, for example: ```java public Result read(String path) { InputStream content = getContent(path); return Result.builder() .output(content) .mediaType(bestGuessMediaType(path)) .build(); } ``` --- product: Mule SDK version: 1.13 is-latest-version: true --- # Threading and Asynchronous Processing > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Most modules **should not** have concurrency concerns other than designing for Thread safety. However, there are some very valid use cases in which a module needs to either perform tasks asynchronously or schedule recurring tasks. This section elaborates on the best practices for doing that. As you go through this section, keep in mind that when we refer to a Scheduler, we’re actually referring to an instance of the org.mule.runtime.api.scheduler.Scheduler class. > [!NOTE] Do not confuse asynchronous processing with non-locking I/O. These are not the same thing. ## Don’t Create Your Own Threads or Executors Modules **must not** create their own Threads, Executors or ExecutorServices. Any asynchronous tasks **must** be done by obtaining a Scheduler from Mule’s SchedulerService. The SchedulerService API is very well documented in its Javadocs. Be mindful of the contracts documented there and use them accordingly. ### Dependency-Created Threads Sometimes a module has no choice but to depend on an external source to create its own threads. For example: - A JMS connector that depends on broker libraries to create its own consumer Threads - A client library that spawns its own I/O selector Threads - A JDBC driver that spawns its own Threads As these cases are unavoidable, they are valid exceptions to this rule. However, the following conditions **must** be met: - When there’s no viable alternative and there’s no point in creating your own JMS broker just for the sake of being in control of the Threads - The Threads created by these libraries **must** have a meaningful name Keep in mind that some libraries may allow you to pass an Executor instance and fall back to create its own threads if not provided. In such cases, passing the executor **should** be the method you use. Refer to the custom Schedulers section that follows for additional details. ## Do Not Use Schedulers for Polling in Sources Sources **must not** create Schedulers with the purpose of doing polling. Extend the PollingSource class instead. ## Manage Schedulers Lifecycle As most things in Mule, Schedulers have a lifecycle that **must** be carefully handled. - Choose the correct Scheduler type The Scheduler Service provides three types of Schedulers: - CPU Light These schedulers **should** be used for asynchronous tasks that complete really quickly without significant CPU consumption. They can also be used for tasks that perform non-locking I/O. Examples include simple validations, light calculations, or any kind of logic that doesn’t perform I/O and takes around 10 ms to complete. - CPU Intensive For tasks that perform intensive CPU computation. No I/O operations or long blocking, such as waiting for a lock held by an I/O thread while doing I/O **should** be performed here. Examples include encryption, heavy transformations, expensive validations, and any other kind of logic that doesn’t perform I/O and makes heavy use of the CPU for more than 20 ms. - IO Scheduler For blocking I/O operations, such as writing to an FTP site, accessing a relational database, and so on. Tasks submitted to any of these Schedulers **must** be compliant with the type of work that each Scheduler is meant to handle. These schedulers are slices of the main thread pools used by the Mule 4 [Execution Engine](https://docs.mulesoft.com/mule-runtime/latest/execution-engine). Failing to comply with each Scheduler work profile can lead Mule to operate with sub-optimal performance. - Custom Schedulers There are certain types of Schedulers that don’t completely fit into the profile of any of the three Scheduler types. Custom Schedulers **should** be used for: - Tasks that execute and repeat at a fixed rate (and **should not** be part of a PollingSource). Examples include (but are not limited to): - Cache expiration tasks - Resource releasing tasks - I/O selectors - Queue consumers - Tasks that require their own thread pool - Tasks that have a mixed profile that doesn’t completely fix any of the previous criteria - Give Schedulers a meaningful name All Schedulers obtained through the SchedulerService **must** be assigned with a meaningful name that refers to: - The name of the module that created the Scheduler - The name of the configuration object for the component that created the Scheduler or its position in a flow - The Scheduler purpose - Define the owning Component An important aspect of managing your own Schedulers is defining which component **should** own it: - If the Scheduler is to be used by a source, then the Scheduler **must** be defined as part of that source - If the Scheduler is used for releasing connection related resources, then it **should** be owned by a ConnectionProvider - If the Scheduler performs tasks not specific to any component, or many components will receive tasks from different components, then it **should** be owned by a configuration object The owning component **must** be one that is lifecycle aware. This means that at a minimum it **must** either: - Implement the Initialisable and Disposable interfaces - Implement the Startable and Stoppable interfaces - Never create Schedulers at the operation level Schedulers **must** never be created in operation classes. If an operation requires the use of a Scheduler, then it **must** be created at a config element level and the Scheduler shared across all operations. - Make a best effort to share Schedulers Modules **should** create as many Schedulers as needed, but as few as possible. There are use cases in which it makes sense for different components to have their own Schedulers, but a best effort **must** be made to share and reuse Schedulers across components. - Guarantee that all Schedulers are stopped All created Schedulers **must** be guaranteed to eventually invoke the stop() method. Invocation of this method **should** be tied to the lifecycle of the component that owns it. For example, if the Scheduler is owned by a configuration element, then that component **should** implement the Stoppable interface and invoke it as part of its own stop() call. Sources **should** do it during the onStop() method and so forth. ## Scheduler Lifecycle Example This example of a Source-owned Scheduler applies all the lifecycle concepts: ```java public class ExampleSourceScheduler extends Source { @Inject private SchedulerService schedulerService; @Inject private SchedulerConfig schedulerConfig; private ComponentLocation location; private Scheduler scheduler; @Override public void onStart(SourceCallback sourceCallback) throws MuleException { scheduler = schedulerService.customScheduler(schedulerConfig .withMaxConcurrentTasks(numberOfConsumers) .withName("vm-listener-flow " + location.getRootContainerName()) .withWaitAllowed(true) .withShutdownTimeout(5, SECONDS)); } @Override public void onStop() { if (scheduler != null) { scheduler.stop(); } } } ``` ## See Also - [Salesforce Help](https://help.salesforce.com) - [Mule SDK Development Best Practices](https://docs.mulesoft.com/mule-sdk/latest/best-practices) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, security, tls --- # TLS Configuration > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Adding support for TLS configuration in your module is as simple as declaring a [parameter](https://docs.mulesoft.com/mule-sdk/latest/parameters) of type `TlsContextFactory`, for example: ```java @Parameter @Optional private TlsContextFactory tlsContextFactory; ``` The example above will automatically generate support for this XML setting: ```xml ``` Note that a TLS context can also be defined as a global element of your app, so you can reference it by its name anywhere you need to use it. If TLS needs are hidden from the user (for example, if you need to connect safely to a service internally), you can create your own `TlsContextFactory` using its builder: ```java private final TlsContextFactory tlsContextFactory = TlsContextFactory.builder() .trustStorePath("tls/trustStore") .trustStorePassword("mulepassword") .build() ``` This builder also allows creating default instances of `TlsContextFactory`: ```java private final TlsContextFactory defaultTlsContextFactory = TlsContextFactory.builder().buildDefault(); ``` Once a `TlsContextFactory` is available (whether as a parameter or obtained through its builder), it should be initialized, for example: ```java if (tlsContextFactory instanceof Initialisable) { ((Initialisable) tlsContextFactory).initialise(); } ``` It can then be used to obtain an `SSLContext`, a `SSLSocketFactory` or a `SSLServerSocketFactory`. It can also obtain enabled TLS protocols and cipher suites, allowing you to set up most Java components that use TLS/SSL as shown in the examples below. You could set up a [FTPSClient](https://commons.apache.org/proper/commons-net/apidocs/org/apache/commons/net/ftp/FTPSClient.html) by obtaining an [SSLContext](https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/SSLContext.html) and then configure the protocols and cipher suites for it: ```java SSLContext sslContext = tlsContextFactory.createSslContext(); String[] enabledProtocols = tlsContextFactory.getEnabledProtocols(); String[] enabledCiphers = tlsContextFactory.getEnabledCipherSuites(); FTPSClient client = new FTPSClient(sslContext); client.setEnabledProtocols(enabledProtocols); client.setEnabledCipherSuites(enabledCiphers); ``` You could obtain an [SSLSocketFactory](https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/SSLSocketFactory.html) to create your own [Socket](https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html): ```java SSLSocketFactory sslSocketFactory = tlsContextFactory.createSocketFactory(); Socket socket = sslSocketFactory.create(); ``` To create a [ServerSocket](https://docs.oracle.com/javase/8/docs/api/java/net/ServerSocket.html)instead, you can obtain an [SSLServerSocketFactory](https://docs.oracle.com/javase/8/docs/api/index.html?javax/net/ssl/SSLServerSocketFactory.html): ```java SSLServerSocketFactory sslServerSocketFactory = tlsContextFactory.createServerSocketFactory(); ServerSocket socket = sslServerSocketFactory.createServerSocket(); ``` To correctly configure TLS context and all the available parameters that you can set, see [Configure TLS with Keystores and Truststores](https://docs.mulesoft.com/mule-runtime/latest/tls-configuration). --- product: Mule SDK version: 1.13 is-latest-version: true --- # Tracing Features in the Mule Java SDK > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Tracing in the Mule Java SDK enables SDK-developed components to contribute telemetry data to the Mule runtime engine. Sources and operations can customize spans by defining span names, adding attributes, and setting the OpenTelemetry span kind. Tracing also supports trace context propagation, which integrates the Mule Java SDK with distributed tracing systems. ## Implementing Tracing Features For operations, add `DistributedTraceContextManager` as an argument. The SDK [implicitly injects](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-implicit-params) this argument during each operation call. For sources, obtain `DistributedTraceContextManager` from `SourceCallbackContext`. `DistributedTraceContextManager` provides access to all the available tracing features for both sources and operations. ## Example: Customize an Operation Span This example shows a module definition: ```java @Extension(name = "Foo") @Operations(FooOperations.class) public class FooModule { @Parameter private String fooModuleParameter; public String getFooModuleParameter() { return fooModuleParameter; } } ``` This operation customizes the span: ```java public class FooOperations { public String foo(@Config FooModule config, DistributedTraceContextManager distributedTraceContextManager) { // Implicit parameters must handle potential null values. if (distributedTraceContextManager != null) { // Override the current span name. distributedTraceContextManager.setCurrentSpanName("fooOperation"); // Add an attribute to the span. distributedTraceContextManager.addCurrentSpanAttribute("attributeKey", "attributeValue"); } return "OK"; } } ``` ## Example: Customize a Source Span Sources [push messages to flows](https://docs.mulesoft.com/mule-sdk/latest/sources-push-message) by using `SourceCallbackContext`. This example is based on the HTTP Listener behavior: ```java public class HttpListener extends Source { @Override public void onStart(SourceCallback sourceCallback) throws MuleException { httpServer = serverProvider.connect(); server.listen(path).onRequest(request -> { SourceCallbackContext context = sourceCallback.createContext(); // Obtain the DistributedTraceContextManager from the SourceCallbackContext. DistributedTraceContextManager distributedTraceContextManager = sourceCallback.getDistributedTraceContextManager(); // Override the current span name. distributedTraceContextManager.setCurrentSpanName("http:special-source"); // Add an attribute to the span. distributedTraceContextManager.addCurrentSpanAttribute("attributeKey", "attributeValue"); Result result = requestToResult(request, context); sourceCallback.handle(result); }); } } ``` ## Trace Context Propagation Sources and connectors play different roles in both Mule and distributed tracing. In Mule, a source triggers a flow and creates the first span in its corresponding trace. Because of this role, the source must handle distributed trace context. If a trace already exists, the source must continue that trace instead of creating a new one. Connectors propagate the current trace context to external systems outside Mule runtime. These systems can include databases, non-Mule APIs, and message queues. The Mule runtime Java SDK supports the W3C trace context propagation standard. This standard defines a [trace context](https://www.w3.org/TR/trace-context/#trace-context-http-headers-format) that consists of a `traceparent` and a `tracestate`: ```text traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01 tracestate: congo=t61rcWkgMzE ``` This representation enables trace context propagation across systems that support the standard. ## Example: Trace Context Propagation in Sources and Connectors This example uses the HTTP Listener and the HTTP Requester to demonstrate trace context propagation. When the HTTP Listener receives W3C trace context headers in a request, use the `DistributedTraceContextManager` to indicate where to retrieve the W3C trace context: ```java public class HttpListener extends Source { @Override public void onStart(SourceCallback sourceCallback) throws MuleException { httpServer = serverProvider.connect(); server.listen(path).onRequest(request -> { SourceCallbackContext context = sourceCallback.createContext(); // Obtain the DistributedTraceContextManager from the SourceCallbackContext. DistributedTraceContextManager distributedTraceContextManager = sourceCallback.getDistributedTraceContextManager(); // This defines the map where the W3C trace context key values are available for retrieval (an HTTP request in this case, so the HTTP headers are used). // The mule runtime will trace the flow execution as child spans of this provided trace context. distributedTraceContextManager.setRemoteTraceContextMap(request.getHeaders()); Result result = requestToResult(request, context); sourceCallback.handle(result); }); } } ``` The HTTP Requester retrieves the current W3C trace context during request operations by using `getRemoteTraceContextMap()`, and then propagates it: ```java public class HttpRequestOperations implements Initialisable, Disposable { public void request(HttpRequesterConfig config, DistributedTraceContextManager distributedTraceContextManager) { HttpRequestBuilder builder = HttpRequestBuilder.createRequestBuilder(config); // Implicit parameters must handle potential null values. if (distributedTraceContextManager != null) { // This obtains the current trace context as a map, propagating it to the connected system (an HTTP endpoint in this case, so HTTP headers are added). distributedTraceContextManager.getRemoteTraceContextMap().forEach(builder::addHeader); } } } ``` ## Distributed Trace Context API This table shows the distributed trace context manager methods:

Method

Parameter

Description

void setRemoteTraceContextMap(Map<String, String> contextMap)

contextMap

A Map<String, String> that contains the key-value pairs that store the information of the remote trace context.

void setCurrentSpanName(String name)

name

Sets the name of the current span.

Map<String, String> getRemoteTraceContextMap()

N/A

Returns the context map for the remote trace context.

void addCurrentSpanAttribute(String key, String value)

  • key

  • value

  • Key of the span attribute.

  • Value of the span attribute. Use span.kind.override as the key to override the span kind.

void addCurrentSpanAttributes(Map<String, String> attributes)

attributes

A Map<String, String> that contains the attributes to add to the current span. Use span.kind.override as the key to override the span kind.

> [!NOTE] `setRemoteTraceContextMap()` is intended for sources only. Mule silently ignores calls > made from connector operations. ## See Also - [OpenTelemetry Support in Mule Runtime](https://docs.mulesoft.com/mule-runtime/latest/otel-support) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, sources, listener, triggers, transactions --- # Transactional Message Sources > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Just like operations, Message Sources support transactions. Examples of Message Sources include the JMS and VM connector listeners. Both listeners take messages out of a queue and push it to a Flow. If the message processing is successful, the transaction is committed. Otherwise, the transaction is rolled back, and the message goes back to the queue. To be transactional, a Message Source needs to operate over a connection type that implements the `TransactionalConnection` interface, just like operations. XA transactions are also supported automatically if the connection implements `XATransactionalConnection`. Example: Transactional Message Source ```java public class TransactionalSource extends Source { @Connection private ConnectionProvider connectionProvider; .... } ``` ## Message Source Transactional Actions When a Message Source is transactional, a synthetic parameter named `transactionalAction` is automatically added to it. That parameter is an `enum` type that can take these values: - ALWAYS\_BEGIN: Ensures that a new transaction is created for each invocation. - NONE: Indicates that the source will not start any transaction and will not participate of one that is opened in the flow. ## Message Source Transaction Types When the source supports XA transactions, a `transactionType` parameter is added. This parameter can take these values: - LOCAL: Starts a regular transaction. - XA: Starts an XA transaction instead. ## Handling Transactions In the SDK, transactions are modeled through Connections. So each transaction requires a different connection instance. This is because Message Sources need to support parallelism, and the same connection cannot serve two different transactions at the same time. This requires a model different from that of the HTTP Listener example used elsewhere in the SDK documentation. So, consider this oversimplified example that uses the `` from the VM Connector: ```java public VMListener extends Source { @Connection private ConnectionProvider sessionProvider; @Override public void onStart(SourceCallback sourceCallback) throws MuleException { while(notStopped()) { QueueSession session = sessionProvider.connect(); (1) CallbackContext ctx = callback.createContext(); (2) TransactionHandle status = ctx.bindConnection(session); (3) try { callback.handle(session.poll(), ctx); (4) } catch (Exception e) { status.rollback(); } } } @OnSuccess public void onSuccess(SourceCallbackContext context) { handleSuccess(context.getConnection()); (5) } @OnError public void onError(SourceCallbackContext context, Error error) { handleError(context, error); } } ```
1Multiple connections are obtained.
2For each connection, a new CallbackContext is created.
3Each connection is registered with the context through the bindConnection() method in the SourceCallbackContext that was just created.
4The context is passed to the handle method.
5The connection will later be available for all the onSuccess, onError, and onTerminate methods, through the SourceCallbackContext.getConnection() method.
Because the connection has been bound to the context, the runtime will automatically close the connection when needed. If the connection is transactional and the source is also configured to be transactional, then calling the `bindConnection()` method will automatically start the transaction on that connection. Also if the connection is transactional, the runtime will automatically commit the transaction after the `onSuccess()` method or roll it back after the `onError()` method. > [!TIP] Notice that in this case, the transaction needs to be started BEFORE polling any message > from the queue. It is a good practice to call `bindConnection()` immediately after the connection > has been obtained. ## Custom Transaction Handling Some connectors might need to provide custom transaction handling. For example, suppose that in the case of an error, you want to publish an error response. In that case, you need the transaction to be committed, because if it gets rolled back, the response will never reach its destination. Example: Custom Transaction Handling ```java @OnError public void onError(SourceCallbackContext context, Error error) { ctx.getConnection().publish(buildErrorResponse(error)); (1) ctx.getTransactionHandle().commit(); (2) } ```
1The connection is still in an open state when the onSuccess() and onError() methods are executed.
2The context has a TransactionHandle that allows you to manually manipulate the transaction. If you manually resolve the transaction, the runtime will respect your wishes and not do it for you later. You do not need to do this for the most common use cases.
--- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, operation, source, tx, transactions, xa --- # Transactions > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The SDK supports the creation of connectors that join and create a new transaction in a Mule Flow, including Source and Operation executions that work as a unit. So, if a problem occurs inside the flow, all the components participating in the transaction will roll back their execution as a unit. All operations within the transaction are rolled back so that no one part results in partial completion. ## Implementing Transactions in a Connector Mule transaction support works on the premise of a transactional resource identified by a key. The SDK follows this same concept of reusing the connection abstraction and using the component connection as the key of the transaction. An extension must have a `ConnectionProvider` in order to have a transaction because the `ConnectionProvider` yields instances of the `TransactionalConnection` interface. ### TransactionalConnection Interface To enable transactions in your connector, you need a Connection Provider ([`ConnectionProvider`](https://docs.mulesoft.com/mule-sdk/latest/connections)) because it provides connections that implement the `TransactionalConnection` interface. The connection must implement `TransactionalConnection` methods that are used to communicate the beginning and conclusion of a transaction. ```java public class TXConnectionProvider implements ConnectionProvider { ... } ``` ```java public class TxConnection implements TransactionalConnection { @Override public void begin() throws TransactionException { //logic to begin a transaction } @Override public void commit() throws TransactionException { //logic to commit a transaction } @Override public void rollback() throws TransactionException { //logic to rollback a transaction } } ``` Implementing this interface requires implementation of the following methods:
MethodDescription

begin()

This method is called, when the transaction must begin.

commit()

This is called when the transaction finished correctly and must be committed.

rollback()

This is called then the transaction finished with an error and this one must be rollbacked.

### Transactions in Operations To make an operation work under a transaction, the `@Connection` parameter should be a transactional-capable connection, that is, a connection that implements `TransactionalConnection`. #### Transactional Actions When an operation is transactional, a synthetic parameter named `transactionalAction` is automatically added to it. That parameter is an Enum type that can take these values:
Action NameDescription

ALWAYS_JOIN

The connector will always join the existing transaction. If there is no transaction, or if the current transaction is not compatible with the current operation, the operation will fail to execute.

JOIN_IF_POSSIBLE (Default)

If a transaction is available, use it. Otherwise, continue processing.

NOT_SUPPORTED

The operation will execute outside any ongoing transaction.

```xml ``` ##### Operation Transactional Actions Inside the Operation Code In some cases, you might want to know the operation level at which `transactionalAction` has been configured in the Mule app. You can do so by setting a parameter of `OperationTransactionalAction` type to inject the used value. ```java public void work(@Connection TxConnection connection, OperationTransactionalAction action) { // } ``` #### Operation Transaction Usage Operations cannot start transactions. They can be started by a [Message Source](https://docs.mulesoft.com/mule-sdk/latest/sources), or the operation has to be inside a `` scope that starts a new transaction. Prior to executing a transactional operation, Mule will check if the current transaction has a bound key: - If there is already a bounded key (because a previous Operation or Source has already registered), Mule will check for its compatibility with the current Operation. If it is compatible, the Operation will use the bounded connection. Otherwise, the execution flow will fail, indicating that the current Operation was trying to enter to an incompatible Transaction. - If not, it will bound the Operation’s Connection as the key and execute the `TransactionalConnection.begin()`, communicating that the transaction for the current Operation should start. At the end of the execution of the transaction scope (meaning the `` scope or the entire flow if the transaction was initialized by a [Message Source](https://docs.mulesoft.com/mule-sdk/latest/sources)), Mule will call `TransactionalConnection.commit()` if the transaction finished correctly. Otherwise, if an error occurred, it will execute `TransactionalConnection.rollback()`. When the connection joins the transaction, the provider’s connection management strategy is overridden, meaning that regardless of the connection’s strategy, the same connection will be used for all matching operations between the transaction’s scope. > [!WARNING] This behavior is not totally compatible with the concept of `CachedConnectionProvider` > because that would allow the same connection to join different transactions concurrently. This > implies that you should know how to manage different transactions simultaneously in the same > connection. The use of `CachedConnectionProviders` with Transactions is strongly discouraged. ### Transactions in Sources To make an Operation work under a transaction, the `@Connection` parameter should be a `ConnectionProvider` capable of providing transaction-compatible connections. #### Transactional Actions ##### Source Transactional Actions When a Message Source is transactional, a synthetic parameter named `transactionalAction` is automatically added to it. That parameter is an Enum type that can take these values:
Table 1. Source Transactional Actions
Action NameDescription

ALWAYS BEGIN

Ensures that a new transaction is created for each invocation.

NONE (Default)

The source will not start any transaction and will not participate in one that is open in the Flow.

##### Source Transactional Actions inside the Operation Code In some cases, you might want to know at Source level which `transactionalAction` has been configured in the Mule app. If so, you can define a parameter of `SourceTransactionalAction` type to inject the used value, for example: Injecting SourceTransactionalAction to Source ```java public class TransactionalSource extends Source { @Connection private ConnectionProvider connection; @Parameter private SourceTransactionalAction action; // rest of the code } ``` More more on this topic, [Sources Transactions](https://docs.mulesoft.com/mule-sdk/latest/sources-transactions). ### XA Transactions XA transactions are supported in a similar way. The only difference is that instead of a `TransactionalConnection`, the provider should return an `XATransactionalConnection`, which is a connection capable of returning an `XAResource`. --- product: Mule SDK version: 1.13 is-latest-version: true --- # Troubleshoot SDK Components and Operations - Mule 4 > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The following troubleshooting features are available starting from Mule SDK 1.4. To troubleshoot Java SDK, learn how to enable logging (including verbose logging) and how to interpret common error messages. ## Logging Use logging to troubleshoot the following SDK components:
Component NameLogging Capabilities

Source

Logs information about the source lifecycle, such as when the lifecycle starts and ends. In case of error and reconnection, the log provides the full stack trace associated with the raised exception. For cluster environments, the log provides source status information, which indicates whether it is the primary source or not.

Polling Source

Logs information about items accepted and rejected by the source. In the case of rejection, the log contains information about the cause of the rejection.

Polling Source Watermarking

Logs information about watermarking handling on polling sources. Because this process uses low-level logic, enable trace logging only when debugging or troubleshooting is necessary.

Pooling Connection Provider

Logs information about connection management for pooling connection providers. This log provides connection pool status information as well as connection handling, which indicates when the pool retrieves and returns connections.

## Verbose Logging Use verbose logging to troubleshoot SDK components to get information regarding their status or behavior. Because of the high impact to application performance, limit verbose logging to testing and troubleshooting operations. Enable verbose logging in Anypoint Studio, or Anypoint Runtime Manager if using CloudHub APIs. In both cases, the application’s log file logs the information. ### Enable Verbose Logging in Anypoint Studio The following steps describe how to enable verbose logging using Anypoint Studio: 1. Access Anypoint Studio and navigate to the **Package Explorer** view. 2. Open your application’s project name. 3. Open the `src/main/resources` path folder. 4. Open the `log4j2.xml` file inside the folder. ![Folder structure of the log4j2 xml file](/mulesoft/current/en-us/mule-sdk/latest/_images/enable-sdk-logging-studio.png) 5. Add an `` tag inside the `` tag: ```xml ... ... ``` Use the following table as a reference for specifying the component’s fully qualified name:
NameFully Qualified NameLog Level

Source

org.mule.runtime.module.extension.internal.runtime.source.ExtensionMessageSource

DEBUG

Polling Source

org.mule.runtime.module.extension.internal.runtime.source.poll.PollingSourceWrapper

DEBUG

Polling Source Watermarking

org.mule.runtime.module.extension.internal.runtime.source.poll.PollingSourceWrapper

TRACE

Pooling Connection Provider

org.mule.runtime.core.internal.connection.PoolingConnectionHandler org.mule.runtime.core.internal.connection.PoolingConnectionManagementStrategy

DEBUG

The following example shows how to add a fully qualified name to the `log4j2.xml` file to enable verbose logging for Source: ```xml ... ``` ### Enable Verbose Logging in Anypoint Runtime Manager The following steps describe how to enable verbose logging using Anypoint Runtime Manager: 1. Access Anypoint Runtime Manager. 2. Select the desired application. 3. Go to **Settings** on the left pane. 4. Go to the **Logging** tab. ![Logging tab in Runtime Manager](/mulesoft/current/en-us/mule-sdk/latest/_images/enable-sdk-logging-runtime-manager.png) 5. Add the component’s fully qualified name and the logging level (DEBUG or TRACE). Refer to the table that shows how to specify the component’s fully qualified name. 6. Click **Apply Changes**. ## Logging Examples The following examples show expected logs for SDK components: - [Source Logging Example](#source-logging-example) - [Polling Source Logging Example](#polling-source-logging-example) - [Polling Source Watermarking Logging Example](#polling-source-watermarking-logging-example) - [Pooling Connection Provider Logging Example](#pooling-connection-provider-logging-example) The logs in these examples are from a Mule application that consists of an FTP Connector source that lists files in an FTP server and a logger that logs the name of the file it processes. ![Mule flow in Studio that shows FTP Connector source and Logger](/mulesoft/current/en-us/mule-sdk/latest/_images/troubleshooting-app.png) ### Source Logging Example The following example shows logs for Source: ```none org.mule.runtime.module.extension.internal.runtime.source.ExtensionMessageSource: Message source 'listener' on flow 'ftp-troubleshooting-examples' threw exception. Attempting to reconnect... org.mule.runtime.module.extension.internal.runtime.source.ExtensionMessageSource: Message source 'listener' on flow 'ftp-troubleshooting-examples' successfully reconnected org.mule.runtime.module.extension.internal.runtime.source.ExtensionMessageSource: Message source 'listener' on flow 'ftp-troubleshooting-examples' could not be reconnected. Will be shutdown. org.mule.runtime.module.extension.internal.runtime.source.ExtensionMessageSource: Message source 'listener' on flow 'ftp-troubleshooting-examples' is stopping ``` ### Polling Source Logging Example The following example shows logs for Polling Source: ```none org.mule.runtime.module.extension.internal.runtime.source.poll.PollingSourceWrapper: Item with id:[/ftp-example.json] is accepted org.mule.runtime.module.extension.internal.runtime.source.poll.PollingSourceWrapper: Item with id:[/ftp-example.json] is rejected with status:[FILTERED_BY_WATERMARK] ``` ### Polling Source Watermarking Logging Example The following example shows logs for Polling Source Watermarking: ```none org.mule.runtime.module.extension.internal.runtime.source.poll.PollingSourceWrapper: A new watermark maximum has been found when processing item with id /ftp-example.json for source in flow ftp-troubleshooting-examples ``` ### Pooling Connection Provider Logging Example The following example shows logs for Pooling Connection Provider: ```none org.mule.runtime.core.internal.connection.PoolingConnectionManagementStrategy: Acquiring connection org.mule.extension.ftp.internal.connection.FtpFileSystem@1a0c776f from the pool FTP_Config-5e20b7d9-417d-4307-ab4c-9ef3e7ae4d9b org.mule.runtime.core.internal.connection.PoolingConnectionManagementStrategy: Status for pool FTP_Config-5e20b7d9-417d-4307-ab4c-9ef3e7ae4d9b: 1 connections are active out of 5 max active limit, 0 connections are idle out of 5 max idle limit ``` ## Error: Cannot Find org.mule.sdk.api Classes Java SDK throws errors such as `Cannot access XYZ class file: XYZ not found` or `Cannot find symbol: method someMethod() location: variable someVar of type XYZ`, in which XYZ is a class from the `org.mule.sdk.api` package. For example: \[ERROR\] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project : Compilation failure: cannot access org.mule.sdk.api.runtime.streaming.StreamingHelper \[ERROR\] class file for org.mule.sdk.api.runtime.streaming.StreamingHelper not found cannot find symbol \[ERROR\] symbol: method resolveCursors(java.util.HashMap,boolean) \[ERROR\] location: variable streamingHelper of type org.mule.runtime.extension.api.runtime.streaming.StreamingHelper The `org.mule.sdk.api` package is not supported when using `mule-modules-parent` version 1.4.0 or earlier, or when targeting a Minimum Mule Version of 4.4.0 or earlier. To fix this error, either: - Upgrade to `mule-java-extension-parent` version 1.11.0 or later and use [Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility) to properly configure your module’s Minimum Mule Version - If you must support older Mule versions without forward compatibility, use the equivalent classes from the `org.mule.runtime.extension.api` package instead ## See Also - [Salesforce Help](https://help.salesforce.com) - [Known Issues](https://help.salesforce.com/s/issues) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Upgrading Java for Custom Connectors (Customers) > For the full documentation index, see: https://docs.mulesoft.com/llms.txt > [!NOTE] This guide applies to MuleSoft customers only. If you are a MuleSoft partner, see > [Upgrading Java for Custom Connectors (Partners)](https://docs.mulesoft.com/mule-sdk/latest/partner-connector-upgrade). Upgrade, test, and release your custom connectors for Java 17 to ensure compatibility within the MuleSoft ecosystem. Ensuring this compatibility is crucial because running an incompatible connector can lead to runtime failures, decreased performance, and security vulnerabilities that could impact your app’s stability. ## Before You Begin Before upgrading and testing your custom connector for Java 17, you must be familiar with the following considerations: - Target compilation level In Mule 4.9.0 and later, you can compile any Mule project for Java 17 or lower. Additional dependencies must be compiled for Java 17 or lower. - Declaring the minimum Mule version Two parent POMs are available for building extensions. Each one handles the minimum Mule version declaration differently. - `mule-java-extension-parent` (recommended) This is the recommended parent POM for all new extensions and for existing extensions that can migrate. It supports forward compatibility and requires you to explicitly declare the minimum Mule version in your project, giving you full control over which Mule runtime versions your connector supports. The `mule-extensions-archetype-maven-plugin` uses this parent when generating new extension projects. For migration instructions, see [Upgrading Existing Modules](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-setup#upgrading-existing-modules). > [!NOTE] Switching parent POMs for existing connectors may not be straightforward depending on > the connector’s complexity. If you must upgrade with minimal changes, you can continue to use > `mule-modules-parent` as described below. - `mule-modules-parent` (deprecated) This parent POM is deprecated and remains available for existing connectors where switching to `mule-java-extension-parent` is not practical. If you compile for Java 17, upgrade `mule-modules-parent` to version 1.9.0 or later. The connector’s minimum Mule version is automatically set based on the parent version. For example, version 1.9.0 sets `minMuleVersion` to 4.9.0, ensuring compatibility only with Mule 4.9.0 or later. If the `mule-modules-parent` version is earlier than 1.9.0, the bytecode level for the connector and its additional dependencies must be for Java 8. You can’t use language features from Java 11 or Java 17. Verify that `minMuleVersion` in `META-INF/mule-artifact/mule-artifact.json` reflects the correct version. See [Specify Java Compatibility](https://docs.mulesoft.com/mule-sdk/latest/java-version-support#specify-java-compatibility). - Unsupported directives Don’t set `--add-opens`, `--add-exports`, or `--add-modules` directives. These directives are not allowed in CloudHub, Runtime Fabric, or on-premises. These directives also require managing them for each connector across all environments, and introduce a backward compatibility burden, complicating future Java upgrades and potentially enabling security vulnerabilities. If these directives are used, they are not covered under Support. - Minimum Mule version If your custom connector is running on Mule 4.5.x or earlier, you can’t run Java 17 tests that extend the `MuleArtifactFunctionalTestCase`, but you can still run Java 8 tests. To decouple the minimum Mule version when running tests, add the `-Dmunit.jvm` parameter to use Java 17 as described in [Test Your Custom Connector with MUnit](#test-your-custom-connector-with-munit). ## Upgrade Your Custom Mule SDK Connectors Upgrade your custom Mule SDK (Java SDK and XML SDK) connectors to Java 17. ### Upgrade Third-Party Libraries Your custom connector likely relies on third-party libraries, so it’s important to ensure they are compatible with Java 17 as well. Even if your custom connector passes tests, it is still important to deliver secure and reliable software. If a critical bug or vulnerability is found, you must upgrade to a fixed version. Using up-to-date libraries that explicitly support Java 17 makes this process easier and more reliable, as the vendor has already tested them on Java 17. Upgrade the third-party libraries of your custom connector to be compatible with Java 17: - Java EE libraries Mule 4.6.x exposes Java EE libraries differently than earlier Mule versions. You must add the BOM dependency to your custom connector and, if applicable, exclude the provided conflicting library. You can also add the libraries in the BOM dependency separately. ```java 4.6.0 org.mule mule-javaee-runtime-bom ${muleJavaEeBomVersion} pom import ``` For more information, see [Java EE Libraries](https://docs.mulesoft.com/mule-sdk/latest/java-ee-libraries). - External libraries Upgrade external libraries, which are third-party libraries that are not bundled with your custom connector. Some examples include a JDBC driver, JMS broker client, or Groovy runtime. The following example shows a JDBC driver external library configuration: ```java @ExternalLib(name = "MySQL JDBC Driver", description = "A JDBC driver that supports connection to the MySQL Database", nameRegexpMatcher = "(.*)\\.jar", requiredClassName = "com.mysql.jdbc.Driver", coordinates = "mysql:mysql-connector-java:5.1.44") public class MySqlConnectionProvider implements ConnectionProvider { // } ``` You must update your external libraries to a version that is compatible with Java 17. ### Review Your Code After you compile your custom connector, ensure you can run your custom connector with Java 17. Reviewing your code is important because some restrictions are flagged as warnings in Java 11 but are flagged as failures and can’t be disabled in Java 17. For example, reflection is more restricted in Java 17, so you must review your code to ensure there are no illegal actions. To help you review your code, learn more about the different changes in Java 17: - JDK Migration Guide See the [Oracle JDK Migration Guide](https://docs.oracle.com/en/java/javase/17/migrate/getting-started.html#GUID-C25E2B1D-6C24-4403-8540-CFEA875B994A) for a full list of the changes made from Java 8 to Java 17. Use the guide to assess the impact to your code. - Mule runtime engine See [Mule runtime](https://docs.mulesoft.com/general/java-support#mule-runtime) to learn more about the Java 17 changes made in Mule runtime engine. - Reflection on Java classes To ensure compatibility with Java 17, review and update any code that uses reflection to access Java classes or set parameter values. Java 17 imposes stricter access controls, so avoid introspecting JDK internals. Have a robust test suite that runs with Java 17. Pay special attention to the `setAccessible(true)` method because these access changes are not allowed in Java 17. - Serialization Update your serialization code to accommodate the restrictions of Java 17. If your code uses libraries like Gson for serializing classes, switch to using Data Transfer Objects (DTOs) where reflective access to JDK classes is involved. Ensure that serialization libraries are updated and adjust your code to minimize reliance on reflection. For more information, refer to [Configure Custom Serializers](https://docs.mulesoft.com/mule-runtime/latest/configure-custom-serializers). - Library upgrades Upgrade your libraries to versions compatible with Java 17. This includes updating ByteBuddy to version 1.14.0, Jacoco to 0.8.10, and SLF4J to version 2.x. Replace CGLib with ByteBuddy and check for updates to other libraries like Groovy and JRuby. Verify that all dependencies are compatible with Java 17 to avoid runtime issues. - Changes with running the test suite Adapt your testing approach for Java 17. If you use Mockito, be aware that Mockito can’t mock JVM classes. Consider custom implementations for mocking and switch from PowerMock to newer Mockito versions. Update your tests to handle the changes in Java 17 and avoid deprecated methods. - Java Platform Module System (JPMS) JPMS was introduced in Java 9. This feature introduces stricter encapsulation, impacting how Mule modules interact with JDK classes. As Mule upgrades to Java 17 and JPMS, these changes are necessary: - Refactor Split Packages: Resolve issues with internal and API package splits by reorganizing and refactoring packages to conform with JPMS modularization standards. - Address `ResourceBundle` Loading: Implement solutions for issues related to resource bundle loading, such as using `ResourceBundleProvider` or alternative methods. - Analyze the third-party libraries' changelogs to understand Java 17 compatibility and changes related to JPMS. ### Add Missing Code Because reflection is more restricted in Java 17, API objects now require setters. Previously with Java 8, it was enough for API objects to only have default constructors and getters for all DataWeave accessible properties. Now, API objects must also have setters so DataWeave can write those properties without using reflection. Constructors and setters are required if your class is instantiated by DataWeave, and getters are required if your class is read. If your class is returned and not instantiated, only getters are required. However, using both getters and setters simplifies the validation and certification process. ## Upgrade Your Custom Configuration Properties Providers To upgrade your custom configuration properties provider to be compatible with Java 17, switch to use [Java SDK](https://docs.mulesoft.com/mule-sdk/latest/getting-started) and use the `@JavaVersionSupport` annotation as explained in [Release Your Custom Connector](https://docs.mulesoft.com/general/customer-connector-upgrade#release-your-custom-connector). You must also perform all the steps described in [Upgrading Java for Custom Connectors (Customers)](https://docs.mulesoft.com/general/customer-connector-upgrade), just like with any custom connector. The example custom configuration properties provider mentioned in [Example: Mule SDK Module](https://docs.mulesoft.com/mule-runtime/latest/custom-configuration-properties-provider#example-mule-sdk-module) is updated to support Java 17. Refer to that example to update your custom configuration properties provider. For more details about what the changed code looks like (including migrating tests to MUnit), refer to this [changeset](https://github.com/mulesoft/mule-custom-properties-providers-module-example/commit/410ee26d8b3c44adacb077e099f32886f99eb6be). Alternatively, if switching to using [Java SDK](https://docs.mulesoft.com/mule-sdk/latest/getting-started) involves too many changes for you, add a declaration using `ExtensionDeclarer` inside `ExtensionLoadingDelegate` in your custom configuration properties provider. For more details about what the changed code looks like, refer to this [changeset](https://github.com/mulesoft/mule-custom-properties-providers-module-example/commit/19d9d45cd52b0695f7b9c2b9019bae88f45fb228). ## Test Your Custom Connector with MUnit Run your MUnit tests to test compatibility of your connector against Java 17. Ensure your local JDK version is 17. 1. Open the pom.xml file of your Mule app. 2. Replace the `mule-maven-plugin` version with the `${mule.maven.plugin.version}` parameter. 3. If you haven’t already, add the `munit-maven-plugin`. Replace the version with the `${munit.version}` parameter and replace `runtimeVersion` with the `${app.runtime}` parameter. ```xml com.mulesoft.munit.tools munit-maven-plugin ${munit.version} test test test coverage-report true html ${app.runtime} http.port ``` > [!NOTE] MUnit 3.1 is compatible only with Mule runtime 4.3.0 and later. If your connector is > compatible with Mule runtime 4.2.0 and earlier, you must create a legacy profile that > overrides the MUnit version. 4. If you have MUnit dependencies, such as `munit-runner` and `munit-tools`, replace the version for each dependency with the `${munit-version}` parameter. 5. Replace the version for each connector dependency with the Java 17 compatible version of the connector. 6. Open a terminal window in the root of your Mule project and run the following command. This command displays the earliest versions of Mule runtime, MUnit, and the Mule Maven plugin that are compatible with Java 17. ```bash mvn -f pom.xml -s ~/.m2/settings.xml -Dapp.runtime=4.6.0 -Dmunit.version=3.1.0 -Dmule.maven.plugin.version=4.1.0 -fae test ``` You can now see if your connector is compatible with Java 17. For more information about running MUnit tests, refer to [MUnit](https://docs.mulesoft.com/munit/latest/). If you use `mule-java-extension-parent` (recommended), the minimum Mule version is independent of the parent version and you control it directly in your project. For more information, see [Upgrading Existing Modules](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-setup#upgrading-existing-modules). If you use the legacy `mule-modules-parent`, the Mule runtime version determines the parent version. For example, Mule runtime 4.6.0 corresponds to `mule-modules-parent` 1.6.0. Minor versions maintain a correspondence, such as Mule runtime 4.1.0 with `mule-modules-parent` 1.1.0, Mule runtime 4.2.0 with `mule-modules-parent` 1.2.0, and so forth. Mule runtime 4.6.0 and later supports Java 17. However, a connector can be compatible with both Mule 4.3.0 and Java 17 simultaneously. If your connector must be compatible with Mule 4.3.0, its `mule-modules-parent` version cannot exceed 1.3.0. You don’t need to use `mule-modules-parent` 1.6.0 for your connector to be compatible with Java 17. Using `mule-modules-parent` 1.6.0 is specifically required to leverage other features from the Mule runtime 4.6.0 in the connector. ## Release Your Custom Connector After you update your code and your tests are green, you are ready to release a new Java 17-compatible version of your custom connector. 1. To communicate Java 17 compatibility, generate metadata for Java compatibility of your custom connector by adding or upgrading the custom connector `mule-sdk-api` dependency to the latest version: ```xml org.mule.sdk mule-sdk-api 0.10.1 ``` 2. For Java SDK, add the `@JavaVersionSupport` annotation in the same class as the `@Extension` annotation and include the `JAVA_17` value, for example: > [!NOTE] You don’t need to add any annotations for XML SDK because XML SDK modules are Java 17 > compatible and inherit the property automatically. ```java @Extension(name = "Database") @Operations(...) @JavaVersionSupport({JAVA_8, JAVA_11, JAVA_17}) public class DatabaseConnector { .. } ``` In Mule 4.5.0 and later, custom connectors that don’t specify the `@JavaVersionSupport` annotation are assumed to be compatible with Java 8 and Java 11. You can mark your custom connector as compatible with Java 17 only; however, you must ensure that no adoption or backward compatibility issues exist. When you deploy a Mule app, Mule verifies that all modules in the Mule app are compatible with the Java version. If Mule finds an incompatibility, Mule throws an error and the application does not deploy. > [!NOTE] If you receive an error message specific to an XML SDK based connector, such as > `Extension 'module-error-handler-plugin' does not support Java 17. Supported versions are: [1.8, 11]`, > this means that your Mule app still contains some connectors that are not compatible with Java 17. > To resolve this error, upgrade all connectors in your Mule app to be compatible with Java 17. If your code is compatible with Java 17 but you don’t declare Java 17 compatibility, you can still get a successful test run. To run a quick check on your custom connector or if all dependencies are not ready, pass the following argument to skip hard checks on the Java support declaration: ```bash -M-Dmule.jvm.version.extension.enforcement=LOOSE ``` For more information, see [Java Version Support](https://docs.mulesoft.com/mule-sdk/latest/java-version-support). ## See Also - [Java Support](https://docs.mulesoft.com/general/java-support) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Upgrading Java for Custom Connectors (Partners) > For the full documentation index, see: https://docs.mulesoft.com/llms.txt > [!NOTE] This guide applies to MuleSoft partners only. If you are a MuleSoft customer, see > [Upgrading Java for Custom Connectors (Customers)](https://docs.mulesoft.com/mule-sdk/latest/customer-connector-upgrade). Upgrade, test, and release your custom connectors for Java 17 to ensure compatibility within the MuleSoft ecosystem. Ensuring this compatibility is crucial because running an incompatible connector can lead to runtime failures, decreased performance, and security vulnerabilities that could impact your app’s stability. ## Before You Begin Before upgrading and testing your custom connector for Java 17, you must be familiar with the following considerations: - Target compilation level In Mule 4.9.0 and later, you can compile any Mule project for Java 17 or lower. Additional dependencies must be compiled for Java 17 or lower. - Declaring the minimum Mule version Two parent POMs are available for building extensions. Each one handles the minimum Mule version declaration differently. - `mule-java-extension-parent` (recommended) This is the recommended parent POM for all new extensions and for existing extensions that can migrate. It supports forward compatibility and requires you to explicitly declare the minimum Mule version in your project, giving you full control over which Mule runtime versions your connector supports. The `mule-extensions-archetype-maven-plugin` uses this parent when generating new extension projects. For migration instructions, see [Upgrading Existing Modules](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-setup#upgrading-existing-modules). > [!NOTE] Switching parent POMs for existing connectors may not be straightforward depending on > the connector’s complexity. If you must upgrade with minimal changes, you can continue to use > `mule-modules-parent` as described below. - `mule-modules-parent` (deprecated) This parent POM is deprecated and remains available for existing connectors where switching to `mule-java-extension-parent` is not practical. If you compile for Java 17, upgrade `mule-modules-parent` to version 1.9.0 or later. The connector’s minimum Mule version is automatically set based on the parent version. For example, version 1.9.0 sets `minMuleVersion` to 4.9.0, ensuring compatibility only with Mule 4.9.0 or later. If the `mule-modules-parent` version is earlier than 1.9.0, the bytecode level for the connector and its additional dependencies must be for Java 8. You can’t use language features from Java 11 or Java 17. Verify that `minMuleVersion` in `META-INF/mule-artifact/mule-artifact.json` reflects the correct version. See [Specify Java Compatibility](https://docs.mulesoft.com/mule-sdk/latest/java-version-support#specify-java-compatibility). - Unsupported directives Don’t set `--add-opens`, `--add-exports`, or `--add-modules` directives. These directives are not allowed in CloudHub, Runtime Fabric, or on-premises. These directives also require managing them for each connector across all environments, and introduce a backward compatibility burden, complicating future Java upgrades and potentially enabling security vulnerabilities. If these directives are used, they are not covered under Support. - Minimum Mule version If your custom connector is running on Mule 4.5.x or earlier, you can’t run Java 17 tests that extend the `MuleArtifactFunctionalTestCase`, but you can still run Java 8 tests. To decouple the minimum Mule version when running tests, add the `-Dmunit.jvm` parameter to use Java 17 as described in [Test for Java 17 Compatibility Directly](#test-for-java-17-compatibility-directly). ## Upgrade Your Custom Mule SDK Connectors Upgrade your custom Mule SDK (Java SDK and XML SDK) connectors to Java 17. ### Upgrade Third-Party Libraries Your custom connector likely relies on third-party libraries, so it’s important to ensure they are compatible with Java 17 as well. Even if your custom connector passes tests, it is still important to deliver secure and reliable software. If a critical bug or vulnerability is found, you must upgrade to a fixed version. Using up-to-date libraries that explicitly support Java 17 makes this process easier and more reliable, as the vendor has already tested them on Java 17. Upgrade the third-party libraries of your custom connector to be compatible with Java 17: - Java EE libraries Mule 4.6.x exposes Java EE libraries differently than earlier Mule versions. You must add the BOM dependency to your custom connector and, if applicable, exclude the provided conflicting library. You can also add the libraries in the BOM dependency separately. ```java 4.6.0 org.mule mule-javaee-runtime-bom ${muleJavaEeBomVersion} pom import ``` For more information, see [Java EE Libraries](https://docs.mulesoft.com/mule-sdk/latest/java-ee-libraries). - External libraries Upgrade external libraries, which are third-party libraries that are not bundled with your custom connector. Some examples include a JDBC driver, JMS broker client, or Groovy runtime. The following example shows a JDBC driver external library configuration: ```java @ExternalLib(name = "MySQL JDBC Driver", description = "A JDBC driver that supports connection to the MySQL Database", nameRegexpMatcher = "(.*)\\.jar", requiredClassName = "com.mysql.jdbc.Driver", coordinates = "mysql:mysql-connector-java:5.1.44") public class MySqlConnectionProvider implements ConnectionProvider { // } ``` You must update your external libraries to a version that is compatible with Java 17. ### Review Your Code After you compile your custom connector, ensure you can run your custom connector with Java 17. Reviewing your code is important because some restrictions are flagged as warnings in Java 11 but are flagged as failures and can’t be disabled in Java 17. For example, reflection is more restricted in Java 17, so you must review your code to ensure there are no illegal actions. To help you review your code, learn more about the different changes in Java 17: - JDK Migration Guide See the [Oracle JDK Migration Guide](https://docs.oracle.com/en/java/javase/17/migrate/getting-started.html#GUID-C25E2B1D-6C24-4403-8540-CFEA875B994A) for a full list of the changes made from Java 8 to Java 17. Use the guide to assess the impact to your code. - Mule runtime engine See [Mule runtime](https://docs.mulesoft.com/general/java-support#mule-runtime) to learn more about the Java 17 changes made in Mule runtime engine. - Reflection on Java classes To ensure compatibility with Java 17, review and update any code that uses reflection to access Java classes or set parameter values. Java 17 imposes stricter access controls, so avoid introspecting JDK internals. Have a robust test suite that runs with Java 17. Pay special attention to the `setAccessible(true)` method because these access changes are not allowed in Java 17. - Serialization Update your serialization code to accommodate the restrictions of Java 17. If your code uses libraries like Gson for serializing classes, switch to using Data Transfer Objects (DTOs) where reflective access to JDK classes is involved. Ensure that serialization libraries are updated and adjust your code to minimize reliance on reflection. For more information, refer to [Configure Custom Serializers](https://docs.mulesoft.com/mule-runtime/latest/configure-custom-serializers). - Library upgrades Upgrade your libraries to versions compatible with Java 17. This includes updating ByteBuddy to version 1.14.0, Jacoco to 0.8.10, and SLF4J to version 2.x. Replace CGLib with ByteBuddy and check for updates to other libraries like Groovy and JRuby. Verify that all dependencies are compatible with Java 17 to avoid runtime issues. - Changes with running the test suite Adapt your testing approach for Java 17. If you use Mockito, be aware that Mockito can’t mock JVM classes. Consider custom implementations for mocking and switch from PowerMock to newer Mockito versions. Update your tests to handle the changes in Java 17 and avoid deprecated methods. - Java Platform Module System (JPMS) JPMS was introduced in Java 9. This feature introduces stricter encapsulation, impacting how Mule modules interact with JDK classes. As Mule upgrades to Java 17 and JPMS, these changes are necessary: - Refactor Split Packages: Resolve issues with internal and API package splits by reorganizing and refactoring packages to conform with JPMS modularization standards. - Address `ResourceBundle` Loading: Implement solutions for issues related to resource bundle loading, such as using `ResourceBundleProvider` or alternative methods. - Analyze the third-party libraries' changelogs to understand Java 17 compatibility and changes related to JPMS. ### Add Missing Code Because reflection is more restricted in Java 17, API objects now require setters. Previously with Java 8, it was enough for API objects to only have default constructors and getters for all DataWeave accessible properties. Now, API objects must also have setters so DataWeave can write those properties without using reflection. Constructors and setters are required if your class is instantiated by DataWeave, and getters are required if your class is read. If your class is returned and not instantiated, only getters are required. However, using both getters and setters simplifies the validation and certification process. ## Upgrade Your Custom Configuration Properties Providers To upgrade your custom configuration properties provider to be compatible with Java 17, switch to use [Java SDK](https://docs.mulesoft.com/mule-sdk/latest/getting-started) and use the `@JavaVersionSupport` annotation as explained in [Release Your Custom Connector](https://docs.mulesoft.com/general/partner-connector-upgrade#release-your-custom-connector). You must also perform all the steps described in [Upgrading Java for Custom Connectors (Partners)](https://docs.mulesoft.com/general/partner-connector-upgrade), just like with any custom connector. The example custom configuration properties provider mentioned in [Example: Mule SDK Module](https://docs.mulesoft.com/mule-runtime/latest/custom-configuration-properties-provider#example-mule-sdk-module) is updated to support Java 17. Refer to that example to update your custom configuration properties provider. For more details about what the changed code looks like (including migrating tests to MUnit), refer to this [changeset](https://github.com/mulesoft/mule-custom-properties-providers-module-example/commit/410ee26d8b3c44adacb077e099f32886f99eb6be). Alternatively, if switching to using [Java SDK](https://docs.mulesoft.com/mule-sdk/latest/getting-started) involves too many changes for you, add a declaration using `ExtensionDeclarer` inside `ExtensionLoadingDelegate` in your custom configuration properties provider. For more details about what the changed code looks like, refer to this [changeset](https://github.com/mulesoft/mule-custom-properties-providers-module-example/commit/19d9d45cd52b0695f7b9c2b9019bae88f45fb228). ## Test Your Custom Connector with MTF Test your custom connector with Module Testing Framework (MTF) to ensure Java 17 compatibility. For more information about MTF, see [MTF](https://beta.docs.mulesoft.com/beta-mtf/mule-sdk/1.1/mtf). ### Set Up Your Build Ensure your pipeline runs against all supported Java versions (Java 8, Java 11, and Java 17). The following example shows a single build pipeline that is configured to run tests against all supported Java versions, in which `default` corresponds to Java 17: ![Example of single build pipeline](/mulesoft/current/en-us/general/_images/single-build-pipeline.png) The pipeline runs all tests even if the previous tests fail. For example, the pipeline runs Java 17 tests even if the Java 11 tests fail. Although the pipeline contains multiple tests, the pipeline has one compilation phase and one release phase, which targets Java 8. ### Run an Initial Test Run an initial test to test your custom connector for Java 17 compatibility. You can continue to run tests as you change the custom connector code: 1. In the `pom.xml` file of your custom connector, update the munit-extensions-maven-plugin configuration to include the following configuration (the `jacoco.version` property must be 0.8.10 or later): ```xml -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco.version}/org.jacoco.agent-${jacoco.version}-runtime.jar=destfile=${session.executionRootDirectory}/target/jacoco-munit.exec ``` 2. Run your MTF test to generate the coverage report. Use the `-Dtest=none` and `-DfailIfNoTests=false` flags when running the test to avoid including the JUnit tests in the coverage report. The coverage report is available in `target/jacoco-munit.exec`. ### View your Coverage Report View your coverage report to see your custom connector coverage. You must have at least 80% coverage for a high certainty of Java 17 compatibility. 1. Open IntelliJ IDEA. 2. Go to **Run** > **Show Coverage Data**. 3. In **Choose Coverage Suite to Display**, add `jacoco-munit.exec` to the list if it’s not there already. 4. Look at the coverage percentages to analyze your results. The coverage percentage reflects how much of your connector is tested. A key issue to avoid is illegal reflective access in your code. The only ways to detect this are to [Review Your Code](#review-your-code) or through testing. Aiming for at least 80% coverage allows flexibility on simpler code, such as getters and setters, while ensuring thorough testing of all critical business logic. ### Add the JDeps Maven Plugin JDeps is a tool for static code analysis that detects the usage of JDK internal APIs that are no longer available or accessible. For more information, refer to the [OpenJDK wiki](https://wiki.openjdk.org/spaces/JDK8/pages/17957128/Java+Dependency+Analysis+Tool). Add the JDeps Maven plugin to your custom connector’s `pom.xml` file: ```xml org.apache.maven.plugins maven-jdeps-plugin 3.1.2 jdkinternals test-jdkinternals true ``` If your custom connector is using a JDK internal API that is no longer available or accessible, the build fails. Run the build with both Java 8 and 11 by changing the `JAVA_HOME` value, as the JDeps Maven plugin relies on the JDeps tool bundled with the JDK. Running the build with each Java version ensures extra confidence. To run the JDeps Maven plugin without tests, use the following command: ```bash mvn clean install -Dtest=none -DfailIfNoTests=false -DskipTests=true ``` This helps focus on checking the offending libraries and internal access issues without dealing with potential test failures or long test runtimes. ### Test for Java 17 Compatibility You can test for Java 17 compatibility running on either Java 11 or Java 17. If you are running on Java 11, you can perform early validations by adding a parameter for illegal reflective access. See [Add a Parameter for Illegal Reflective Access](#add-a-parameter-for-illegal-reflective-access). If you are running on Java 17, you can test for Java 17 directly. See [Test for Java 17 Compatibility Directly](#test-for-java-17-compatibility-directly). #### Add a Parameter for Illegal Reflective Access Reflective access is one of the breaking changes of Java 17. If you run your MTF tests with the default Java 11 behavior, the MTF tests log only a warning for reflective access. To resemble Java 17 behavior, run your MTF tests with the `--illegal-access=deny` JVM parameter so the MTF tests fail instead of logging only a warning. Use this parameter in Mule runtime versions 4.2.0 and later. To set up your custom connector’s `pom.xml` file to include the configuration: 1. Add an empty property: ```xml ``` 2. Update the munit-extensions-maven-plugin configuration to include the following configuration: ```xml <_JAVA_OPTIONS>-XX:+PrintCommandLineFlags ${mtf.javaopts} ``` You can now run your MTF tests with the `--illegal-access=deny` parameter. Here is an example bash script (replace with the latest Mule runtime version available): ```bash #!/bin/bash RUNTIME_VERSION=4.6.0 MUNIT_JVM=/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home/bin/java mvn clean mkdir target mvn verify \ -DruntimeProduct=MULE_EE \ -DruntimeVersion=$RUNTIME_VERSION \ -Dmunit.jvm=$MUNIT_JVM \ -Dmtf.javaopts="--illegal-access=deny" > ./target/test.log ``` After running your MTF tests, go to the `target/illegal-access.log` file and check for classes or dependencies that misbehave. You can also use the following command to exclude the known warnings outside of your custom connector: ```bash cat target/illegal-access.log | sort | uniq | grep -Ev "org.mule.module.artifact|org.mule.metadata|org.mule.runtime|org.mule.service" ``` #### Test for Java 17 Compatibility Directly Run your MTF tests to test compatibility of your custom connector against Java 17. As mentioned previously, you can use a single build pipeline that runs against all supported Java versions. You can also set up another temporary build pipeline for Java 17 so your main build pipeline doesn’t become unstable. After you upgrade to Java 17, discard the temporary build pipeline and converge on your main build pipeline. 1. Set the path to your JVM installation in the `MUNIT_JVM` variable (you must install it yourself). You must also set `JAVA_HOME` to Java 8. 2. Ensure the following MTF dependencies are set in your custom connector `pom.xml` file: - munit 3.1.0 - munit-extensions-maven-plugin 1.2.0 - mtf-tools 1.2.0 - mule-maven-plugin 4.1.0 - mule-extensions-maven-plugin 1.6.0-rc1 These MTF dependencies require a minimum Mule version of 4.3.0. To ensure your MTF tests don’t validate against Mule runtime versions earlier than 4.3.0, add the following to the `munit-plugin` configuration in your custom connector `pom.xml` file: ```xml [...] ${minVersion} false EE ``` You can run MTF tests against Java 17 only with Mule runtime 4.6.0 and later. For Mule runtime 4.5.x and earlier, you can run MTF tests only against Java 8 and Java 11. MUnit 3.1 is compatible only with Mule runtime 4.3.0 and later. If your connector is compatible with Mule runtime 4.2.0 and earlier, you must create a legacy profile that overrides the MUnit version. Use the following bash script to test your custom connector against Java 17: ```bash #!/bin/bash RUNTIME_VERSION=4.6.0 MUNIT_JVM=/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home/bin/java mvn clean mkdir target mvn verify \ -DruntimeProduct=MULE_EE \ -DruntimeVersion=$RUNTIME_VERSION \ -Dmunit.jvm=$MUNIT_JVM \ -Dmule.module.tweaking.validation.skip=true \ -Dmule.jvm.version.extension.enforcement=LOOSE > ./target/test.log ``` If you use `mule-java-extension-parent` (recommended), the minimum Mule version is independent of the parent version and you control it directly in your project. For more information, see [Upgrading Existing Modules](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-setup#upgrading-existing-modules). If you use the legacy `mule-modules-parent`, the Mule runtime version determines the parent version. For example, Mule runtime 4.6.0 corresponds to `mule-modules-parent` 1.6.0. Minor versions maintain a correspondence, such as Mule runtime 4.1.0 with `mule-modules-parent` 1.1.0, Mule runtime 4.2.0 with `mule-modules-parent` 1.2.0, and so forth. Mule runtime 4.6.0 and later supports Java 17. However, a connector can be compatible with both Mule 4.3.0 and Java 17 simultaneously. If your connector must be compatible with Mule 4.3.0, its `mule-modules-parent` version cannot exceed 1.3.0. You don’t need to use `mule-modules-parent` 1.6.0 for your connector to be compatible with Java 17. Using `mule-modules-parent` 1.6.0 is specifically required to leverage other features from the Mule runtime 4.6.0 in the connector. ### Read Your Tests After you run your MTF tests, your build has either of the following outcomes: - Test failures You probably need to change your custom connector code to ensure Java 17 compatibility. - All tests pass Either your custom connector does not require any major changes or your test suite is not comprehensive enough. Review your test suite and double-check that your code coverage is good and that your test scenarios and assertions are not too simple. ## Release Your Custom Connector After you update your code and your tests are green, you are ready to release a new Java 17-compatible version of your custom connector. 1. To communicate Java 17 compatibility, generate metadata for Java compatibility of your custom connector by adding or upgrading the custom connector `mule-sdk-api` dependency to the latest version: ```xml org.mule.sdk mule-sdk-api 0.10.1 ``` 2. For Java SDK, add the `@JavaVersionSupport` annotation in the same class as the `@Extension` annotation and include the `JAVA_17` value, for example: > [!NOTE] You don’t need to add any annotations for XML SDK because XML SDK modules are Java 17 > compatible and inherit the property automatically. ```java @Extension(name = "Database") @Operations(...) @JavaVersionSupport({JAVA_8, JAVA_11, JAVA_17}) public class DatabaseConnector { .. } ``` In Mule 4.5.0 and later, custom connectors that don’t specify the `@JavaVersionSupport` annotation are assumed to be compatible with Java 8 and Java 11. You can mark your custom connector as compatible with Java 17 only; however, you must ensure that no adoption or backward compatibility issues exist. When you deploy a Mule app, Mule verifies that all modules in the Mule app are compatible with the Java version. If Mule finds an incompatibility, Mule throws an error and the application does not deploy. > [!NOTE] If you receive an error message specific to an XML SDK based connector, such as > `Extension 'module-error-handler-plugin' does not support Java 17. Supported versions are: [1.8, 11]`, > this means that your Mule app still contains some connectors that are not compatible with Java 17. > To resolve this error, upgrade all connectors in your Mule app to be compatible with Java 17. If your code is compatible with Java 17 but you don’t declare Java 17 compatibility, you can still get a successful test run. To run a quick check on your custom connector or if all dependencies are not ready, pass the following argument to skip hard checks on the Java support declaration: ```bash -M-Dmule.jvm.version.extension.enforcement=LOOSE ``` For more information, see [Java Version Support](https://docs.mulesoft.com/mule-sdk/latest/java-version-support). ## See Also - [Java Support](https://docs.mulesoft.com/general/java-support) --- product: Mule SDK version: 1.13 is-latest-version: true keywords: parameter, parameters, mule, sdk, dsl, xml, subtype, type --- # Using Generic Parameters For Extended Behavior > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Type definitions in the Module API affect the experience provided to the user. The right construction of types can greatly simplify the API that you expose to the user. ## Simplifying the UX with SubTypeMapping If a parameter represents a generic component such as `authentication` or `destination`, multiple valid sets of configurations are allowed. Your authentication can be of different types (`basic`, `digest`, `ntlm`, `oauth`) but it is a terrible experience for the user to have all the parameters of each authentication available all at once. For example, if we want an authentication that supports `basic`, `digest` and `ntlm` in a single parameter, our Authentication class would be short, but also confusing: ```java public class Authentication { @Parameter private boolean useDigest; (1) @Parameter private String username; @Parameter @Password private String password; @Parameter @Optional private String domain; (2) @Parameter @Optional private String workstation; } ```
1The parameter useDigest only exists to separate basic and digest
2domain and workstation are only relevant for an ntlm configuration, making useDigest irrelevant.
It’s easy to get lost in the correct configuration even when you need only four parameters. Instead, if you use `@SubTypeMapping`, it’s easy to understand how to use the types from the code itself, and declaring values is more concise: ```java @Extension(name = "HTTP") @SubTypeMapping(baseType = Authentication.class, (1) subTypes = {BasicAuthentication.class, DigestAuthentication.class, NtlmAuthentication.class}) public class HttpConnector { } public interface Authentication { (2) void authenticate(HttpRequestBuilder builder); } public class BasicAuthentication implements Authentication { (3) @Parameter private String username; @Parameter @Password private String password; } public class DigestAuthentication implements Authentication { (4) @Parameter private String username; @Parameter @Password private String password; } public class NtlmAuthentication implements Authentication { (5) @Parameter private String username; @Parameter @Password private String password; @Parameter @Optional private String domain; @Parameter @Optional private String workstation; } ```
1Declaration of the SubTypeMapping
2Generic Authentication interface
3Implementation of Authentication as basic
4Implementation of Authentication as digest
5Implementation of Authentication as ntlm
Next, declare a parameter of the generic type `Authentication`: ```java @Alias("request") public class HttpRequesterProvider implements CachedConnectionProvider { @Parameter private Authentication authentication; } ``` Now the user is able to declare each of the three possible values in a more concise and coherent way: ```xml > ``` ## Extending behavior With Modules Contribution If you want a module to provide its own method of authentication, use a combination of `@Import` and `@SubTypeMapping`. In this example, we’re adding an `oauth` authentication to the `http` module: ```java @Extension(name = "OAuth") @Import(type = HttpRequestAuthentication.class) (1) @SubTypeMapping(baseType = HttpRequestAuthentication.class, (2) subTypes = {DefaultAuthorizationCodeGrantType.class, ClientCredentialsGrantType.class}) public class OAuthExtension { } ```
1Declare the import from the HTTP Authentication type.
2Add more subtype mappings to the HttpRequestAuthentication type from the HTTP extension.
Now, once the two new authentication methods are implemented, we can parameterize them to the HTTP connector in any application, without modifying any code of the original extension `HTTP`. That is, for the same application we had above, we can add a new authentication method: ```xml (1) ```
1The authentication element is the same, but it contains a new element, authorization-code-grant-type
2The element authorization-code-grant-type is from the oauth namespace
--- product: Mule SDK version: 1.13 is-latest-version: true keywords: validation, validators, mule, sdk --- # Validators with Mule SDK > For the full documentation index, see: https://docs.mulesoft.com/llms.txt Validators are operations validate the Mule Message without changing the message. Validators can produce these effects: - If the condition the validator imposes is fulfilled, the flow continues, and the Mule Message remains unchanged. - If the condition the validator imposes is not fulfilled, an error is thrown. ## What are the Requirements for a Validator? To be a validator, an operation should meet these requirements: - It should be reached by the `@Validator` annotation. This can be assigned at the class level or at the method level. When you use the `@Validator` annotation at a class level, all public methods are marked as validators. - It should return `void`. - It should throw at least one error that is (or extends from) `MULE:VALIDATION`. Upon a validation failure, validators must throw a Mule validation error or a child of one. Methods with the `@Validator` annotation belong to the Stereotype `ValidatorStereotype`. Components cannot have two stereotypes, so you cannot assign more than one stereotype to a method. Validators share the same UX as operations. Once your module is added for use in Studio, the validators will appear in the palette. When a validator is added to a flow, its parameters are assigned just as if it were an operation. ## How to Make a Validator This example shows how to make a simple validator. It creates a simple extension named `balance`: ```java @Operations({BalanceOperations.class}) @Extension(name = "balance") @ErrorTypes(BalanceError.class) public class BalanceExtension { } ``` The `BalanceOperations` class below adds a validation method that is annotated with `@Validator`. The method throws an error type that comes from the generic validation error type. Note that you can also annotate the class `BalanceOperations` with `@Validator`, but if you do, any other operations in it will be flagged as validators. ```java public class BalanceOperations { @Validator @Throws(BalanceErrorsProvider.class) public void hasSufficientFunds(Integer balance, Integer price) throws Exception { if (price > balance){ throw new ModuleException(BalanceError.INSUFFICIENT_FUNDS, new IllegalArgumentException("There is not enough money to make the transaction")); } } } ``` This example creates the error to throw if the validation fails. Notice that it is named according to the validations failure. ```java public enum BalanceError implements ErrorTypeDefinition { INSUFFICIENT_FUNDS(MuleErrors.VALIDATION); private ErrorTypeDefinition> parent; BalanceError(ErrorTypeDefinition> parent) { this.parent = parent; } @Override public Optional>> getParent() { return Optional.ofNullable(parent); } } ``` The validator method needs an `ErrorTypeProvider` that knows all the error types that the validation can throw. This example creates an `ErrorTypeProvider` that says that the only error the method can throw is of type **BALANCE:INSUFFICIENT\_FUNDS**. ```java public class BalanceErrorsProvider implements ErrorTypeProvider { @Override public Set getErrorTypes() { HashSet errors = new HashSet<>(); errors.add(BalanceError.INSUFFICIENT_FUNDS); return errors; } } ``` ## How To Use Validators Assume that you want to use the validator created above on a flow. The syntax will be the same as if it were an operation. Because `balance` and `price` are required parameters, it is necessary to assign those. In this case, the validation is made with the values in the variables `price` and `balance`, but you could use any expression there: ```xml ``` ## Validators and the "all" Scope Because validators are of the Stereotype `ValidatorStereotype`, they can be used in the `all` scope from the Validation module. The next example uses a validator from the Validation module. It also assumes that there is a number extension that has a validator named `isPositive` that validates whether a value is in fact positive. ```xml ``` This example checks that there is a `buyerName` variable that is not an empty string. After that, it ensures that both variables, `price` and `balance`, have positive values. Finally, it checks whether there is a sufficient balance on the account with the validator. --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, value provider, dynamic values --- # Value Providers > For the full documentation index, see: https://docs.mulesoft.com/llms.txt When developing a connector, you can let the end user select a parameter from a set of values. This makes it easier for the user to know what to configure, and you can be sure that the configured values will be correct. You can provide this functionality for known values by using a [Java Enum](#java_enum) as a parameter. The SDK automatically takes all the values from the Enum so they can be provided as a combo-selector in the UI. To include values that are not known, such as custom values, you should use [Value Providers](#value_providers), instead. Note that unlike [Metadata](https://docs.mulesoft.com/mule-sdk/latest/metadata), which is meant to discover the dynamic types of a parameter, this feature is designed to provide the possible values of a parameter. ## Java Enum Assume that you are developing a connector for a Content Publishing Service that can publish content with these roles: - `ADMIN` - `READER` - `WRITER` To provide these roles, you simply create a `UserRole` Enum and expose it as a parameter of the `publishAs` operation, for example: Example: Java Enum the Defines Known Roles ```java public enum UserRole { ADMIN, READER, WRITER } ``` The operation method in the next example lets the user publish content using a certain `UserRole`. ```java public void publishAs(@Content Object content, UserRole roleEnum){ System.out.println("Publishing " + content + " with Role: " + roleEnum); } ``` This solution is easy and effective, but it carries several limitations and problems: - Does not support custom values: Because the parameter is based on a Enum, you cannot define a custom value. For example, if the service to which you are connecting can be configured with custom roles, you will not be able to continue using the `UserRole` Enum because it will limit the connector’s capabilities. - The values are defined statically: You cannot change the values depending on a connection or configuration. - Java Enums have naming restrictions: You cannot have an Enum value starting with a Number or special characters. If need custom roles but do not want to lose the drop-down list of known User Roles, you can use [Value Providers](#value_providers) for dynamic values and known values. ## Value Providers Value Providers is a mechanism for providing values in a dynamic and smart way for any parameter in any component of a connector: [Operations](https://docs.mulesoft.com/mule-sdk/latest/operations), [Sources](https://docs.mulesoft.com/mule-sdk/latest/sources), [Configurations](https://docs.mulesoft.com/mule-sdk/latest/configs), [Connection Providers](https://docs.mulesoft.com/mule-sdk/latest/connections), scopes, and so on. ### To Implement Value Providers This example produces the same behavior as the Enum solution above, but it provides the benefit of an open set of values. It will support a custom value. 1. Create a Java Class implementing `ValueProvider`. When implementing this interface, you must implement the `resolve()` method, which returns a set of [values](#value-structure). ```java public class StaticUserRoleValueProvider implements ValueProvider { @Override public Set resolve() { return ValueBuilder.getValuesFor("ADMIN", "READER", "WRITER"); } } ``` 2. Mark the parameter that requires dynamic values with the `@OfValues()` annotation. `@OfValues` requires a `ValueProvider` implementation that is bound to the parameter. For example, the following operation method `publishAs` includes such binding: ```java public void publishAs(@Content Object content, @OfValues(StaticUserRoleValueProvider.class) String userRole){ (1) System.out.println("Publishing " + content + " with Role: " + userRole); } ``` 3. Now, you can use it in Studio to start retrieving values. ![Workflow of selecting a predefined value or defining a custom value](/mulesoft/current/en-us/mule-sdk/latest/_images/value-provider-static.gif) ### Value Providers with Connections or Configurations Value Providers can receive connections and configurations. This makes it possible to resolve values based on the current connection and configuration. > [!IMPORTANT] You cannot inject connections and configurations into Value Providers that are > located in connection and configuration parameters. #### Receiving a Connection or Configuration You use the `@Connection` and `@Config` annotations to declare the use of connections and configurations inside a Value Provider. To work, a Value Provider that uses these annotations depends on a _valid_ configuration or connection. The SDK ensures that the resolving logic will not be executed unless the connection and configuration are valid. ```java public class ConnectedValueProvider implements ValueProvider { @Connection ServiceConnection connection; @Config ServiceConfig config; @Override public Set resolve() throws ValueResolvingException { //Do whatever is required with the connection or config List result = connection.retrieveInfo(); return ValueBuilder.getValuesFor(result); } } ``` > [!WARNING] The injected connections and configurations must be of a type that is compatible with > those defined in the operation or source where the Value Provider is referenced. ### Example 2: Connected Value Provider Use Case In the Roles example above, the service could define custom roles, but it was not possible for the connector to communicate those roles. Once the Value Provider is implemented to resolve values through a working connection, it becomes possible to fetch the available roles and communicate them through the Value Provider: ```java public class UserRoleValueProvider implements ValueProvider { @Connection ServiceConnection connection; @Override public Set resolve() throws ValueResolvingException { return ValueBuilder.getValuesFor(connection.getAvailableRoles()); } } ``` ### Value Providers that Depend on Other Parameters In addition to injecting connections and configurations, Value Providers can depend on other parameters of the _same context_. The SDK ensures that the Value Provider resolving logic will not be executed until the required parameters are configured. The words "_same context_" mean that if the Value Provider is used in a component, the required parameter must exist in that component. For example, if the configuration `FancyConfig` with a Value Provider in the parameter `dynamicParam` requires the value of the parameter `aConfigParam`, `aConfigParam` must exist in the `FancyConfig` configuration. > [!CAUTION] The use of expressions in the required parameters might disable the execution of the > Value Provider due to the impossibility of resolving the expression without an active event. #### Declaring Required Parameters As with connectors and configurations, you use the `@Parameter` annotation to declare a parameter that is required for executing the resolving logic. You use the annotation in a field of the Value Provider with the **same type and name** as the required parameter, such as `String requiredParam` in these examples: Example External parameters: Operation declaring two params, one with a value provider ```java public void operationWithValueProvider(String requiredParam, @OfValues(ValueProviderWithRequiredParams.class) String dynamicParam){ } ``` Example External parameters: Value provider requiring the `requiredParam` parameter. ```java public class ValueProviderWithRequiredParams implements ValueProvider { @Parameter String requiredParam; @Override public Set resolve() { return ValuesBuilder.getValuesFor(param); } } ``` ##### What Happens If The Required Parameter Is Not Configured? If the parameter is defined as required in the component but is not configured by the end user, the Value Provider will not execute. However, if the parameter is defined as optional, the Value Provider will be executed with a `Null` value, so you will need to handle the nullability. ##### Example 3: Value Provider with Context Parameters Consider the case where you want to have a Date Picker for the day and month. You can easily represent them with two Enums, but not all months have the same number of days. So the representation might allow the user to configure invalid dates. To fix this issue: 1. Define an operation that exposes a Date Picker. The operation receives two parameters, an `monthEnum` that statically communicates all the available months and a `day` that is used to communicate the day of the month. Publish On Date Operation ```java public void publishOnDate(Month monthEnum, @OfValues(DayValueProvider.class) String day) { } ``` 2. Define the `Month` Enum. The `Month` contains all the available months and identifies the number of days in each month. MonthEnum Enum ```java public enum Month { JANUARY(31), FEBRUARY(28), MARCH(31), APRIL(30), MAY(31), JUNE(30), JULY(31), AUGUST(31), SEPTEMBER(30), OCTOBER(31), NOVEMBER(30), DECEMBER(31); private int dayCount; MonthEnum(int i) { dayCount = i; } public int getDayCount() { return dayCount; } } ``` 3. Create a Value Provider that consumes the selected Month. Depending on the selected month, the Value Provider will dynamically provide all the available days in that month. The `DayValueProvider` indicates that it requires the parameter `monthEnum` to work. ```java public class DayValueProvider implements ValueProvider { @Parameter Month monthEnum; (1) @Override public Set resolve() { return ValueBuilder.getValuesFor(getNumbersFrom(1, monthEnum.getDayCount()) .stream() .map(num -> String.format("%02d", num))); } List getNumbersFrom(int init, int end){ List numbers = new ArrayList<>(end - init); for (int i = init; i <= end; i++) { numbers.add(i); } return numbers; } } ``` 4. Result! As shown in the animation below, the `Day` selector gets populated dynamically based on the `Month enum` parameter value. ![Example of value provider with context parameters](/mulesoft/current/en-us/mule-sdk/latest/_images/value-provider-months.gif) #### Value Structure Value Providers return a set of Values. A `Value` is a simple structure composed of these properties: - `id` : A unique identifier for this value. This is required. - `displayName`: A name that will be displayed in the UI. This is optional. By default, the ID will be used as the Display Name. #### How to create a Value There is a unique way to create values by using `ValueBuilder`. ```java ValueBuilder adminValueBuilder = ValueBuilder.newValue("ADMIN_USR_ROLE"); (1) adminValueBuilder.withDisplayName("Admin"); (2) Value adminValue = newValue.build(); (3) ```
1You must create the ValueBuilder with the ID of the Value.
2Optionally, you can enrich the value with a Display Name.
3Build the builder to return a Value instance.
## ValueBuilder Utils `ValueBuilder` provides utilities to make it easier to create values for certain cases. If you have a `List`, `String[]`, `Stream`, or `Map` with values that need to be transformed to Values, the easiest way to make this transformation is to use `getValuesFor()`. ```java // Array Case Set arrayCase = ValueBuilder.getValuesFor("Admin", "Writer"); // List Case List valueList = new ArrayList<>(); valueList.add("Admin"); valueList.add("Writer"); Set listCase = ValueBuilder.getValuesFor(valueList); // Stream Case Set streamCase = ValueBuilder.getValuesFor(valueList.stream()); // Map Case // The Key will be considered as ID and the Value as Display Name Map valueMap = new HashMap<>(); valueMap.put("ADMIN_USR_ROLE", "Admin"); valueMap.put("WRITER_USR_ROLE") "Writer"); Set mapCase = ValueBuilder.getValuesFor(valueMap); ``` --- product: Mule SDK version: 1.13 is-latest-version: true keywords: mule, sdk, forward compatibility, minMuleVersion, MinMuleVersion annotation --- # Working with Minimum Mule Versions > For the full documentation index, see: https://docs.mulesoft.com/llms.txt With forward compatibility, each component in your module (operations, sources, configurations, and connection providers) has its own Minimum Mule Version. The SDK determines these versions automatically based on API usage, and you can control them when needed. ## Automatic Calculation The SDK tooling automatically calculates the minimum Mule version for each component based on the APIs it uses. When a component interacts with an SDK API (annotations, interfaces, classes), it inherits the minimum Mule version requirement of that API. For example, if an operation uses an annotation that requires Mule 4.12.0, the operation’s minimum Mule version is automatically set to 4.12.0. This calculation considers: - Annotations applied to the component class, fields, and methods - Interfaces implemented by the component - Types used in method parameters and return values - Superclasses extended by the component The final minimum Mule version for a component is the highest version required by any of the APIs it uses. ## Baseline Minimum Mule Version The baseline minimum Mule version is set via the `min.mule.version` property in your `pom.xml` file. When you [create a new module with the Maven archetype](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility#create-module-archetype)), this property defaults to `4.11.0`. Components can’t have a minimum Mule version lower than the baseline value. ### Example: Component-Level Versions This diagram shows how the SDK tooling calculates minimum Mule versions per component: Module: my-connector ├── Baseline (min.mule.version): 4.11.0 │ ├── listFiles operation │ └── Uses: @Parameter, StreamingHelper │ └── Calculated MMV: 4.11.0 (baseline) │ ├── uploadFile operation │ └── Uses: @Parameter, StreamingHelper, NewUploadFeature (requires 4.12.0) │ └── Calculated MMV: 4.12.0 │ └── streamWithCheckpoint operation └── Uses: @Parameter, CheckpointHelper (requires 4.13.0) └── Calculated MMV: 4.13.0 In this example: - All three operations are part of the same module release - `listFiles` works on Mule 4.11.0 and later (the baseline) - `uploadFile` works on Mule 4.12.0 and later - `streamWithCheckpoint` works on Mule 4.13.0 and later - The module’s minimum Mule version is 4.11.0 (the lowest among all components) Application developers targeting Mule 4.11.0 see only `listFiles`. Those on 4.12.0 see `listFiles` and `uploadFile`. Those on 4.13.0 see all three operations. ### Module-Level Minimum Mule Version While each component has its own Minimum Mule Version, the module as a whole also has one. The module’s minimum Mule version is the lowest version required by any of its components, or the baseline if specified. In the [Component-Level Versions example](#example-component-level-version), the module’s minimum Mule version is 4.11.0 because that is the lowest version required by any of its components (`listFiles`). ## Verify a Feature’s Minimum Mule Version Each API in the `org.mule.sdk.api` package is annotated with `@MinMuleVersion`, indicating the minimum runtime version required for that feature. Check this annotation in the source code to understand the version requirements before using an API. For example, if you see `@MinMuleVersion("4.12.0")` on an annotation, any component using that annotation requires at least Mule 4.12.0. ## Set Version Requirements With @MinMuleVersion While the SDK calculates Minimum Mule Versions automatically, you can use the `@MinMuleVersion` annotation to explicitly set a higher version for your components. ```java import org.mule.sdk.api.annotation.MinMuleVersion; @MinMuleVersion("4.12.0") public class AdvancedOperations { public void newFeatureOperation() { // This operation requires Mule 4.12.0 or later } } ``` Use this annotation when you want to restrict a component to specific minimum Mule version even though the component technically runs on earlier versions. Common reasons include: - The component depends on runtime behavior changes not reflected in SDK APIs - You want to limit initial availability while gathering feedback - The component is designed to work alongside other features only available in later versions You can’t use `@MinMuleVersion` to set a version lower than what the SDK calculates. If you try, the calculated version takes precedence and a warning is logged during the build. ## How Minimum Mule Version Affects Visibility The minimum Mule version for each component is stored in the Extension Model. Design-time tools, like Anypoint Code Builder and Anypoint Studio, use this information to: - Filter out components that require a higher Mule version than the application’s target Mule runtime version - Display warnings when a component isn’t compatible with the selected Mule runtime version As a result, application developers see only the components they can use with their target Mule runtime version. ## See Also - [Setting Up Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-setup) - [Controlling Version Propagation](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility-enforcement) - [SDK Forward Compatibility](https://docs.mulesoft.com/mule-sdk/latest/forward-compatibility) --- product: Mule SDK version: 1.13 is-latest-version: true --- # Writing Your First Test Case > For the full documentation index, see: https://docs.mulesoft.com/llms.txt This section provides a step-by-step introduction to testing a module’s operation. It explains the basics of test cases that you must perform. ## First Concepts Every test case for a module contains these basic items: - Test Case Class: A Java class containing the test logic. Call flows and assert the results. - Mule App XML: A Mule app that makes use of the Module to test. Note that this documentation is based on the module project generated with the Mule Extension Archetype ([Creating Your First SDK Project](https://docs.mulesoft.com/mule-sdk/latest/getting-started#generating-a-project-using-the-maven-archetype-directly)). Learn how to test a module using this example and refer to that documentation for the parent dependency, which includes all the dependencies required to build the test case. To export packages, refer to [Export Resources](https://docs.mulesoft.com/mule-runtime/latest/how-to-export-resources). ## 1\. Creating the Test Case Class Your Test Case Class must inherit from `MuleArtifactFunctionalTestCase`, which enables the testing of modules inside a Mule app. ```java public class BasicOperationsTestCase extends MuleArtifactFunctionalTestCase { } ``` You must include the following tag in your pom.xml file: ```xml org.mule.extensions mule-modules-parent 1.6.0 ``` > [!NOTE] If you want to compile connectors with Java 17, use version 1.9.0. ## 2\. Writing Your Mule App You test a module by running Mule app that uses the module, executing its flows and making assertions about the result of these executions. The Mule app XML must be placed inside the `src/test/resources` folder of the module project. ```xml ``` ![Basic example of a Mule app](/mulesoft/current/en-us/mule-sdk/latest/_images/first-test-app.png) Tip: An easy way to write Mule apps is to create them in Anypoint Studio, copy the generated XML, and paste it inside the Module project. ## 3\. Importing Your Mule App into the Test Case After creating the test case class and Mule app XML, you bind the two components, identifying the Mule app that will execute the test case. You do this by overriding the `getConfigFile()` method and returning a `String` containing a relative path of the Mule app XML. > [!WARNING] The config file path is relative to the `module-project/src/test/resources` folder. For > example, if the XML file is located in > `module-project/src/test/resources/test-connector-mule-app.xml`, the `getConfigFile()` method must > return `test-connector-mule-app.xml`. ```java public class ModuleTestCase extends MuleArtifactFunctionalTestCase { @Override protected String getConfigFile() { return "test-connector-mule-app.xml"; } } ``` ## 4\. Writing Your First Test Case At this stage, everything is set up to start writing the module test cases. The first test to write tests a simple operation that executes the flow, retrieves the output, and makes an assertion over it. ### 4.1 Executing a flow You use the `flowRunner("flowName")` utility method to create an instance of `FlowRunner`, a utility that can execute flows. The `FlowRunner` instance is configured to run the flow identified in the parameter. Then you use `run()` on the `FlowRunner` instance, for example: ```java Event event = flowRunner("sayHiFlow").run(); ``` Execution of the flow returns an `Event` that contains the `payload`, `attributes`, `vars`, and all the available information about the execution of the flow. > [!NOTE] If your flow returns a stream, you must call the keepStreamsOpen() method to prevent the > runtime from closing streams (which is the normal behavior when a flow ends). If you do not call > the keepStreamsOpen() method, you receive a `CursorProviderAlreadyClosedException` error when > consuming a stream. The following example shows how to call the keepStreamsOpen() method: > > ```java > Event event = flowRunner("streamFlow").keepStreamsOpen().run(); > ``` ### 4.2 Retrieving the Payload Value After executing the flow, you have an `event` variable that contains the result of the flow execution. This variable is required for obtaining the value of the payload and make the required assertions: ```java Object payloadValue = event.getMessage() .getPayload() .getValue(); assertThat(payloadValue, is("Hello Mariano Gonzales!!!")) ``` ### 4.3 Summary This example puts the preceding steps of the test case together: ```java @Test public void executeSayHiOperation() throws Exception { Event sayHiFlow = flowRunner("sayHiFlow").run(); (1) String payloadValue = ((String) sayHiFlow .getMessage() .getPayload() .getValue()); (2) assertThat(payloadValue, is("Hello Mariano Gonzales!!!")); (3) } ```
1Executes the sayHiFlow flow.
2Retrieves the payload value.
3Makes an assertion over the value.
--- product: Mule SDK version: 1.13 is-latest-version: true --- # XML SDK > For the full documentation index, see: https://docs.mulesoft.com/llms.txt The XML SDK is an alternative to the more advanced Java-based Mule SDK. XML SDK is for creating custom modules, similar to the way you create a Mule app. In fact, you can use existing Mule components in the module. The framework simply adds a few syntactic idioms as XML elements that enclose the main parts of module, such as ``, ``, and `` elements. For example, this snippet defines a single operation in the `Hello XML SDK`: ```xml ``` You use a Mule SDK component in a Mule flow the same way that you use any other component. In this example, execution of the flow `"random-flow"` sets the payload to `"Hello World!"`, as defined in the operation ``. ```xml ``` Note that the XML SDK is strongly typed, so the data type of defined parameters for every operation is statically set for both the input and output. ## Install the Required Software Install and verify that the following tools are working in your local environment: - Java Development Kit 17 (JDK 17) or earlier to compile and build your custom Java code. - For preexisting connectors, [JDK 8 might be needed](https://docs.mulesoft.com/mule-sdk/latest/java-version-support#jdk_8). - Apache Maven 3.9.8 or later to manage your project’s build. ## XML SDK Basics An XML SDK component is composed of key elements that delineate both its behavior and the way the runtime interacts with it: - Operations - Properties - The enclosing Module ### Operations An `` element defines a set of input parameters and a single output. Like a function, it has input parameters, performs actions (described in the body), and has a single output. Unlike a function, the behavior of an operation can vary if it stores values or references external sources. - Input parameters (``): Declares a type to be entered when calling the operation. Keep in mind that these parameters are the only data that the message processors in the `` scope can access. - Body (``): Defines a chain of components to be executed, similar to a flow. - Output (``): Declares the output type of your XML SDK module. This is the type of the payload after it is processed by the ``. - Errors: Declares the error types the XML SDK can raise (or map) within the ``. The following XML SDK module takes two numbers as parameters and has a single operation that sums them together: ```xml ... ``` To use an XML SDK module in a Mule app, you simply add it to a Mule flow, for example: ```xml ```
Table 1. Attributes of <parameter>
NameUseDefault ValueDescription

name

required

NA

Name of the <parameter>

defaultValue

optional

NA

The <operation> uses the default value if you do not provide another value.

use

required

AUTO

Possible values:

  • REQUIRED: Parameter must be present. It cannot be REQUIRED if the parameter has a defaultValue.

  • OPTIONAL: Parameter is not required.

  • AUTO: Defaults at runtime to REQUIRED if defaultValue is absent. Otherwise, it is marked as OPTIONAL.

type

required

NA

Defines the data type of the <parameter>. You can use any primitive type such as string, boolean, datetime, date, number, time, or it can also rely on any type defined in the catalog.

password

optional

false

Marks the <parameter> with **** in the UI.

role

required

BEHAVIOUR

Set of defined roles for a given parameter that modifies the generated XSD for the current <parameter>.

  • BEHAVIOUR renders an attribute.

  • CONTENT implies support for DataWeave in place as a child element.

  • PRIMARY works like CONTENT but maps to the payload by default.

summary

optional

NA

Adds a small tooltip to the <parameter>.

example

optional

NA

Adds a short example of the data type for this parameter.

displayName

optional

NA

Provides a UI label. When there is no displayName, the default value is a hyphenated version of the name attribute.

order

optional

NA

Defines an order in which to render each element in the UI.

tab

optional

NA

Defines the group (or tab) to which the <parameter> must belong in the UI.

visibility

optional

PUBLIC

Available since version 1.2

Marks an operation’s visibility to either PUBLIC (anyone can see and use it) or PRIVATE (accessible only for the current module and cannot be seen externally), for example:

<operation name="example" visibility="PRIVATE">
<parameters>
<parameter name="errorTopic" type="string" />

doc:description

optional

NA

Documentation for the <parameter>.

Table 2. Attributes of <output>
NameUseDefault ValueDescription

type

optional

The data type of the output payload. Note that you can set it to void by removing the element. This prevents the <operation> from modifying the Mule event even if its behavior involves modifying the payload.

Attribute type definitions are supported by `` elements when you use the `` element.
Table 3. Attributes of <output-attributes>
NameUseDefault ValueDescription

type

optional

The data type of the output attribute. Note that you can set it to void by removing the element. This prevents the <operation> from modifying the Mule event even if its behavior involves modifying the payload.

Both outputs (`` and ``) become part of the `MuleMessage` that is created when the control returns to the invoker.
Table 4. Attributes of <error>
NameUseDefault ValueDescription

type

required

The type of error code to throw (or remap) in the <body>. More info about Mule Error concept.

### Properties A `` is for a field defined by an end user of the XML SDK component. It serves as a global configuration for the entire Mule project in which it is used. Properties are similar to the parameters exposed by operations, but they act at a level that affects all instances of the XML SDK component in the project, instead of a specific operation. Like parameters in operations, properties are usually simple types that have default values. > [!TIP] To avoid confusing end users of the XML SDK module, only expose the properties that they > might need to edit. For example, do not expose internal values that they cannot or should not > change. The following XML SDK module sends requests to [GitHub API V3](https://docs.github.com/en/rest/users) to retrieve an authenticated user: ```xml ``` The example references a `` that is defined in the module: - In a global element as the value for a `request-config`. - In an operation as the value to a `config-ref` attribute in an `http-request`. The following Mule app uses XML SDK module. Note that the `github` prefix (for example, `github:get-user`) is derived from the `name` of the module. ```xml ``` Every execution of the `"test-github-flow"` returns the GitHub information of the authenticated user: ```json { "login": "fernandezlautaro", "id": 4719511, "avatar_url": "https://avatars1.githubusercontent.com/u/4719511?v=3", "gravatar_id": "", "url": "https://api.github.com/users/fernandezlautaro", ... } ``` Note that incorrect credentials return this error response from GitHub: ```json { "message": "Requires authentication", "documentation_url": "https://developer.github.com/v3" } ```
Table 5. <property> Attributes
NameUseDefault ValueDescription

name

required

NA

Name of the <property>.

defaultValue

optional

NA

The <property> uses the default value if you do not provide another value.

use

required

AUTO

Possible values:

  • REQUIRED: Property must be present. It cannot be REQUIRED if the parameter has a defaultValue.

  • OPTIONAL: Property is not required.

  • AUTO: Defaults at runtime to REQUIRED if defaultValue is absent. Otherwise, it is marked as OPTIONAL.

type

required

NA

Defines the data type of the <property>. You can use any primitive type such as string, boolean, datetime, date, number, time, or it can also rely on any type defined in the catalog.

password

optional

false

Hides the value of the property value in the UI when typing it (using ****).

summary

optional

NA

Adds a small tooltip to the <property>.

example

optional

NA

Adds a short example of the data type for this property.

displayName

optional

NA

Provides a UI label. When there is no displayName, the default value is a hyphenated version of the name attribute.

order

optional

NA

Defines an order in which to render each element in the UI.

tab

optional

NA

Defines the group (or tab) to which the <property> must belong in the UI.

doc:description

optional

NA

Documentation for the <property>.

> [!NOTE] Extensions made with XML SDK do not support implicit configurations, which means even > though all properties defined in the extension are optional, all operations must have a > configuration. You can create a configuration without specifying the values of its parameters so > you can use the default values. ### Module The `` element is the root of an XML SDK module. It contains all properties and operations that belong to the module.
Table 6. <module> Attributes
NameUseDefault ValueDescription

name

required

NA

Name of the <module>.

vendor

optional

"MuleSoft"

Vendor of the XML SDK module.

prefix

optional

NA

The prefix of the module used when generating schemas. If empty, the module uses a hyphenated version of the name attribute. Refer to Prefix Attribute.

namespace

optional

NA

Namespace to use for the module during schema generation. Otherwise, the default is http://www.mulesoft.org/schema/mule/<prefix> where <prefix> is the prefix attribute value.

doc:description

optional

NA

Documentation for the <module>.

You import an XML SDK schema into a Mule app by using the `namespace` attribute. The XML schemas are generated dynamically. The next table shows how `namespace`, `prefix`, and `name` attributes work together.
Table 7. <module> provides name, prefix, and namespace
Provided ValuesGenerated Values

name="hello with spaces"

name="hello with spaces"

prefix="hello-prefix"

prefix="hello-prefix"

namespace="http://www.mulesoft.org/schema/a/different/path/mule/hello"

namespace="http://www.mulesoft.org/schema/a/different/path/mule/hello"

The generated schema location: `http://www.mulesoft.org/schema/a/different/path/mule/hello/current/mule-hello-prefix.xsd`
Table 8. <module> provides name and prefix
Provided ValuesGenerated Values

name="hello with spaces"

name="hello with spaces"

prefix="hello-prefix"

prefix="hello-prefix"

NA

namespace=http://www.mulesoft.org/schema/mule/hello-prefix

Generated schema location: `http://www.mulesoft.org/schema/mule/hello-prefix/current/mule-hello-prefix.xsd`
Table 9. <module> provides just name
provided valuesgenerated values

name="hello with spaces"

name="hello with spaces"

NA

prefix="hello-with-spaces"

NA

namespace=http://www.mulesoft.org/schema/mule/hello-with-spaces

Generated schema location is `http://www.mulesoft.org/schema/mule/hello-with-spaces/current/mule-hello-with-spaces.xsd` ### Prefix Attribute The `prefix` attribute is the prefix of the module used when generating schemas. If empty, the module uses a hyphenated version of the XML SDK `name` attribute. The value of the `prefix` attribute correlates with the name of the folder containing the resource files. Thus, setting or not setting this value affects the name of the resource folder. When setting a prefix with a custom name, you must rename the resource folder to match the custom prefix name. The following module only has a `name` attribute `name="hello with spaces"`. This means that its `prefix` is dynamically generated as `hello-with-spaces`, and its `namespace` is dynamically generated as `http://www.mulesoft.org/schema/mule/hello-with-spaces/current/mule-hello-with-spaces.xsd`. It also means that the Mule app must have a schema location (`schemaLocation`) that points to a reference that matches that value. ```xml ``` This `hello with spaces` module above can be used in a Mule app, for example: ```xml ``` ## Create and test an XML SDK Project To create an XML SDK module: 1. Add the MuleSoft repository to your Maven (`mvn`) settings file: ```text Mule true mulesoft-releases MuleSoft Repository http://repository.mulesoft.org/releases/ default ``` 2. Use Maven (`mvn`) from to execute the following command: ```text mvn archetype:generate \ -DarchetypeGroupId=org.mule.extensions \ -DarchetypeArtifactId=mule-extensions-xml-archetype \ -DarchetypeVersion=1.2.0 \ -DgroupId=org.mule.extension \ -DartifactId=hello-mule-extension \ -DmuleConnectorName=Hello ``` > [!TIP] Since the connector name above is `Hello`, the namespace for the module will be > automatically set to `module-hello`, as configured in the `prefix` attribute. 3. When prompted to indicate whether the values are correct, press `enter` to continue. The Maven archetype creates a stub project with a minimal amount of code for the XML SDK module and a functional test to run it. The structure of that project looks something like this: ```text ➜ hello-mule-extension tree . . ├── pom.xml └── src ├── main │ └── resources │ └── org │ └── mule │ └── yourdomain │ └── module-Hello.xml (1) └── test └── munit └── assertion-munit-test.xml (2) 8 directories, 3 files ``` (1) `hello-mule-extension/src/main/resources/org/mule/yourdomain/module-Hello.xml`: Defines the XML SDK root element. (2) `hello-mule-extension/src/test/munit/assertion-munit-test.xml`: An assertion operation that calls the XML SDK operation. 4. Run `mvn clean install` in the `/hello-mule-extension` to create the plugin for the `Hello XML SDK` module. This command installs a parent project as well as its child projects. It also runs the suite through MUnit for the operation defined in the module. ```text ➜ hello-mule-extension mvn clean install ... .. . ================================================================================== Number of tests run: 2 - Failed: 0 - Errors: 0 - Skipped: 0 - Time elapsed: 2246ms ================================================================================== [INFO] ==================================================================================== [INFO] MUnit Run Summary - Product: MULE, Version: 4.1.1 [INFO] ==================================================================================== [INFO] >> assertion-munit-test.xml test result: Tests: 2, Errors: 0, Failures: 0, Skipped: 0 [INFO] [INFO] ==================================================================================== [INFO] > Tests: 2 [INFO] > Errors: 0 [INFO] > Failures: 0 [INFO] > Skipped: 0 [INFO] ==================================================================================== .... ... .. [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 39.166 s [INFO] Finished at: 2017-06-14T22:07:42-03:00 [INFO] Final Memory: 61M/928M [INFO] ------------------------------------------------------------------------ ➜ hello-smart-connector ``` (3) When you want to add this connector to a project from your local Maven repository for testing, add the following dependency: org.mule.extension hello-mule-extension 1.0.0-SNAPSHOT mule-plugin \+ You can now use this connector from your Studio palette. - ## Add a Custom Icon to Your Connector Connector icons are files in the .svg format. To customize the icon of a connector, add an `icon.svg` file that contains the custom icon under the `icon` folder in the root of the connector tree. The following image shows an example of the folder structure for the icon: ![The connector root directory has a folder named icon that contains the icon.svg file](/mulesoft/current/en-us/mule-sdk/latest/_images/icon-svg-change.png) After you add the custom icon to your connector, the icon appears in Anypoint Exchange and in Anypoint Studio. ## Consuming a Mule Plugin from an XML SDK Module To consume a Mule plugin from within an XML SDK module: 1. Add the dependency into the POM file for the XML SDK module. For example, for an XML SDK module to use the HTTP connector and the OAuth module, the POM needs to include the following dependencies: ```xml org.mule.connectors mule-http-connector 1.2.1 mule-plugin compile org.mule.modules mule-oauth-module 1.1.2 mule-plugin compile ``` 2. Add the schema location to the `` root element, for example: ```xml ... ``` ## Reuse Operations In some cases, operations have repeated message processors, on which we can rely if they are encapsulated in a new operation and called from other places. Each `` defined in a `` can be reused in the _same_ `` if the operation does not have cyclic dependencies. For example, assume that a `` validates input parameters before performing inserts and updates. Notice that validations in the next example are repeated in the operations `validate-and-insert` and `validate-and-update`. ```xml INSERT INTO PLANET(NAME) VALUES (:name) #[{ 'name' : vars.name }] update PLANET set NAME= :newName where NAME=':originalName' #[{'originalName' : vars.originalName, 'newName' : vars.newName}] ``` To simplify this process in the previous example, you can add a `validate` operation that you call from the other operations, for example: ```xml ``` To consume the other operations from within a ``: 1. Add an XML namespace `xmlns:tns` attribute and a new value to `schemaLocation` to the ``. Note that the value must map the target namespace of the current module. 2. Call the operations by using the `tns` prefix followed by the name of the operation.The complete module looks something like this: ```xml INSERT INTO PLANET(NAME) VALUES (:name) #[{ 'name' : vars.name }] update PLANET set NAME= :newName where NAME=':originalName' #[{'originalName' : vars.originalName, 'newName' : vars.newName}] ``` Note that the `config-ref` is not included because this is a reference to the _same_ module, which implies all global instances are shared among operations. ## Reuse Operations Within Operations To reuse an operation within another operation with a `use=CONTENT` [content parameter](https://docs.mulesoft.com/mule-sdk/latest/content-parameters), reference the `use=CONTENT` parameter in the inner tag name. Suppose there is a `shared-operation` operation that defines its `requiredParameter` parameter with `use=CONTENT`. Another operation, `sample-operation`, reuses `shared-operation` within its own definition, for example: ```xml #[vars.required-Parameter] ``` > [!NOTE] Notice that the parameter, **requiredParameter**, uses the camel case naming convention, > but the reference, **vars.required-Parameter**, uses the kebab case naming convention. When you > name a parameter or an operation, use the kebab case naming convention to ensure consistency > within your project. Refer to [XML SDK Limitations](#xml-sdk-limitations). The `requiredParameter` value must be referenced as an inner tag of `sample-operation`. Otherwise, the project throws a build error, such as, `Attribute 'required-parameter' is not allowed to appear in element 'tns:shared-operation'.` The XML definition of the connector generates parameters set with `use=CONTENT` as inner tags. Refer to [File Connector](https://docs.mulesoft.com/mule-sdk/latest/content-parameters#content-param), in which the parameter named `content` is set with `use=CONTENT` as the inner tag ``. ## Providing a Test Connection At design time, it is helpful to provide feedback when the attributes of a global element are fed with wrong values, such as wrong username or password, bad URLs, and so on. To provide such feedback, your module needs to incorporate a global element that supports connection testing. For example, the XML SDK module `` might use the connection testing functionality from the File connector by incorporating the `file:connection` element into the module. By default, the module picks up and supports the connection testing feature from the File configuration. ```xml ``` From the UI, connection testing is delegated to the global element encapsulated by `fileConfig`. If a module contains two or more global elements that provide a test connection, an error occurs when you build the module unless you mark the global element that you want to use with the `xmlns:connection="true"` attribute, for example: ```xml ``` > [!NOTE] Marking multiple global elements with `xmlns:connection="true"` makes the compilation > fail, as there can be only one. ## Handling Errors In some cases, operations within the `` throw error codes that should not be propagated as-is. In this case, you need to remap the codes to something more meaningful to the end user. In other cases, the issues might pertain to conditions within the ``. The XML SDK relies on [error mappings](https://docs.mulesoft.com/mule-runtime/latest/mule-error-concept#error-mappings) for the former. For the latter, the [raise error component](https://docs.mulesoft.com/mule-runtime/latest/raise-error-component-reference) is used. This example performs error mapping in an operation that divides two numbers: ```xml ... ``` If the divisor `numberB` is zero, the `div` operation results in the `MULE:EXPRESSION` runtime error, which does not describe the error specifically enough. To create a more specific error, you can use error mapping to make the `div` operation produce the `MATH-XML-SDK:DIVISION_BY_ZERO` error. You must include the namespace in the custom error to avoid unexpected behavior. For example: ```xml ... ``` You can produce the same error by executing a validation before the evaluation of the expression `#[vars.numberA / vars.numberB]`. If the expression fails, the `MATH-XML-SDK:DIVISION_BY_ZERO` error results, for example: ```xml ... ``` ## XML SDK Catalog The standard data types for `` and `` are primitive types: `string`, `boolean`, `number`, `date`, `datetime`, `localdatetime`, `time`, `localtime`, `timezone`, `binary`, `any`, `regex`. To define types that with more complex structures than the primitive types, you can create a catalog of data types that you inject into the module. This example creates a catalog file (`hello-smart-connector/smart-connector/src/main/resources/module-Hello-catalog.xml`) with the following content: ```xml ``` The catalog file references XSD and JSON schema files: - `person-schema.xsd`, which contains the following content: ```xml ``` - `person-schema.json`, which contains the following content: ```json { "type": "object", "properties": { "age": { "type": "integer" }, "name": { "type": "string" }, "lastname": { "type": "string" } }, "additionalProperties": false } ``` So, the structure of the `tree hello-smart-connector/smart-connector` folder looks like this: ```text ➜ ~ tree hello-smart-connector/smart-connector hello-smart-connector/smart-connector ├── pom.xml └── src └── main └── resources ├── module-Hello-catalog.xml ├── module-Hello.xml ├── person-schema.json └── person-schema.xsd ``` Once the schemas are ready, you use types they define by referencing the associated catalogs (`PersonXsdType` and `PersonJsonType`), for example: ```xml ... ``` Notice that the value of the `type` attribute for the JSON schema is the name of the catalog that contains that schema (`PersonJsonType`). However, for the XML schema, the value of the `type` attribute appends two colons `::` and the qname (qualified name) reference to the `Person` element: `PersonXsdType::{http://uri}Person`. To perform the DataWeave transformation from JSON to XML (shown within ``), it is necessary to add the following dependency to the POM file so that the module can find the required schema (`mule-ee.xsd`): ```xml com.mulesoft.mule.runtime.modules mule-module-spring-config-ee ${mule.version} provided ``` To use the operations from the [example](#example) above in a Mule app, it is necessary to feed values to them, for example: ```xml ``` When parameterizing values that are not primitive types, the defined `` can declare them as `role="CONTENT"` so that it is not mandatory to use an additional processor in the `` to call the operation. The `person-xml-to-json` operation in this example adds this attribute to the `content` parameter: ```xml ... ... ``` To use the operations from the [example](#example2) above in a Mule app, it is necessary to feed values to them, for example: ```xml .. ``` ## TLS Support Starting with Mule 4.6, XML SDK supports Transport Layer Security (TLS). Use TLS to enhance security for your app. XML SDK connectors can support the `` parameter in their configuration parameters. ### Parameter Declaration Use the `xmlns:tlsEnabled=true` annotation to specify that the component supports TLS. The annotated element does not necessarily have to be a top-level element. In the following example, the annotated element is `` which is a child of the `` top-level element: ```xml ``` You can specify the `` parameter in only one component. Using it on more than one component results in an error. XML SDK adds a `` parameter in the configuration of the generated extension. This parameter is optional unless the `` parameter in the target element is required and doesn’t have a default value. In the generated extension, the supplied `` parameter is mapped to the config element with this annotation. You can declare the `` parameter inline or by-reference: - Inline declaration ```xml ``` - By-reference declaration ```xml ``` ### Parameter Default Value The target element can already provide a value for the `` parameter, for example: ```xml ``` In this case, the value that comes from the module definition is used as the default. The synthetic parameter is also added to the module config, which, if provided, is used as an override. For both inline and by-reference declarations, providing a default value at the target element makes the synthetic parameter optional even if the parameter at the target element is required. ## Working Examples of XML SDK Modules [https://github.com/mulesoft-labs/smart-connectors-integration-tests](https://github.com/mulesoft-labs/smart-connectors-integration-tests) contains the following directories: - `apps-using-smart-connectors`: Mule apps that use XML SDK modules - `smart-connectors`: XML SDK modules that incorporate DataWeave, HTTP connector, File connector, Validation module, and so on. The following subsections describe some of these examples. ### Example: Using Core Components This example incorporates core components, such as Set Payload (`mule:set-payload`). GitHub Location: [smart-connectors/smart-connector-using-core](https://github.com/mulesoft-labs/smart-connectors-integration-tests/tree/master/smart-connectors/smart-connector-using-core) ```xml ``` ### Example: Using JSON Custom Types This example incorporates JSON types. GitHub Location: [smart-connectors/smart-connector-using-custom-types-json](https://github.com/mulesoft-labs/smart-connectors-integration-tests/tree/master/smart-connectors/smart-connector-using-custom-types-json) ```xml ``` Catalog ```xml ``` Schema ```json { "type": "object", "properties": { "number": { "type": "number" }, "street_name": { "type": "string" }, "street_type": { "type": "string", "enum": ["Street", "Avenue", "Boulevard"] } }, "additionalProperties": false } ``` ### Example: Using Custom XML Types This example incorporates custom XML types. GitHub Location: [smart-connectors/smart-connector-using-custom-types-xsd](https://github.com/mulesoft-labs/smart-connectors-integration-tests/tree/master/smart-connectors/smart-connector-using-custom-types-xsd) ```xml ``` Catalog ```xml ``` Schema 1 ```xml A user with all the information ``` ### Example: Exporting Resources This example explains how to add a resource to your project so that it is exported and packaged with your connector. GitHub Location: [smart-connectors/smart-connector-exporting-resources](https://github.com/mulesoft-labs/smart-connectors-integration-tests/tree/master/smart-connectors/smart-connector-exporting-resources) As the objective is to export a resource and make it available in the main mule app, the module file `module-exporting-resources.xml` does not contain any functionality. module-exporting-resources.xml ```xml ``` The following resource file is exported: myMappings.dwl ```text %dw 2.0 /****************************************************************************************************************************** NOTICE: This file, `dwModule.dwl`, must be referenced in the META-INF/mule-artifact/mule-artifact.json file to properly export the resource so that the functions being consumed (`five()`, `echo(String)`, `toUpper(Person)`, `toUpper(Array): Array = persons map toUpper($) ``` The resource file **must** be placed inside a new folder named after the module’s name (which was defined in the [main module file](#module-exporting-resources)). In this example, the module is named `module-exporting-resources`, so the folder must be named `module_exporting_resources`. Note that hyphens are replaced with underscores. All files contained in this new folder or its subfolders are automatically exported by the XML SDK. In the example, the folder structure looks like this: ```text smart-connector-exporting-resources ├── pom.xml └── src └── main └── resources ├── module-exporting-resources.xml └── module_exporting_resources └── weirdFolder └── myMappings.dwl ``` Finally, the exported file can be used in a Mule flow. This first example illustrates how to use the dataweave functions defined in the exported file: ```xml ``` This second example shows how to access the file through its location to be used by, for instance, the [Parse Template](https://docs.mulesoft.com/mule-runtime/4.3/parse-template-reference) component. ```xml ``` ### Example: Using DataWeave This example incorporates DataWeave by using the Transform (`ee:transform`) component. GitHub Location: [smart-connectors/smart-connector-using-dw](https://github.com/mulesoft-labs/smart-connectors-integration-tests/tree/master/smart-connectors/smart-connector-using-dw) ```xml ``` ### Example: Using the File Connector Location `[smart-connectors/smart-connector-using-file](https://github.com/mulesoft-labs/smart-connectors-integration-tests/tree/master/smart-connectors/smart-connector-using-file)`: depends on File Connector, e.g.: `file:list` ```xml ``` ### Using HTTP Connector This example incorporates the HTTP connector, using an HTTP Request (`http:requester`). GitHub Location: [smart-connectors/smart-connector-using-http](https://github.com/mulesoft-labs/smart-connectors-integration-tests/tree/master/smart-connectors/smart-connector-using-http) ```xml ``` ### Example: Using Another XML SDK This example shows one XML SDK module (`module-using-smart-connector`) using the XML SDK module `module-using-core` (described in [Example: Using Core Components](#module-using-core)). GitHub Location: [smart-connectors/smart-connector-using-smart-connector](https://github.com/mulesoft-labs/smart-connectors-integration-tests/tree/master/smart-connectors/smart-connector-using-smart-connector) ```xml ``` ### Using the Validation Module This example uses the Validation module, specifically `validation:is-email`. GitHub Location: [smart-connectors/smart-connector-using-validation](https://github.com/mulesoft-labs/smart-connectors-integration-tests/tree/master/smart-connectors/smart-connector-using-validation) ```xml ``` ## XML SDK Limitations The SDK currently has the following limitations: - XML SDK only provides outbound operations, not sources (such as a ``) or routers. - Operations do not support recursive calls. - Operations cannot have parameters called `name` or `config-ref` as those are reserved keywords. - Use the kebab case naming convention to name parameters and operations in any XML SDK project to prevent errors while generating XML tags, as the name values are converted to contain hyphens between words.