-
Language:
English
-
Language:
English
Red Hat Training
A Red Hat training course is available for Red Hat JBoss Operations Network
11. Example: Writing a Custom Java Client
As alluded to in Chapter 1, Understanding How Scripts Work with the JBoss ON Server and CLI, the clients in JBoss ON use either the JBoss Remoting framework or the JBoss ON remote APIs to access server functionality. The JBoss ON CLI is essentially a Java skin over the remote API. Any application written in Java or a JVM-compatible language can access the JBoss ON remote API.
This example creates an LDAP integration for LDAP group-based authorization for JBoss ON. The sample Java class pulls in the authorization and search classes from the JBoss ON API, and then the script starts a simple synchronization service that maps the LDAP groups and users to the JBoss ON roles and users.
Note
LDAP-based group authorization is already configured in JBoss ON. This client is simply used as an example to show how a remote Java client can interact with the JBoss ON server.
11.1. Creating SampleLdapClientMain.class
This Java class uses the JBoss ON API for users, permissions, roles, and searching and sorting resource entries. The class then sets up a mapping between the LDAP database and the JBoss ON database, so that the user and role information in each is synchronized.
The JBoss ON CLI exposes a number of libraries, including domain classes for searching and remote classes for handling resources. The
SampleLdapClientMain.java
file requires these remote client JARs to be in its classpath:
cliRoot/rhq-remoting-cli-3.1.2.GA/lib/rhq-remoting-client-api-4.4.0-SNAPSHOT.jar
cliRoot/rhq-remoting-cli-3.1.2.GA/lib/rhq-core-domain-4.4.0-SNAPSHOT.jar
cliRoot/rhq-remoting-cli-3.1.2.GA/lib/persistence-api-1.0.jar
cliRoot/rhq-remoting-cli-3.1.2.GA/lib/rhq-enterprise-server-4.4.0-SNAPSHOT-client.jar
cliRoot/rhq-remoting-cli-3.1.2.GA/lib/hibernate-annotations-3.2.1.GA.jar
Example 1, “SampleLdapClientMain.java” is annotated to show what each step of the class is doing.
Example 1. SampleLdapClientMain.java
package org.rhq.sample.client.java.ldap; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.authz.Permission; import org.rhq.core.domain.authz.Role; import org.rhq.core.domain.criteria.ResourceCriteria; import org.rhq.core.domain.criteria.ResourceGroupCriteria; import org.rhq.core.domain.criteria.RoleCriteria; import org.rhq.core.domain.resource.Resource; import org.rhq.core.domain.resource.group.ResourceGroup; import org.rhq.core.domain.util.PageList; import org.rhq.enterprise.clientapi.RemoteClient; import org.rhq.enterprise.server.auth.SubjectManagerRemote; import org.rhq.enterprise.server.authz.RoleManagerRemote; import org.rhq.enterprise.server.resource.ResourceManagerRemote; import org.rhq.enterprise.server.resource.group.ResourceGroupManagerRemote; /** * This sample program utilizes the RHQ Remote API via a Java Client. * * The RHQ CLI is the preferred remote client approach for script-based clients. Programmatic Java clients * can utilize the Remote API via the same mechanism used by the CLI, making use of ClientMain object, as * done in this sample. This is the recommended mechanism although a remote Java client could also use the * remote API exposed as WebServices. * * @author Jay Shaughnessy */ public class SampleLdapClientMain { // A remote session always starts with a login, define default user/password/server/port private static String username = "rhqadmin"; private static String password = "rhqadmin"; private static String host = "localhost"; private static int port = 7080; /** * This is a standalone remote client but calls to the remote API could be embedded into another application. */ public static void main(String[] args) { if (args.length > 0) { if ((args.length != 2) && (args.length != 4)) { System.out .println("\nUsage: SampleLdapClientMain [ [ username password ] | [username password host port] ]"); System.out.println("\n\nDefault credentials: rhqadmin/rhqadmin"); System.out.println("\n\nDefault host: determined from wsconsume of WSDL"); return; } else { username = args[0]; password = args[1]; if (args.length == 4) { host = args[2]; port = Integer.valueOf(args[3]); } } } LdapClient ldapClient = null; try { ldapClient = new LdapClient(); ldapClient.synchLdapJbasManagers(); } catch (Throwable t) { System.out.println("Error: " + t); t.printStackTrace(); } finally { if (null != ldapClient) { // clean up the session by logging out from the RHQ server ldapClient.logout(); } } } /** * The LdapClient interacts with the RHQ Server to help synchronize a (fake) LDAP server with RHQ. */ public static class LdapClient { // group containing all jbas resources private static final String JBAS_GROUP = "jbas-resource-group"; // role for jbas managers private static final String JBAS_MANAGER_ROLE = "jbas-manager-role"; // the users that should be assigned the JBAS_MANAGER_ROLE private static final List<String> JBAS_MANAGERS = new ArrayList<String>(); // the prmissions that should be assigned the JBAS_MANAGER_ROLE private static final Set<Permission> JBAS_MANAGER_PERMISSIONS = new HashSet<Permission>(); // jbas AS Server resource type (note, this picks up AS4 and AS5 resources as they share the same type name) private static final String JBAS_SERVER_NAME = "JBossAS Server"; /* The Remote API offers different remote "managers" roughly broken down by subsystem/function * Below are the managers needed by this client, there are several others that offer * interfaces into areas such as operations, alerting, content, etc. See the API. */ private ResourceGroupManagerRemote resourceGroupManager; private ResourceManagerRemote resourceManager; private RoleManagerRemote roleManager; private SubjectManagerRemote subjectManager; /* This represents the RHQ user that is logged in and making the remote calls. This user must * already exist. For the work being done here the user must also have SECURITY_MANAGER permissions. */ private Subject subject; /* This is the object through which we access the remote API */ private RemoteClient remoteClient; static { // add some fake users since we're not actually hooked into an ldap server JBAS_MANAGERS.add("mgr-1"); JBAS_MANAGERS.add("mgr-2"); // add some permissions since we're not actually hooked into an ldap server JBAS_MANAGER_PERMISSIONS.addAll(Permission.RESOURCE_ALL); } public LdapClient() throws Exception { this.remoteClient = new RemoteClient(null, host, port); this.subject = remoteClient.login(username, password); this.resourceGroupManager = this.remoteClient.getResourceGroupManager(); this.resourceManager = this.remoteClient.getResourceManager(); this.roleManager = this.remoteClient.getRoleManager(); this.subjectManager = this.remoteClient.getSubjectManager(); } /* * This method simulates a sync between an Ldap server that has defined a group of JBAS managers * and wants to associate them with a role allowing jbas management. Meaning, a role that * has the proper permissions and is associated with the jbas resources. */ private void synchLdapJbasManagers() throws Exception { // create the jbas manager role if necessary // use a criteria search with a name filter to look for the role RoleCriteria roleCriteria = new RoleCriteria(); roleCriteria.addFilterName(JBAS_MANAGER_ROLE); PageList<Role> jbasManagerRoles = roleManager.findRolesByCriteria(subject, roleCriteria); Role jbasManagerRole; if (1 == jbasManagerRoles.size()) { jbasManagerRole = jbasManagerRoles.get(0); } else { // if it doesn't exist, create it jbasManagerRole = new Role(JBAS_MANAGER_ROLE); jbasManagerRole = roleManager.createRole(subject, jbasManagerRole); } // ensure the proper permissions are granted to the role by using an update jbasManagerRole.setPermissions(JBAS_MANAGER_PERMISSIONS); roleManager.updateRole(subject, jbasManagerRole); // create, populate and associate the jbas group if necessary ResourceGroupCriteria resourceGroupCriteria = new ResourceGroupCriteria(); resourceGroupCriteria.addFilterName(JBAS_GROUP); PageList<ResourceGroup> jbasGroups = resourceGroupManager.findResourceGroupsByCriteria(subject, resourceGroupCriteria); ResourceGroup jbasGroup; if (1 == jbasGroups.size()) { jbasGroup = jbasGroups.get(0); } else { jbasGroup = new ResourceGroup(JBAS_GROUP); jbasGroup = resourceGroupManager.createResourceGroup(subject, jbasGroup); // Ensure the group is recursive to make all the children available. // In this case a specific method is available, so a general update call is not needed. resourceGroupManager.setRecursive(subject, jbasGroup.getId(), true); } // Now find all of the JBAS server resources by adding a criteria filter on resource type name ResourceCriteria resourceCriteria = new ResourceCriteria(); resourceCriteria.addFilterResourceTypeName(JBAS_SERVER_NAME); PageList<Resource> jbasServers = resourceManager.findResourcesByCriteria(subject, resourceCriteria); if (!jbasServers.isEmpty()) { int[] jbasServerIds = new int[jbasServers.size()]; int i = 0; for (Resource jbasServer : jbasServers) { jbasServerIds[i++] = jbasServer.getId(); } // ..and add them to the group which will be associated with the manager role resourceGroupManager.addResourcesToGroup(subject, jbasGroup.getId(), jbasServerIds); } // Now, associate the mixed group of Jbas servers to the manager role roleManager.addResourceGroupsToRole(subject, jbasManagerRole.getId(), new int[] { jbasGroup.getId() }); // sync managers with the role // 1. remove obsolete managers roleCriteria = new RoleCriteria(); roleCriteria.addFilterId(jbasManagerRole.getId()); // add a fetch criteria to the criteria object to get the optionally returned subjects for the role. roleCriteria.fetchSubjects(true); jbasManagerRole = roleManager.findRolesByCriteria(subject, roleCriteria).get(0); Set<Subject> subjects = jbasManagerRole.getSubjects(); if ((null != subjects) && !subjects.isEmpty()) { for (Subject subject : subjects) { if (!JBAS_MANAGERS.contains(subject.getName())) { roleManager.removeSubjectsFromRole(subject, jbasManagerRole.getId(), new int[] { subject .getId() }); } } } // 2. add new managers, create subjects for the managers, if necessary Subject jbasManagerSubject; for (String jbasManager : JBAS_MANAGERS) { jbasManagerSubject = subjectManager.getSubjectByName(jbasManager); // add the required fields for a subject, note that we skip credentials since this is // simulating ldap if (null == jbasManagerSubject) { jbasManagerSubject = new Subject(); jbasManagerSubject.setName(jbasManager); jbasManagerSubject.setEmailAddress("jbas.manager@sample.com"); jbasManagerSubject.setFactive(true); jbasManagerSubject.setFsystem(false); jbasManagerSubject = subjectManager.createSubject(subject, jbasManagerSubject); } // Finally, make sure my current set of jbas managers is associated with the manager role. roleManager.addSubjectsToRole(subject, jbasManagerRole.getId(), new int[] { jbasManagerSubject.getId() }); } } public void logout() { if ((null != subjectManager) && (null != subject)) { try { subjectManager.logout(subject); } catch (Exception e) { // just suppress the exception, nothing else we can do } } } } }
11.2. Sample LDAP Script
The sample
.bat
script invokes the custom Java class.
@echo off rem =========================================================================== rem RHQ Remote Client LDAP Example Startup Script rem rem The following variables must be set rem rem RHQ_CLIENT_HOME The home directory of the RHQ Client Installation. The rem RHQ Client can be downloaded from the RHQ GUI under rem the Administration->Downloads menu. rem =========================================================================== rem ---------------------------------------------------------------------- rem Set Environment Variables rem ---------------------------------------------------------------------- set RHQ_CLIENT_HOME=*MUST BE SET* rem ---------------------------------------------------------------------- rem Prepare the classpath rem Add all jar files supplied by the RHQ remote client install rem ---------------------------------------------------------------------- set CLASSPATH=. call :append_classpath "%RHQ_CLIENT_HOME%\conf" for /R "%RHQ_CLIENT_HOME%\lib" %%G in ("*.jar") do ( call :append_classpath "%%G" ) rem ---------------------------------------------------------------------- rem Prepare the VM command line options to be passed in rem ---------------------------------------------------------------------- if not defined RHQ_CLIENT_JAVA_OPTS ( set RHQ_CLIENT_JAVA_OPTS=-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true ) rem ---------------------------------------------------------------------- rem Uncomment For debugging on port 9999 rem ---------------------------------------------------------------------- rem set RHQ_CLIENT_ADDITIONAL_JAVA_OPTS=-agentlib:jdwp=transport=dt_socket,address=9999,server=y,suspend=y rem ---------------------------------------------------------------------- rem Execute the VM which starts the CLIENT rem ---------------------------------------------------------------------- set CMD="%JAVA_HOME%\bin\java.exe" %RHQ_CLIENT_JAVA_OPTS% %RHQ_CLIENT_ADDITIONAL_JAVA_OPTS% -cp "%CLASSPATH%" org.rhq.sample.client.java.ldap.SampleLdapClientMain %RHQ_CLIENT_CMDLINE_OPTS% %* cmd.exe /S /C "%CMD%" goto :done rem ---------------------------------------------------------------------- rem CALL subroutine that appends the first argument to CLASSPATH rem ---------------------------------------------------------------------- :append_classpath set _entry=%1 if not defined CLASSPATH ( set CLASSPATH=%_entry:"=% ) else ( set CLASSPATH=%CLASSPATH%;%_entry:"=% ) goto :eof rem ---------------------------------------------------------------------- rem CALL subroutine that exits this script normally rem ---------------------------------------------------------------------- :done endlocal exit /B 0