Search code examples
javaservletswebsphere

How to get WebSphere ConfigService in a servlet?


I have a servlet in an enterprise application on WebSphere Application Server 7.
I want the servlet to read configuration parameters from a custom resource environment provider.

This particular environment is not WebSphere Portal, and I'm not using Spring, but I'm trying to adapt the code from this page about using resource envirnment providers with Spring:
http://blogs.perficient.com/portals/2012/03/28/using-wps-style-resource-environment-providers-with-spring

I'm having a problem with the first line of my test method:

com.ibm.websphere.management.configservice.ConfigService service
    =com.ibm.websphere.management.configservice.ConfigServiceFactory.getConfigService();

The getConfigService() method in this line always returns null in my servlet. The method doesn't throw any exceptions, and no errors appear in the server logs; it just returns null.
(Note I show package names in the above code for clarity. In the real code, I import the relevant classes.)

How do I get a ConfigService object in my servlet?

The ConfigServiceFactory class also has a createConfigService(boolean enable, java.util.Properties props) method, but the Javadoc doesn't say what's expected for its arguments, and I can't find any examples using it.

Edit:

I've tried using a ConfigServiceProxy as per http://www-01.ibm.com/support/docview.wss?uid=swg21411254, as suggested by Magic Wand, and can't get that to work either.
Details of the problems follow. Does anyone know how to make this work?

Point 5 near the bottom of the page about using ConfigServiceProxy says:

Modify the properties connectProps.setProperty(AdminClient.CONNECTOR_HOST, "localhost"); and connectProps.setProperty(AdminClient.CONNECTOR_PORT, "8880") if needed.

But it doesn't say how to determine the correct host and port, and I haven't found that information elsewhere, so I'm just guessing.

To find host names and ports, I've logged into the WebSphere Integrated Solutions Console, gone to Servers -> Server Types -> WebSphere application servers, clicked the app server my servlet runs on, and clicked "Ports".

Here's the part of my code that tries to get a ConfigServiceProxy, in which I change only the host name and port number depending on which port I'm trying:

 Properties connectProps = new Properties();
    connectProps.setProperty(AdminClient.CONNECTOR_TYPE,
        AdminClient.CONNECTOR_TYPE_SOAP);
    connectProps.setProperty(AdminClient.CONNECTOR_HOST,"localhost");
    connectProps.setProperty(AdminClient.CONNECTOR_PORT,"9634");
    AdminClient adminClient=AdminClientFactory.createAdminClient(connectProps);
    ConfigService service=new ConfigServiceProxy(adminClient);

First, I tried the only port for which the host is "localhost", because that's what the linked example uses. The port name for that is "IPC_CONNECTOR_ADDRESS", and the port number is 9634.
This produces an exception on calling AdminClientFactory.createAdminClient. The stack trace starts with:

com.ibm.websphere.management.exception.ConnectorException: ADMC0016E: The system cannot create a SOAP connector to connect to host localhost at port 9634.
    at com.ibm.websphere.management.AdminClientFactory.createAdminClientPrivileged(AdminClientFactory.java:634)
    at com.ibm.websphere.management.AdminClientFactory.access$000(AdminClientFactory.java:126)
    at com.ibm.websphere.management.AdminClientFactory$1.run(AdminClientFactory.java:209)
    at com.ibm.ws.security.util.AccessController.doPrivileged(AccessController.java:63)
    at com.ibm.websphere.management.AdminClientFactory.createAdminClient(AdminClientFactory.java:205)
    at com.isw.ResourceEnvironmentProviderPlaceHolderConfigurer.getConfigService(ResourceEnvironmentProviderPlaceHolderConfigurer.java:113)

And the nested causes are:

Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:56)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:39)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:527)
    at com.ibm.websphere.management.AdminClientFactory.createAdminClientPrivileged(AdminClientFactory.java:456)
    ... 38 more
Caused by: com.ibm.websphere.management.exception.ConnectorNotAvailableException: [SOAPException: faultCode=SOAP-ENV:Client; msg=Connection reset; targetException=java.net.SocketException: Connection reset]
    at com.ibm.ws.management.connector.soap.SOAPConnectorClient.reconnect(SOAPConnectorClient.java:422)
    at com.ibm.ws.management.connector.soap.SOAPConnectorClient.(SOAPConnectorClient.java:222)
    ... 43 more
Caused by: [SOAPException: faultCode=SOAP-ENV:Client; msg=Connection reset; targetException=java.net.SocketException: Connection reset]
    at org.apache.soap.transport.http.SOAPHTTPConnection.send(SOAPHTTPConnection.java:479)
    at org.apache.soap.rpc.Call.WASinvoke(Call.java:451)
    at com.ibm.ws.management.connector.soap.SOAPConnectorClient$4.run(SOAPConnectorClient.java:372)
    at com.ibm.ws.security.util.AccessController.doPrivileged(AccessController.java:118)
    at com.ibm.ws.management.connector.soap.SOAPConnectorClient.reconnect(SOAPConnectorClient.java:365)
    ... 44 more

Next, I tried a port named "SOAP_CONNECTOR_ADDRESS", where the host is the server's fully qualified domain name, and the port number is 8881, so the lines setting host and port look like:

 connectProps.setProperty(AdminClient.CONNECTOR_HOST,"server.company.net.au");
    connectProps.setProperty(AdminClient.CONNECTOR_PORT,"9634");

This produced an exception on trying to construct the ConfigServiceProxy. The stack trace starts with these lines, and has no cause:

javax.management.InstanceNotFoundException: WebSphere:process=InfraCluster_server1,type=ConfigService,*
    at com.ibm.websphere.management.configservice.ConfigServiceProxy.(ConfigServiceProxy.java:67)
    at com.isw.ResourceEnvironmentProviderPlaceHolderConfigurer.getConfigService(ResourceEnvironmentProviderPlaceHolderConfigurer.java:114)
    at com.isw.ResourceEnvironmentProviderPlaceHolderConfigurer.loadEnvironmentProviderProperties(ResourceEnvironmentProviderPlaceHolderConfigurer.java:205)
    at com.isw.insight.client.REPTest.doGet(REPTest.java:50)

Finally, I tried a port named "WC_adminhost", where the host is "*" and the port is 9062. I've tried this both with the host name set to "localhost" and the server's fully qualified domain name, and both fail with the same exception.
Like the IPC_CONNECTOR_ADDRESS port, this produces an exception on calling AdminClientFactory.createAdminClient. The stack trace starts with:

com.ibm.websphere.management.exception.ConnectorException: ADMC0016E: The system cannot create a SOAP connector to connect to host server.company.net.au at port 9062.
    at com.ibm.websphere.management.AdminClientFactory.createAdminClientPrivileged(AdminClientFactory.java:634)
    at com.ibm.websphere.management.AdminClientFactory.access$000(AdminClientFactory.java:126)
    at com.ibm.websphere.management.AdminClientFactory$1.run(AdminClientFactory.java:209)
    at com.ibm.ws.security.util.AccessController.doPrivileged(AccessController.java:63)
    at com.ibm.websphere.management.AdminClientFactory.createAdminClient(AdminClientFactory.java:205)
    at com.isw.ResourceEnvironmentProviderPlaceHolderConfigurer.getConfigService(ResourceEnvironmentProviderPlaceHolderConfigurer.java:113)

And the nested causes are:

Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:56)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:39)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:527)
    at com.ibm.websphere.management.AdminClientFactory.createAdminClientPrivileged(AdminClientFactory.java:456)
    ... 36 more
Caused by: com.ibm.websphere.management.exception.ConnectorNotAvailableException: [SOAPException: faultCode=SOAP-ENV:Client; msg=Error opening socket: java.net.ConnectException: Connection refused; targetException=java.lang.IllegalArgumentException: Error opening socket: java.net.ConnectException: Connection refused]
    at com.ibm.ws.management.connector.soap.SOAPConnectorClient.reconnect(SOAPConnectorClient.java:422)
    at com.ibm.ws.management.connector.soap.SOAPConnectorClient.(SOAPConnectorClient.java:222)
    ... 41 more
Caused by: [SOAPException: faultCode=SOAP-ENV:Client; msg=Error opening socket: java.net.ConnectException: Connection refused; targetException=java.lang.IllegalArgumentException: Error opening socket: java.net.ConnectException: Connection refused]
    at org.apache.soap.transport.http.SOAPHTTPConnection.send(SOAPHTTPConnection.java:475)
    at org.apache.soap.rpc.Call.WASinvoke(Call.java:451)
    at com.ibm.ws.management.connector.soap.SOAPConnectorClient$4.run(SOAPConnectorClient.java:372)
    at com.ibm.ws.security.util.AccessController.doPrivileged(AccessController.java:118)
    at com.ibm.ws.management.connector.soap.SOAPConnectorClient.reconnect(SOAPConnectorClient.java:365)
    ... 42 more

Solution

  • I couldn't determine why ConfigServiceFactory.getConfigService() always returns null, but I have managed to obtain a working ConfigService instance using AdminClient and ConfigServiceProxy based on the code from http://www-01.ibm.com/support/docview.wss?uid=swg21411254.

    The basic code is this, with the host name and port number possibly changing depending on the server configuration:

    Properties connectProps = new Properties();
    connectProps.setProperty(AdminClient.CONNECTOR_TYPE,
        AdminClient.CONNECTOR_TYPE_SOAP);
    connectProps.setProperty(AdminClient.CONNECTOR_HOST,"localhost");
    connectProps.setProperty(AdminClient.CONNECTOR_PORT,"8880");
    AdminClient adminClient=AdminClientFactory.createAdminClient(connectProps);
    ConfigService service=new ConfigServiceProxy(adminClient);

    The IBM support document doesn't say how to determine the correct host and port. Here is some detail about that:

    Host name "localhost" and port number 8880 are the defaults when WebSphere Application Server is configured as a single server with no deployment manager. In a clustered environment, at least the port number will be different for each application server.

    In my clustered test environment, I first checked the ports listed for each application server in the WebSphere Integrated Solutions Console (ISC). None of those were correct.

    I subsequently found (via Google searching) that each server has several files controlling the actual ports used.
    These files are found on each application server's file system in ${USER_INSTALL_ROOT}/properties.
    The value of ${USER_INSTALL_ROOT} can be found in "Environment -> Websphere variables" in the ISC.
    The complete directory path on Linux is typically /opt/IBM/WebSphere/AppServer/profiles/AppSrv01/properties.
    In that directory, the correct host name and port can be found in the file "wsadmin.properties".

    As well as using the correct host and port, my environment also required username/password authentication for the SOAP connection. This can be done in two ways.

    The credentials can be set in code, with these two lines added to the above code before calling AdminClientFactory.createAdminClient:

    connectProps.setProperty(AdminClient.USERNAME,"username");
    connectProps.setProperty(AdminClient.PASSWORD,"password");

    This requires the password in plain-text, which is not recommended.

    Alternatively, credentials can be set in the "soap.client.props" file in the same directory as the "wsadmin.properties" file.
    In "soap.client.props" on every application server on which the code runs, these properties must be set:

    com.ibm.SOAP.securityEnabled=true
    com.ibm.SOAP.loginUserid=username
    com.ibm.SOAP.loginPassword=password

    Note the com.ibm.SOAP.securityEnabled property in particular: its value must be true for the username and password to be used.

    After setting these properties and saving the file on each server, use the "PropFilePasswordEncoder" command in an operating-system console to encode the password.
    I used the following documentation for this command:
    http://publib.boulder.ibm.com/infocenter/wsdoc400/v6r0/index.jsp?topic=/com.ibm.websphere.iseries.doc/info/ae/ae/qshpropenc.htm

    The actual command used on my application server was:
    /opt/IBM/WebSphere/AppServer/bin/PropFilePasswordEncoder.sh /opt/IBM/WebSphere/AppServer/profiles/AppSrv01/properties/soap.client.props com.ibm.SOAP.loginPassword
    This only encodes the password for basic authentication, as my configuration doesn't use SSL for SOAP.

    After all this, my code works.
    I did not have to restart any servers at any point in the above process.