![]() ![]() ![]() ![]() ![]() ![]() |
The following sections describe how to use Web Services Reliable Messaging:
WARNING: | This feature can be implemented only for a JAX-RPC 1.1-based Web Service; you cannot implement it for a JAX-WS 2.0 Web Service. |
Web Service reliable messaging is a framework whereby an application running in one application server can reliably invoke a Web Service running on another application server, assuming that both servers implement the WS-ReliableMessaging specification. Reliable is defined as the ability to guarantee message delivery between the two Web Services.
Note: | Web Services reliable messaging works between any two application servers that implement the WS-ReliableMessaging specification. In this document, however, it is assumed that the two application servers are WebLogic Server instances. |
WebLogic Web Services conform to the WS-ReliableMessaging specification (February 2005), which describes how two Web Services running on different application servers can communicate reliably in the presence of failures in software components, systems, or networks. In particular, the specification describes an interoperable protocol in which a message sent from a source endpoint (or client Web Service) to a destination endpoint (or Web Service whose operations can be invoked reliably) is guaranteed either to be delivered, according to one or more delivery assurances, or to raise an error.
A reliable WebLogic Web Service provides the following delivery assurances:
See the WS-ReliableMessaging specification for detailed documentation about the architecture of Web Service reliable messaging. Using Web Service Reliable Messaging: Main Steps describes how to create the reliable and client Web Services and how to configure the two WebLogic Server instances to which the Web Services are deployed.
Note: | Web Services reliable messaging is not supported with the JMS transport feature. |
WebLogic Web Services use WS-Policy files to enable a destination endpoint to describe and advertise its Web Service reliable messaging capabilities and requirements. The WS-Policy specification provides a general purpose model and syntax to describe and communicate the policies of a Web service.
These WS-Policy files are XML files that describe features such as the version of the supported WS-ReliableMessaging specification, the source endpoint’s retransmission interval, the destination endpoint’s acknowledgment interval, and so on.
You specify the names of the WS-Policy files that are attached to your Web Service using the @Policy
JWS annotation in your JWS file. Use the @Policies
annotation to group together multiple @Policy
annotations. For reliable messaging, you specify these annotations only at the class level.
WebLogic Server includes two simple WS-Policy files that you can specify in your JWS file if you do not want to create your own WS-Policy files:
DefaultReliability.xml
—Specifies typical values for the reliable messaging policy assertions, such as inactivity timeout of 10 minutes, acknowledgement interval of 200 milliseconds, and base retransmisstion interval of 3 seconds. See DefaultReliability.xml WS-Policy File for the actual WS-Policy file.LongRunningReliability.xml
—Similar to the preceding default reliable messaging WS-Policy file, except that it specifies a much longer activity timeout interval (24 hours.) See LongRunningReliability.xml WS-Policy File for the actual WS-Policy file.You cannot change these pre-packaged files, so if their values do not suit your needs, you must create your own WS-Policy file.
See Creating the Web Service Reliable Messaging WS-Policy File for details about creating your own WS-Policy file if you do not want to one included with WebLogic Server. See Web Service Reliable Messaging Policy Assertion Reference for reference information about the reliable messaging policy assertions.
<?xml version="1.0"?>
<wsp:Policy
xmlns:wsrm="http://schemas.xmlsoap.org/ws/2005/02/rm/policy"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:beapolicy="http://www.bea.com/wsrm/policy"
>
<wsrm:RMAssertion >
<wsrm:InactivityTimeout
Milliseconds="600000" />
<wsrm:BaseRetransmissionInterval
Milliseconds="3000" />
<wsrm:ExponentialBackoff />
<wsrm:AcknowledgementInterval
Milliseconds="200" />
<beapolicy:Expires Expires="P1D" optional="true"/>
</wsrm:RMAssertion>
</wsp:Policy>
<?xml version="1.0"?>
<wsp:Policy
xmlns:wsrm="http://schemas.xmlsoap.org/ws/2005/02/rm/policy"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:beapolicy="http://www.bea.com/wsrm/policy"
>
<wsrm:RMAssertion >
<wsrm:InactivityTimeout
Milliseconds="86400000" />
<wsrm:BaseRetransmissionInterval
Milliseconds="3000" />
<wsrm:ExponentialBackoff />
<wsrm:AcknowledgementInterval
Milliseconds="200" />
<beapolicy:Expires Expires="P1M" optional="true"/>
</wsrm:RMAssertion>
</wsp:Policy>
Configuring reliable messaging for a WebLogic Web Service requires standard JMS tasks such as creating JMS servers and Store and Forward (SAF) agents, as well as Web Service-specific tasks, such as adding additional JWS annotations to your JWS file. Optionally, you create WS-Policy files that describe the reliable messaging capabilities of the reliable Web Service if you do not use the pre-packaged ones.
If you are using the WebLogic client APIs to invoke a reliable Web Service, the client application must run on WebLogic Server. Thus, configuration tasks must be performed on both the source WebLogic Server instance on which the Web Service that includes client code to invoke the reliable Web Service reliably is deployed, as well as the destination WebLogic Server instance on which the reliable Web Service itself is deployed.
The following procedure describes how to create a reliable Web Service, as well as a client Web Service that in turn invokes an operation of the reliable Web Service reliably. The procedure shows how to create the JWS files that implement the two Web Services from scratch; if you want to update existing JWS files, use this procedure as a guide. The procedure also shows how to configure the source and destination WebLogic Server instances.
It is assumed that you have created a WebLogic Server instance where you have set up an Ant-based development environment and that you have a working build.xml
file to which you can add targets for running the jwsc
Ant task and deploying the generated reliable Web Service. It is further assumed that you have a similar setup for another WebLogic Server instance that hosts the client Web Service that invokes the Web Service reliably. For more information, see:
This is the WebLogic Server instance to which the reliable Web Service is deployed.
See Configuring the Destination WebLogic Server Instance.
This is the WebLogic Server instance to which the client Web Service that invokes the reliable Web Service is deployed.
See Configuring the Source WebLogic Server Instance.
See Creating the Web Service Reliable Messaging WS-Policy File for details about creating your own WS-Policy file.
See Programming Guidelines for the Reliable JWS File.
build.xml file
to include a call to the jwsc
Ant task which will compile the reliable JWS file into a Web Service.
See
Running the jwsc WebLogic Web Services Ant Task for general information about using the jwsc
task.
prompt> ant build-mainService deploy-mainService
See Programming Guidelines for the JWS File That Invokes a Reliable Web Service.
build.xml
file that builds the client Web Service.See Updating the build.xml File for a Client of a Reliable Web Service.
prompt> ant build-clientService deploy-clientService
Configuring the WebLogic Server instance on which the reliable Web Service is deployed involves configuring JMS and store and forward (SAF) resources.
You can either configure these resources yourself, or you can use the Configuration Wizard to extend the WebLogic Server domain using a Web Services-specific extension template. Using the Configuration Wizard greatly simplifies the required configuration steps; for details, see Configuring Your Domain For Web Services Features.
If, however, you prefer to configure the resources yourself, use the following high-level procedure which lists the tasks and then points to the Administration Console Online Help for details on performing the tasks.
See Invoking the Administration Console for instructions on the URL that invokes the Administration Console.
See Create file stores.
See Create JMS servers.
Take note of the JNDI name you define for the JMS queue because you will later use it when you program the JWS file that implements your reliable Web Service.
See Create JMS modules and Create queues.
When you create the SAF agent:
Both
to enable both sending and receiving agents.If you are using the Web Service reliable messaging feature in a cluster, you must:
Configuring the WebLogic Server instance on which the client Web Service is deployed involves configuring JMS and store and forward (SAF) resources.
You can either configure these resources yourself, or you can use the Configuration Wizard to extend the WebLogic Server domain using a Web Services-specific extension template. Using the Configuration Wizard greatly simplifies the required configuration steps; for details, see Configuring Your Domain For Web Services Features.
If, however, you prefer to configure the resources yourself, use the following high-level procedure which lists the tasks and then points to the Administration Console Online Help for details on performing the tasks.
See Invoking the Administration Console for instructions on the URL that invokes the Administration Console.
See Create file stores.
See Create JMS servers.
Be sure when you create the SAF agent that you set the Agent Type field to Both
to enable both sending and receiving agents.
A WS-Policy file is an XML file that contains policy assertions that comply with the WS-Policy specification. In this case, the WS-Policy file contains Web Service reliable messaging policy assertions.
You can use one of the two default reliable messaging WS-Policy files included in WebLogic Server; these files are adequate for most use cases. However, because these files cannot be changed, if they do not suit your needs, you must create your own. See Use of WS-Policy Files for Web Service Reliable Messaging Configuration for a description of the included WS-Policy files. The remainder of this section describes how to create your own WS-Policy file.
The root element of the WS-Policy file is <Policy>
and it should include the following namespace declarations for using Web Service reliable messaging policy assertions:
<wsp:Policy
xmlns:wsrm="http://schemas.xmlsoap.org/ws/2005/02/rm"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:beapolicy="http://www.bea.com/wsrm/policy">
You wrap all Web Service reliable messaging policy assertions inside of a <wsrm:RMAssertion>
element. The assertions that use the wsrm:
namespace are standard ones defined by the
WS-ReliableMessaging specification. The assertions that use the beapolicy:
namespace are WebLogic-specific. See
Web Service Reliable Messaging Policy Assertion Reference for details.
All Web Service reliable messaging assertions are optional, so only set those whose default values are not adequate. The order in which the assertions appear is important. You can specify the following assertions; the order they appear in the following list is the order in which they should appear in your WS-Policy file:
<wsrm:InactivityTimeout>
—Number of milliseconds, specified with the Milliseconds
attribute, which defines an inactivity interval. After this amount of time, if the destination endpoint has not received a message from the source endpoint, the destination endpoint may consider the sequence to have terminated due to inactivity. The same is true for the source endpoint. By default, sequences never timeout. <wsrm:BaseRetransmissionInterval>
—Interval, in milliseconds, that the source endpoint waits after transmitting a message and before it retransmits the message if it receives no acknowledgment for that message. Default value is set by the SAF agent on the source endpoint’s WebLogic Server instance.<wsrm:ExponentialBackoff>
—Specifies that the retransmission interval will be adjusted using the exponential backoff algorithm. This element has no attributes. <wsrm:AcknowledgmentInterval>
—Maximum interval, in milliseconds, in which the destination endpoint must transmit a stand-alone acknowledgement. The default value is set by the SAF agent on the destination endpoint’s WebLogic Server instance.<beapolicy:Expires>
—Amount of time after which the reliable Web Service expires and does not accept any new sequence messages. The default value is to never expire. This element has a single attribute, Expires
, whose data type is an
XML Schema duration type. For example, if you want to set the expiration time to one day, use the following: <beapolicy:Expires Expires="P1D" />
.<beapolicy:QOS>
—Delivery assurance level, as described in Overview of Web Service Reliable Messaging. The element has one attribute, QOS
, which you set to one of the following values: AtMostOnce
, AtLeastOnce
, or ExactlyOnce
. You can also include the InOrder
string to specify that the messages be in order. The default value is ExactlyOnce InOrder
. This element is typically not set. The following example shows a simple Web Service reliable messaging WS-Policy file:
<?xml version="1.0"?>
<wsp:Policy
xmlns:wsrm="http://schemas.xmlsoap.org/ws/2005/02/rm/policy"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:beapolicy="http://www.bea.com/wsrm/policy"
>
<wsrm:RMAssertion>
<wsrm:InactivityTimeout
Milliseconds="600000" />
<wsrm:BaseRetransmissionInterval
Milliseconds="500" />
<wsrm:ExponentialBackoff />
<wsrm:AcknowledgementInterval
Milliseconds="2000" />
</wsrm:RMAssertion>
</wsp:Policy>
This section describes how to create the JWS file that implements the reliable Web Service.
The following JWS annotations are used in the JWS file that implements a reliable Web Service:
@weblogic.jws.Policy
—Required. See Using the @Policy Annotation.@javax.jws.Oneway
—Required only if you are using Web Service reliable messaging on its own, without also using the asynchronous request-response feature. See Using the @Oneway Annotation and Using the Asynchronous Features Together.@weblogic.jws.BufferQueue
—Optional. See Using the @BufferQueue Annotation.@weblogic.jws.ReliabilityBuffer
—Optional. See Using the @ReliabilityBuffer AnnotationThe following example shows a simple JWS file that implements a reliable Web Service; see the explanation after the example for coding guidelines that correspond to the Java code in bold.
package examples.webservices.reliable;
import javax.jws.WebMethod;
import javax.jws.WebService;import javax.jws.Oneway;
import weblogic.jws.WLHttpTransport;
import weblogic.jws.ReliabilityBuffer;
import weblogic.jws.BufferQueue;
import weblogic.jws.Policy;
/**
* Simple reliable Web Service.
*/
@WebService(name="ReliableHelloWorldPortType",
serviceName="ReliableHelloWorldService")
@WLHttpTransport(contextPath="ReliableHelloWorld",
serviceUri="ReliableHelloWorld",
portName="ReliableHelloWorldServicePort")
@Policy(uri="ReliableHelloWorldPolicy.xml",
direction=Policy.Direction.both,
attachToWsdl=true)
@BufferQueue(name="webservices.reliable.queue")
public class ReliableHelloWorldImpl {
@WebMethod()@Oneway()
@ReliabilityBuffer(retryCount=10, retryDelay="10 seconds")
public void helloWorld(String input) {
System.out.println(" Hello World " + input);
}
}
In the example, the ReliableHelloWorldPolicy.xml
file is attached to the Web Service at the class level, which means that the policy file is applied to all public operations of the Web Service. The policy file is applied only to the request Web Service message (as required by the reliable messaging feature) and it is attached to the WSDL file.
The JMS queue that WebLogic Server uses internally to enable the Web Service reliable messaging has a JNDI name of webservices.reliable.queue
, as specified by the @BufferQueue
annotation.
The helloWorld()
method has been marked with both the @WebMethod
and @Oneway
JWS annotations, which means it is a public operation called helloWorld
. Because of the @Policy
annotation, the operation can be invoked reliably. The Web Services runtime attempts to deliver reliable messages to the service a maximum of 10 times, at 10-second intervals, as described by the @ReliabilityBuffer
annotation. The message may require re-delivery if, for example, the transaction is rolled back or otherwise does not commit.
Use the @Policy
annotation in your JWS file to specify that the Web Service has a WS-Policy file attached to it that contains reliable messaging assertions.
See Use of WS-Policy Files for Web Service Reliable Messaging Configuration for descriptions of the two WS-Policy files (DefaultReliability.xml
and LongRunningReliability.xml
) included in WebLogic Server that you can use instead of writing your own.
You must follow these requirements when using the @Policy
annotation for Web Service reliable messaging:
Use the uri
attribute to specify the build-time location of the policy file, as follows:
@Policy(uri="ReliableHelloWorldPolicy.xml",
direction=Policy.Direction.both,
attachToWsdl=true)
The example shows that the ReliableHelloWorldPolicy.xml
file is located in the same directory as the JWS file.
policy:
prefix along with the name and path of the policy file. This syntax tells the jwsc
Ant task at build-time not to look for an actual file on the file system, but rather, that the Web Service will retrieve the WS-Policy file from WebLogic Server at the time the service is deployed. Use this syntax when specifying one of the pre-packaged WS-Policy files or when specifying a WS-Policy file that is packaged in a shared Java EE library. Note: | Shared Java EE libraries are useful when you want to share a WS-Policy file with multiple Web Services that are packaged in different Enterprise applications. As long as the WS-Policy file is located in the META-INF/policies or WEB-INF/policies directory of the shared Java EE library, you can specify the policy file in the same way as if it were packaged in the same archive at the Web Service. See
Creating Shared Java EE Libraries and Optional Packagesfor information on creating libraries and setting up your environment so the Web Service can find the policy files. |
http:
prefix along with the URL, as shown in the following example:@Policy(uri="http://someSite.com/policies/mypolicy.xml"
direction=Policy.Direction.both,
attachToWsdl=true)
You can also set the attachToWsd
attribute of the @Policy
annotation to specify whether the policy file should be attached to the WSDL file that describes the public contract of the Web Service. Typically you want to publicly publish the policy so that client applications know the reliable messaging capabilities of the Web Service. For this reason, the default value of this attribute is true
.
If you plan on invoking the reliable Web Service operation synchronously (or in other words, not using the asynchronous request-response feature), then the implementing method is required to be annotated with the @Oneway
annotation to specify that the method is one-way. This means that the method cannot return a value, but rather, must explicitly return void
.
Conversely, if the method is not annotated with the @Oneway
annotation, then you must invoke it using the asynchronous request-response feature. If you are unsure how the operation is going to be invoked, consider creating two flavors of the operation: synchronous and asynchronous.
See Invoking a Web Service Using Asynchronous Request-Response, and Using the Asynchronous Features Together..
Use the @BufferQueue
annotation to specify the JNDI name of the JMS queue which WebLogic Server uses to store reliable messages internally. The JNDI name is the one you configured when creating a JMS queue in step 4 in LongRunningReliability.xml WS-Policy File.
The @BufferQueue
annotation is optional; if you do not specify it in your JWS file then WebLogic Server uses a queue with a JNDI name of weblogic.wsee.DefaultQueue
. You must, however, still explicitly create a JMS queue with this JNDI name using the Administration Console.
Use this annotation to specify the number of times WebLogic Server should attempt to deliver the message from the JMS queue to the Web Service implementation (default 3) and the amount of time that the server should wait in between retries (default 5 seconds).
Use the retryCount
attribute to specify the number of retries and the retryDelay
attribute to specify the wait time. The format of the retryDelay
attribute is a number and then one of the following strings:
For example, to specify a retry count of 20 and a retry delay of two days, use the following syntax:
@ReliabilityBuffer(retryCount=20, retryDelay="2 days")
Note: | For the @ReliabilityBuffer annotation, the retry of a request is only triggered by a system level failure (for example, a JMS resource issue). Exceptions raised from the user code (JWS or handler chain) does not trigger a retry. |
If you are using the WebLogic client APIs, you must invoke a reliable Web Service from within a Web Service; you cannot invoke a reliable Web Service from a stand-alone client application.
The following example shows a simple JWS file for a Web Service that invokes a reliable operation from the service described in Programming Guidelines for the Reliable JWS File; see the explanation after the example for coding guidelines that correspond to the Java code in bold.
package examples.webservices.reliable;
import java.rmi.RemoteException;
import javax.jws.WebMethod;
import javax.jws.WebService;
import weblogic.jws.WLHttpTransport;import weblogic.jws.ServiceClient;
import weblogic.jws.ReliabilityErrorHandler;
import examples.webservices.reliable.ReliableHelloWorldPortType;
import weblogic.wsee.reliability.ReliabilityErrorContext;
import weblogic.wsee.reliability.ReliableDeliveryException;
@WebService(name="ReliableClientPortType",
serviceName="ReliableClientService")
@WLHttpTransport(contextPath="ReliableClient",
serviceUri="ReliableClient",
portName="ReliableClientServicePort")
public class ReliableClientImpl
{
@ServiceClient(
serviceName="ReliableHelloWorldService",
portName="ReliableHelloWorldServicePort")
private ReliableHelloWorldPortType port;
@WebMethod
public void callHelloWorld(String input, String serviceUrl)
throws RemoteException {
port.helloWorld(input);
System.out.println(" Invoked the ReliableHelloWorld.helloWorld operation reliably." );
}
@ReliabilityErrorHandler(target="port")
public void onReliableMessageDeliveryError(ReliabilityErrorContext ctx) {
ReliableDeliveryException fault = ctx.getFault();
String message = null;
if (fault != null) {
message = ctx.getFault().getMessage();
}
String operation = ctx.getOperationName();
System.out.println("Reliable operation " + operation + " may have not invoked. The error message is " + message);
}
}
Follow these guidelines when programming the JWS file that invokes a reliable Web Service; code snippets of the guidelines are shown in bold in the preceding example:
@ServiceClient
and @ReliabitliyErrorHandler
JWS annotations:import weblogic.jws.ServiceClient;
import weblogic.jws.ReliabilityErrorHandler;
<clientgen>
child element of the jwsc
Ant task, of the port type of the reliable Web Service you want to invoke. The stub package is specified by the packageName
attribute of <clientgen>
, and the name of the stub is determined by the WSDL of the invoked Web Service.import examples.webservices.reliable.ReliableHelloWorldPortType;
import weblogic.wsee.reliability.ReliabilityErrorContext;
import weblogic.wsee.reliability.ReliableDeliveryException;
@ServiceClient
JWS annotation to specify the name and port of the reliable Web Service you want to invoke. You specify this annotation at the field-level on a private variable, whose data type is the JAX-RPC port type of the Web Service you are invoking. @ServiceClient(
serviceName="ReliableHelloWorldService",
portName="ReliableHelloWorldServicePort")
private ReliableHelloWorldPortType port;
@ServiceClient
annotation, invoke the reliable operation:
port.helloWorld(input);
Because the operation has been marked one-way, it does not return a value.
@weblogic.jws.ReliabilityErrorHandler
annotation:@ReliabilityErrorHandler(target="port")
public void onReliableMessageDeliveryError(ReliabilityErrorContext ctx) {
ReliableDeliveryException fault = ctx.getFault();
String message = null;
if (fault != null) {
message = ctx.getFault().getMessage();
}
String operation = ctx.getOperationName();
System.out.println("Reliable operation " + operation + " may have not invoked. The error message is " + message);
}
This method takes ReliabilityErrorContext
as its single parameter and returns void
.
See weblogic.jws.ReliabilityErrorHandler for details about programming this error-handling method.
When programming the client Web Service, be sure you do not:
@ReliabilityErrorHandler
) or use any reliable messaging assertions in the associated WS-Policy files.wsdlLocation
attribute of the @ServiceClient
annotation. This is because the runtime retrieval of the specified WSDL might not succeed, thus it is better to let WebLogic Server use a local WSDL file instead.
WebLogic Server provides a utility class for use with the Web Service Reliable Messaging feature. Use this class to perform common tasks such as set configuration options, get the sequence id, and terminate a reliable sequence. Some of these tasks are performed in the reliable Web Service, some are performed in the Web Service that invokes the reliable Web Service.
See weblogic.wsee.reliability.WsrmUtils for details.
To update a build.xml
file to generate the JWS file that invokes the operation of a reliable Web Service, add taskdefs
and a build-reliable-client
targets that look something like the following; see the description after the example for details:
<taskdef name="jwsc"
classname="weblogic.wsee.tools.anttasks.JwscTask" />
<target name="build-reliable-client">
<jwsc
enableAsyncService="true"
srcdir="src"
destdir="${client-ear-dir}" >
<jws file="examples/webservices/reliable/ReliableClientImpl.java">
<clientgen
wsdl="http://${wls.destination.host}:${wls.destination.port}/ReliableHelloWorld/ReliableHelloWorld?WSDL"
packageName="examples.webservices.reliable"/>
</jws>
</jwsc>
</target>
Use the taskdef
Ant task to define the full classname of the jwsc
Ant tasks.
Update the jwsc
Ant task that compiles the client Web Service to include a <clientgen>
child element of the <jws>
element so as to generate and compile the JAX-RPC stubs for the deployed ReliableHelloWorld
Web Service. The jwsc
Ant task automatically packages them in the generated WAR file so that the client Web Service can immediately access the stubs. You do this because the ReliableClientImpl
JWS file imports and uses one of the generated classes.
WebLogic Server supports production redeployment, which means that you can deploy a new version of an updated reliable WebLogic Web Service alongside an older version of the same Web Service.
WebLogic Server automatically manages client connections so that only new client requests are directed to the new version. Clients already connected to the Web Service during the redeployment continue to use the older version of the service until they complete their work, at which point WebLogic Server automatically retires the older Web Service. If the client is connected to a reliable Web Service, its work is considered complete when the existing reliable messaging sequence is explicitly ended by the client or because of a time-out.
For additional information about production redployment and Web Service clients, see Client Considerations When Redeploying a Web Service.
Client applications that invoke reliable Web Services might not invoke the operation directly, but rather, use a proxy server. Reasons for using a proxy include the presence of a firewall or the deployment of the invoked Web Service to a cluster.
In this case, the WebLogic Server instance that hosts the invoked Web Service must be configured with the address and port of the proxy server. If your Web Service is deployed to a cluster, you must configure every server in the cluster.
weblogic-wsee-proxy-channel-
XXX
, where XXX
refers to the protocol. For example, to create a network channel for HTTPS, call it weblogic-wsee-proxy-channel-https
. See Configure Custom Network Channels for general information about creating a network channel.
![]() ![]() ![]() |