13.6. Remote Java API
KieSession, TaskService and AuditLogService interfaces to the JMS and REST APIs.
KieSession or TaskService interface, without having to deal with the underlying transport and serialization details.
Important
KieSession, TaskSerivce and AuditLogService instances provided by the Remote Java API may "look" and "feel" like local instances of the same interfaces, please make sure to remember that these instances are only wrappers around a REST or JMS client that interacts with a remote REST or JMS API.
RuntimeException indicating that the REST call failed. This is different from the behaviour of a "real" (or local) instance of a KieSession, TaskSerivce and AuditLogService instance because the exception the local instances will throw will relate to how the operation failed. Also, while local instances require different handling (such as having to dispose of a KieSession), client instances provided by the Remote Java API hold no state and thus do not require any special handling.
TaskService.claim(taskId, userId) operation when called by a user who is not a potential owner), will now throw a RuntimeException instead when the requested operation fails on the server.
RemoteRestRuntimeEngineFactory or RemoteJmsRuntimeEngineFactory, both of which are instances of the RemoteRuntimeEngineFactory interface.
RemoteRuntimeEngineFactory instance: there are a number of different constructors for both the JMS and REST implementations that allow the configuration of such things as the base URL of the REST API, JMS queue location or timeout while waiting for responses.
Remote Java API Methods
RemoteRuntimeEngine RemoteRuntimeEngineFactory.newRuntimeEngine()- This method instantiates a new
RemoteRuntimeEngine(client) instance. KieSession RemoteRuntimeEngine.getKieSession()- This method instantiates a new (client)
KieSessioninstance. TaskService RemoteRuntimeEngine.getTaskService()- This method instantiates a new (client)
TaskServiceinstance. AuditLogService RemoteRuntimeEngine.getAuditLogService()- This method instantiates a new (client)
AuditLogServiceinstance.
Note
RemoteRuntimeEngineFactory.addExtraJaxbClasses(Collection<Class<?>> extraJaxbClasses ); method can only be called on builder now. This method adds extra classes to the classpath available to the serialization mechanisms. When passing instances of user-defined classes in a Remote Java API call, it's important to have added the classes via this method first so that the class instances can be serialized correctly.
13.6.1. The REST Remote Java RuntimeEngine Factory
RemoteRestRuntimeEngineFactory class is the starting point for building and configuring a new RuntimeEngine instance that can interact with the remote API. The main use for this class is to create builder instances of REST using the newBuilder() method. These builder instances are then used to either directly create a RuntimeEngine instance that will act as a client to the remote REST API or to create an instance of this factory. Illustrated in the table below are the various methods available in the RemoteRestRuntimeEngineBuilder class:
Table 13.7. RemoteRestRuntimeEngineBuilder Methods
| Method Name | Parameter Type | Description |
|---|---|---|
addDeploymentId | java.lang.String |
This is the name (id) of the deployment the
RuntimeEngine should interact with.
|
addUrl | java.net.URL |
This is the URL of the deployed business-central or BPMS instance.
For example:
http://localhost:8080/business-central/
|
addUserName | java.lang.String |
This is the user name needed to access the REST API.
|
addPassword | java.lang.String |
This is the password needed to access the REST API.
|
addProcessInstanceId | long |
This is the name (id) of the process the
RuntimeEngine should interact with.
|
addTimeout | int |
This maximum number of seconds to wait when waiting for a response from the server.
|
addExtraJaxbClasses | class |
This adds extra classes to the classpath available to the serialization mechanisms.
|
The following example illustrates how the Remote Java API can be used with the REST API.
import org.kie.api.runtime.KieSession;
import org.kie.api.task.TaskService;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.services.client.api.RemoteRestRuntimeEngineFactory;
import org.kie.services.client.api.RemoteRestRuntimeEngineFactoryBuilderImpl;
import org.kie.services.client.api.command.RemoteRuntimeEngine;
public void javaRemoteApiRestExample(String deploymentId, URL baseUrl, String user, String password) {
// The serverRestUrl should contain a URL similar to "http://localhost:8080/business-central/"
RemoteRestRuntimeEngineFactory remoteRestRuntimeEngineFactory = RemoteRestRuntimeEngineFactory.newBuilder()
.addDeploymentId(deploymentId)
.addUrl(url)
.addUserName(userName)
.addPassword(passWord)
.addTimeout(timeOut)
.build();
RemoteRuntimeEngine engine = remoteRestRuntimeEngineFactory.newRuntimeEngine();
// Create KieSession and TaskService instances and use them
KieSession ksession = engine.getKieSession();
TaskService taskService = engine.getTaskService();
// Each opertion on a KieSession, TaskService or AuditLogService (client) instance
// sends a request for the operation to the server side and waits for the response
// If something goes wrong on the server side, the client will throw an exception.
ProcessInstance processInstance
= ksession.startProcess("com.burns.reactor.maintenance.cycle");
long procId = processInstance.getId();
String taskUserId = user;
taskService = engine.getTaskService();
List<TaskSummary> tasks = taskService.getTasksAssignedAsPotentialOwner(user, "en-UK");
long taskId = -1;
for (TaskSummary task : tasks) {
if (task.getProcessInstanceId() == procId) {
taskId = task.getId();
}
}
if (taskId == -1) {
throw new IllegalStateException("Unable to find task for " + user + " in process instance " + procId);
}
taskService.start(taskId, taskUserId);
}
}
13.6.2. Custom Model Objects and Remote API
Note
Procedure 13.1. Accessing custom model objects using the Remote API
- Make sure that the custom model objects have been installed into the local Maven repository of the project that they are a part of (by a process of building the project successfully).
- If your client application is a Maven based project include the custom model objects project as a Maven dependency in the
pom.xmlconfiguration file of the client application.<dependency> <groupId>${groupid}</groupId> <artifactId>${artifactid}</artifactId> <version>${version}</version> </dependency>The value of these fields can be found in your Project Editor within Business Central: → on the main menu and then → from the perspective menu.- If the client application is NOT a Maven based project download the BPMS project, which includes the model classes, from Business Central by clicking on → . Add this jar file of the project on the build path of your client application so that the model object classes can be found and used.
- You can now use the custom model objects within your client application and invoke methods on them using the Remote API. The following listing shows an example of this, where
Personis a custom model object.import org.jbpm.services.task.utils.ContentMarshallerHelper; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.process.ProcessInstance; import org.kie.api.task.TaskService; import org.kie.api.task.model.Content; import org.kie.api.task.model.Task; import org.kie.services.client.api.RemoteRestRuntimeEngineFactory; import org.kie.services.client.api.command.RemoteRuntimeEngine; // the rest of the code here . . . // the following code in a method RemoteRestRuntimeEngineFactory factory = RemoteRestRuntimeEngineFactory.newBuilder().addUrl(url).addUserName(username).addPassword(password).addDeploymentId(deploymentId).addExtraJaxbClasses(new Class[]{UserDefinedClass.class, AnotherUserDefinedClass.class}).build(); runtimeEngine = factory.newRuntimeEngine(); ksession = runtimeEngine.getKieSession(); Map<String, Object> params = new HashMap<String, Object>(); Person person = new Person(); person.setName("anton"); params.put("pVar", person); ProcessInstance pi = kieSession.startProcess(PROCESS_ID, params);Make sure that your client application has imported the correct BPMS libraries for the example to work.
13.6.3. The JMS Remote Java RuntimeEngine Factory
RemoteJmsRuntimeEngineFactory works similar to the REST variation in that it is a starting point for building and configuring a new RuntimeEngine instance that can interact with the remote API. The main use for this class is to create builder instances of JMS using the newBuilder() method. These builder instances are then used to either directly create a RuntimeEngine instance that will act as a client to the remote JMS API or to create an instance of this factory. Illustrated in the table below are the various methods available for the RemoteJmsRuntimeEngineFactoryBuilder:
Table 13.8. RemoteJmsRuntimeEngineFactoryBuilder Methods
| Method Name | Parameter Type | Description |
|---|---|---|
addDeploymentId | java.lang.String |
This is the name (id) of the deployment the
RuntimeEngine should interact with.
|
addProcessInstanceId | long |
This is the name (id) of the process the
RuntimeEngine should interact with.
|
addUserName | java.lang.String |
This is the user name needed to access the JMS queues (in your application server configuration).
|
addPassword | java.lang.String |
This is the password needed to access the JMS queues (in your application server configuration).
|
addTimeout | int |
This maximum number of seconds to wait when waiting for a response from the server.
|
addExtraJaxbClasses | class |
This adds extra classes to the classpath available to the serialization mechanisms.
|
addRemoteInitialContext | javax.jms.InitialContext |
This is a remote InitialContext instance (created using JNDI) from the server.
|
addConnectionFactory | javax.jms.ConnectionFactory |
This is a
ConnectionFactory instance used to connect to the ksessionQueue or taskQueue.
|
addKieSessionQueue | javax.jms.Queue |
This is an instance of the
Queue for requests relating to the process instance.
|
addTaskServiceQueue | javax.jms.Queue |
This is an instance of the
Queue for requests relating to task service usage.
|
addResponseQueue | javax.jms.Queue |
This is an instance of the
Queue used to receive responses.
|
addJbossServerUrl | java.net.URL |
This is the url for the JBoss Server.
|
addJbossServerHostName | java.lang.String |
This is the hostname for the JBoss Server.
|
addHostName | java.lang.String |
This is the hostname of the JMS queues.
|
addJmsConnectorPort | int |
This is the port for the JMS Connector.
|
addKeystorePassword | java.lang.String |
This is the JMS Keystore Password.
|
addKeystoreLocation | java.lang.String |
This is the JMS Keystore Location.
|
addTruststorePassword | java.lang.String |
This is the JMS Truststore Password.
|
addTruststoreLocation | java.lang.String |
This is the JMS Truststore Location.
|
Example Usage
import org.kie.api.runtime.KieSession;
import org.kie.api.task.TaskService;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.services.client.api.RemoteJmsRuntimeEngineFactory;
import org.kie.services.client.api.command.RemoteRuntimeEngine;
public void javaRemoteApiJmsExample(String deploymentId, Long processInstanceId, String user, String password) {
// create a factory class with all the values
RemoteJmsRuntimeEngineFactory jmsRuntimeFactory =
RemoteJmsRuntimeEngineFactory.newBuilder()
.addDeploymentId(deploymentId)
.addProcessInstanceId(processInstanceId)
.addUserName(user)
.addPassword(password)
.addRemoteInitialContext(remoteInitialContext)
.addTimeout(3)
.addExtraJaxbClasses(MyType.class)
.useSsl(false)
.build();
RemoteRuntimeEngine engine = jmsRuntimeFactory.newRuntimeEngine();
// Create KieSession and TaskService instances and use them
KieSession ksession = engine.getKieSession();
TaskService taskService = engine.getTaskService();
// Each opertion on a KieSession, TaskService or AuditLogService (client) instance
// sends a request for the operation to the server side and waits for the response
// If something goes wrong on the server side, the client will throw an exception.
ProcessInstance processInstance
= ksession.startProcess("com.burns.reactor.maintenance.cycle");
long procId = processInstance.getId();
String taskUserId = user;
taskService = engine.getTaskService();
List<TaskSummary> tasks = taskService.getTasksAssignedAsPotentialOwner(user, "en-UK");
long taskId = -1;
for (TaskSummary task : tasks) {
if (task.getProcessInstanceId() == procId) {
taskId = task.getId();
}
}
if (taskId == -1) {
throw new IllegalStateException("Unable to find task for " + user + " in process instance " + procId);
}
taskService.start(taskId, taskUserId);
}
}
Sending and receiving JMS messages
sendAndReceiveJmsMessage example below creates the JaxbCommandsRequest instance and adds commands from the user. In addition, it retrieves JNDI context from the server, creates a JMS connection, etc.
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.api.task.model.TaskSummary;
public void sendAndReceiveJmsMessage() {
String USER = "charlie";
String PASSWORD = "ch0c0licious";
String DEPLOYMENT_ID = "test-project";
String PROCESS_ID_1 = "oompa-processing";
URL serverUrl;
try {
serverUrl = new URL("http://localhost:8080/business-central/");
} catch (MalformedURLException murle) {
logger.error("Malformed URL for the server instance!", murle);
return;
}
// Create JaxbCommandsRequest instance and add commands
Command<?> cmd = new StartProcessCommand(PROCESS_ID_1);
int oompaProcessingResultIndex = 0;
JaxbCommandsRequest req = new JaxbCommandsRequest(DEPLOYMENT_ID, cmd);
req.getCommands().add(new GetTaskAssignedAsPotentialOwnerCommand(USER));
int loompaMonitoringResultIndex = 1;
// Get JNDI context from server
InitialContext context = getRemoteJbossInitialContext(serverUrl, USER, PASSWORD);
// Create JMS connection
ConnectionFactory connectionFactory;
try {
connectionFactory = (ConnectionFactory) context.lookup("jms/RemoteConnectionFactory");
} catch (NamingException ne) {
throw new RuntimeException("Unable to lookup JMS connection factory.", ne);
}
// Setup queues
Queue sendQueue, responseQueue;
try {
sendQueue = (Queue) context.lookup("jms/queue/KIE.SESSION");
responseQueue = (Queue) context.lookup("jms/queue/KIE.RESPONSE");
} catch (NamingException ne) {
throw new RuntimeException("Unable to lookup send or response queue", ne);
}
// Send command request
Long processInstanceId = null; // needed if you're doing an operation on a PER_PROCESS_INSTANCE deployment
String humanTaskUser = USER;
JaxbCommandsResponse cmdResponse = sendJmsCommands(
DEPLOYMENT_ID, processInstanceId, humanTaskUser, req,
connectionFactory, sendQueue, responseQueue,
USER, PASSWORD, 5);
// Retrieve results
ProcessInstance oompaProcInst = null;
List<TaskSummary> charliesTasks = null;
for (JaxbCommandResponse<?> response : cmdResponse.getResponses()) {
if (response instanceof JaxbExceptionResponse) {
// something went wrong on the server side
JaxbExceptionResponse exceptionResponse = (JaxbExceptionResponse) response;
throw new RuntimeException(exceptionResponse.getMessage());
}
if (response.getIndex() == oompaProcessingResultIndex) {
oompaProcInst = (ProcessInstance) response.getResult();
} else if (response.getIndex() == loompaMonitoringResultIndex) {
charliesTasks = (List<TaskSummary>) response.getResult();
}
}
}Sending JMS commands
sendJmsCommands example below is a continuation of the previous example. It sets up user created classes and sends, receives, and extracts responses.
private JaxbCommandsResponse sendJmsCommands(String deploymentId, Long processInstanceId, String user,
JaxbCommandsRequest req, ConnectionFactory factory, Queue sendQueue, Queue responseQueue, String jmsUser,
String jmsPassword, int timeout) {
req.setProcessInstanceId(processInstanceId);
req.setUser(user);
Connection connection = null;
Session session = null;
String corrId = UUID.randomUUID().toString();
String selector = "JMSCorrelationID = '" + corrId + "'";
JaxbCommandsResponse cmdResponses = null;
try {
// setup
MessageProducer producer;
MessageConsumer consumer;
try {
if (jmsPassword != null) {
connection = factory.createConnection(jmsUser, jmsPassword);
} else {
connection = factory.createConnection();
}
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(sendQueue);
consumer = session.createConsumer(responseQueue, selector);
connection.start();
} catch (JMSException jmse) {
throw new RemoteCommunicationException("Unable to setup a JMS connection.", jmse);
}
JaxbSerializationProvider serializationProvider = new JaxbSerializationProvider();
// if necessary, add user-created classes here:
// xmlSerializer.addJaxbClasses(MyType.class, AnotherJaxbAnnotatedType.class);
// Create msg
BytesMessage msg;
try {
msg = session.createBytesMessage();
// serialize request
String xmlStr = serializationProvider.serialize(req);
msg.writeUTF(xmlStr);
// set properties
msg.setJMSCorrelationID(corrId);
msg.setIntProperty(SerializationConstants.SERIALIZATION_TYPE_PROPERTY_NAME, JaxbSerializationProvider.JMS_SERIALIZATION_TYPE);
Collection<Class<?>> extraJaxbClasses = serializationProvider.getExtraJaxbClasses();
if (!extraJaxbClasses.isEmpty()) {
String extraJaxbClassesPropertyValue = JaxbSerializationProvider
.classSetToCommaSeperatedString(extraJaxbClasses);
msg.setStringProperty(SerializationConstants.EXTRA_JAXB_CLASSES_PROPERTY_NAME, extraJaxbClassesPropertyValue);
msg.setStringProperty(SerializationConstants.DEPLOYMENT_ID_PROPERTY_NAME, deploymentId);
}
} catch (JMSException jmse) {
throw new RemoteCommunicationException("Unable to create and fill a JMS message.", jmse);
} catch (SerializationException se) {
throw new RemoteCommunicationException("Unable to deserialze JMS message.", se.getCause());
}
// send
try {
producer.send(msg);
} catch (JMSException jmse) {
throw new RemoteCommunicationException("Unable to send a JMS message.", jmse);
}
// receive
Message response;
try {
response = consumer.receive(timeout);
} catch (JMSException jmse) {
throw new RemoteCommunicationException("Unable to receive or retrieve the JMS response.", jmse);
}
if (response == null) {
logger.warn("Response is empty, leaving");
return null;
}
// extract response
assert response != null : "Response is empty.";
try {
String xmlStr = ((BytesMessage) response).readUTF();
cmdResponses = (JaxbCommandsResponse) serializationProvider.deserialize(xmlStr);
} catch (JMSException jmse) {
throw new RemoteCommunicationException("Unable to extract " + JaxbCommandsResponse.class.getSimpleName()
+ " instance from JMS response.", jmse);
} catch (SerializationException se) {
throw new RemoteCommunicationException("Unable to extract " + JaxbCommandsResponse.class.getSimpleName()
+ " instance from JMS response.", se.getCause());
}
assert cmdResponses != null : "Jaxb Cmd Response was null!";
} finally {
if (connection != null) {
try {
connection.close();
if( session != null ) {
session.close();
}
} catch (JMSException jmse) {
logger.warn("Unable to close connection or session!", jmse);
}
}
}
return cmdResponses;
} Configuration using an InitialContext instance
RemoteJmsRuntimeEngineFactory with an InitialContext instance as a parameter for Red Hat JBoss EAP 6, it is necessary to retrieve the (remote) InitialContext instance first from the remote server. The following code illustrates how to do this.
private InitialContext getRemoteJbossInitialContext(URL url, String user, String password) {
Properties initialProps = new Properties();
initialProps.setProperty(InitialContext.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
String jbossServerHostName = url.getHost();
initialProps.setProperty(InitialContext.PROVIDER_URL, "remote://"+ jbossServerHostName + ":4447");
initialProps.setProperty(InitialContext.SECURITY_PRINCIPAL, user);
initialProps.setProperty(InitialContext.SECURITY_CREDENTIALS, password);
for (Object keyObj : initialProps.keySet()) {
String key = (String) keyObj;
System.setProperty(key, (String) initialProps.get(key));
}
try {
return new InitialContext(initialProps);
} catch (NamingException e) {
throw new RemoteCommunicationException("Unable to create " + InitialContext.class.getSimpleName(), e);
}
}13.6.4. Supported Methods
RuntimeEngine, KieSession, TaskService and AuditLogService interfaces.
UnsupportedOperationException explaining that the called method is not available.
Table 13.9. Available process-related KieSession methods
| Returns | Method signature | Description |
|---|---|---|
void
| abortProcessInstance(long processInstanceId)
|
Abort the process instance
|
ProcessInstance
| getProcessInstance(long processInstanceId)
|
Return the process instance
|
ProcessInstance
| getProcessInstance(long processInstanceId, boolean readonly)
|
Return the process instance
|
Collection<ProcessInstance>
| getProcessInstances()
|
Return all (active) process instances
|
void
| signalEvent(String type, Object event)
|
Signal all (active) process instances
|
void
| signalEvent(String type, Object event, long processInstanceId)
|
Signal the process instance
|
ProcessInstance
| startProcess(String processId)
|
Start a new process and return the process instance (if the process instance has not immediately completed)
|
ProcessInstance
| startProcess(String processId, Map<String, Object> parameters);
|
Start a new process and return the process instance (if the process instance has not immediately completed)
|
Table 13.10. Available rules-related KieSession methods
| Returns | Method signature | Description |
|---|---|---|
Long
| getFactCount()
|
Return the total fact count
|
Object
| getGlobal(String identifier)
|
Return a global fact
|
void
| setGlobal(String identifier, Object value)
|
Set a global fact
|
Table 13.11. Available WorkItemManager methods
| Returns | Method signature | Description |
|---|---|---|
void
| abortWorkItem(long id)
|
Abort the work item
|
void
| completeWorkItem(long id, Map<String, Object> results)
|
Complete the work item
|
void | registerWorkItemHandler(String workItemName, WorkItemHandler handler) |
Register the work items
|
WorkItem
| getWorkItem(long workItemId)
|
Return the work item
|
Table 13.12. Available task operation TaskService methods
| Returns | Method signature | Description |
|---|---|---|
Long
| addTask(Task task, Map<String, Object> params)
|
Add a new task
|
void
| activate(long taskId, String userId)
|
Activate a task
|
void
| claim(long taskId, String userId)
|
Claim a task
|
void
| claimNextAvailable(String userId, String language)
|
Claim the next available task for a user
|
void
| complete(long taskId, String userId, Map<String, Object> data)
|
Complete a task
|
void
| delegate(long taskId, String userId, String targetUserId)
|
Delegate a task
|
void
| exit(long taskId, String userId)
|
Exit a task
|
void
| fail(long taskId, String userId, Map<String, Object> faultData)
|
Fail a task
|
void
| forward(long taskId, String userId, String targetEntityId)
|
Forward a task
|
void
| nominate(long taskId, String userId, List<OrganizationalEntity> potentialOwners)
|
Nominate a task
|
void
| release(long taskId, String userId)
|
Release a task
|
void
| resume(long taskId, String userId)
|
Resume a task
|
void
| skip(long taskId, String userId)
|
Skip a task
|
void
| start(long taskId, String userId)
|
Start a task
|
void
| stop(long taskId, String userId)
|
Stop a task
|
void
| suspend(long taskId, String userId)
|
Suspend a task
|
Table 13.13. Available task retrieval and query TaskService methods
| Returns | Method signature |
|---|---|
Task
| getTaskByWorkItemId(long workItemId)
|
Task
| getTaskById(long taskId)
|
List<TaskSummary>
| getTasksAssignedAsBusinessAdministrator(String userId, String language)
|
List<TaskSummary>
| getTasksAssignedAsPotentialOwner(String userId, String language)
|
List<TaskSummary>
| getTasksAssignedAsPotentialOwnerByStatus(String userId, List<Status>gt; status, String language)
|
List<TaskSummary>
| getTasksOwned(String userId, String language)
|
List<TaskSummary>
| getTasksOwnedByStatus(String userId, List<Status> status, String language)
|
List<TaskSummary>
| getTasksByStatusByProcessInstanceId(long processInstanceId, List<Status> status, String language)
|
List<TaskSummary>
| getTasksByProcessInstanceId(long processInstanceId)
|
Content
| getContentById(long contentId)
|
Attachment
| getAttachmentById(long attachId)
|
Table 13.14. Available AuditLogService methods
| Returns | Method signature |
|---|---|
List<ProcessInstanceLog>
| findProcessInstances()
|
List<ProcessInstanceLog>
| findProcessInstances(String processId)
|
List<ProcessInstanceLog>
| findActiveProcessInstances(String processId)
|
ProcessInstanceLog
| findProcessInstance(long processInstanceId)
|
List<ProcessInstanceLog>
| findSubProcessInstances(long processInstanceId)
|
List<NodeInstanceLog>
| findNodeInstances(long processInstanceId)
|
List<NodeInstanceLog>
| findNodeInstances(long processInstanceId, String nodeId)
|
List<VariableInstanceLog>
| findVariableInstances(long processInstanceId)
|
List<VariableInstanceLog>
| findVariableInstances(long processInstanceId, String variableId)
|
List<VariableInstanceLog>
| findVariableInstancesByName(String variableId, boolean onlyActiveProcesses)
|
List<VariableInstanceLog>
| findVariableInstancesByNameAndValue(String variableId, String value, boolean onlyActiveProcesses)
|
void
| clear()
|

Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.