Getting instance ip from weblogic cluster - java

I have a problem on how to get an instance URL within a cluster with weblogic.
Description:
We have 2 domains: X and Y.
In each domain I have 2 clusters: c01 and c02
In each cluster I have instances: s01,s02,s03,s04
In each instances I have our system which contains of several components, let’s call the components A,B,C and D. I want to make a REST call from A to D which are still in the same instance. How will we get the URL and port to this REST service programmatically?
The problem is that I am just getting the cluster URL when calling InetAddress or alike. I have also played around with MBean, but we are not sure it’s correct way to go since I wont have any user/pass to fill in for Enviroment object when creating the context.
We don’t want this as a build property since we don’t want to do builds for each different instance.
Env:
SpringIntegration
Weblogic 10.3.3
Jersey
Maven
Thanks

Solution:
Got it from an RuntimeServiceMBean:
service = new ObjectName(
"com.bea:Name=RuntimeService,Type=weblogic.management.mbeanservers.runtime.RuntimeServiceMBean");
InitialContext ctx = new InitialContext();
MBeanServer mBeanServer = (MBeanServer) ctx.lookup("java:comp/env/jmx/runtime");
ObjectName rt = (ObjectName) mBeanServer.getAttribute(service, "ServerRuntime");
listenAddress = (String) mBeanServer.getAttribute(rt, "ListenAddress");
server = listenAddress.substring(0, listenAddress.indexOf("/"));
port = (Integer)mBeanServer.getAttribute(rt, "ListenPort");

Related

Running Wildfly in Azure App Service, how to configure JGroups for session replication

Running Wildfly as Azure App Service was possible via a custom java app [https://azure.microsoft.com/en-us/documentation/articles/web-sites-java-custom-upload/]. However, app service nodes don't know the internal IP address while registering with JGroups. They always expose 127.0.0.1. In order to make JGroups cluster members communicate, we need a well known IP address of the node.
How can Wildfly determine the internal IP address of the host that it can use to register with JGroups cluster?
Per my experience, I think you can try to use Azure SDK for Java to get the internal IP address of the host from the WebSiteManagementClient.
Here is a sample code below for getting the internal IP address.
String userName = "<user-name>";
String password = "<password>";
String resourceGroupName = "<resource-group-name>";
String name = "<webapp-name>";
ServiceClientCredentials credentials = new BasicAuthenticationCredentials(userName, password);
WebSiteManagementClient webSiteManagementClient = new WebSiteManagementClientImpl(credentials);
HostingEnvironmentsOperations hostingEnvironmentsOperations = webSiteManagementClient.getHostingEnvironmentsOperations();
ServiceResponse<AddressResponse> serviceResponse = hostingEnvironmentsOperations.getHostingEnvironmentVips(resourceGroupName, name);
AddressResponse addressResponse = (AddressResponse) serviceResponse.getBody();
String internalIp = addressResponse.getInternalIpAddress();
To run the above sample, you need to add the dependent libraries into your Maven project, please see the dependencies below.
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-svc-mgmt-websites</artifactId>
<version>0.9.2</version>
</dependency>
More details for the key classes in the above sample code, please see below.
WebSiteManagementClient & WebSiteManagementClientImpl
HostingEnvironmentsOperations
AddressResponse
You could use a special keyword for the bind_addr, see [1] for details. E.g. bind_addr=match-address:192.168.1.* to try to pick an IP address on a given subnet.
[1] http://www.jgroups.org/manual/index.html#Transport
You could use Peter's code (above) to detect the available IP addresses, then set bind_addr in JGroups, e.g. like this:
InetAddress bind_addr; // detect address by using Azure's SDK
JChannel ch=new JChannel("config.xml");
TP transport=ch.getProtocolStack().getTransport();
transport.setBindAddress(bind_addr);
ch.connect("mycluster");
The important thing is that you need to set the bind address before connecting the channel.

Standalone ActiveMQ client on GlassFish

Hello guys i have such issue i make all thing like tutorial says. So now i want to lookup my Topics and connection factories that i configured but it do not see them. i make something like :
try {
Properties propertiesAMQ = new Properties();
propertiesAMQ.load(new FileInputStream("AMQ.properties"));
logger.info("Property file loaded succesfully...");
propertiesAMQ.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.activemq.jndi.ActiveMQInitialContextFactory");
propertiesAMQ.setProperty(Context.PROVIDER_URL,
"tcp://localhost:61616");
Context ctx = new InitialContext(propertiesAMQ);
javax.jms.TopicConnectionFactory factory = (javax.jms.TopicConnectionFactory) ctx
.lookup("amqpool");
javax.jms.Topic mytopic = (javax.jms.Topic) ctx.lookup("amqmsg")
}
And recieve NameNotFoundException. If i use name of connection factory such as "ConnectionFactory" it will be ok but then it will not see my Topic What i did wrong? Have u other examples of this subject? I'm using glassfish 3.0.1 and AMQ 5.5.0
Probably you are missing the namespace, you can look the exact name in the glassfish console, but most probably it should be;
javax.jms.Topic mytopic = (javax.jms.Topic) ctx.lookup("java:amqmsg")
How do you create your Topic resource? I had a similar problem and the solution was to create the Admin Resource Object using Glassfish command-line tool 'asadmin'. Creating it using the Glassfish admin console did not work (causing NameNotFoundException).
I ended up creating my Queu resource with the following command: 'create-admin-object –restype javax.jms.Queue –raname activemq-rar-5.7.0 –property PhysicalName=queueName queueName'

Consuming web service from a remote computer

I have a desktop application built with jdk 6 which publishes web services to be consumed by a web application. So far I've had no problem while both applications are running in the same physical computer, i can access the wsdl without any problem and the web application works with the desktop application just fine. The thing is I cannot access to the services from a remote computer in the same network. The two PCs are connected and can interact. If I run both applications in PC1, from PC2 I can use the webapp through
http://PC1:8080
I am currently publishing like this:
public Publicador(){
servicios= new Servicios();
Endpoint endpoint = Endpoint.publish("http://PC1:8686/servicios", servicios);
}
where PC1 is the name of the pc. From PC1, i can see the generated wsdl from the following address, and it's the one I used for the wsimport command:
http://PC1:8686/servicios?wsdl
But I cannnot from PC2.
Any ideas why it is not visible from outside PC1?
Incredible as it may seem, I found the simplest of answers... Instead of publishing as
Endpoint endpoint = Endpoint.publish("http://PC1:8686/servicios", servicios);
I published as
Endpoint endpoint = Endpoint.publish("http://0.0.0.0:8686/servicios", servicios);
and that solved it...
Another solution was to get the address to publish from a file, that worked too. I don't know why it didn't hardcoded... I ended up doing it like this:
Properties prop = new Properties();
InputStream is = null;
String currenDir = System.getProperty("user.dir");
String nombreArchivo = currenDir + File.separator + "ubicacion.PROPERTIES";
try {
is=new FileInputStream(nombreArchivo);
prop.load(is);
} catch(IOException ioe) {}
String pc = prop.getProperty("ServiciosWeb");
Endpoint endpoint = Endpoint.publish( pc, servicios);
}

Tomcat JMX - Connecting to server but cant find the MBean i want

I am trying to write a client utility that to connect to Tomcat via JMX and look at the status of the connection datasource.
I set the following VM arguments in $CATALINA_HOME/bin/setenv.bat and restarted Tomcat
set JAVA_OPTS=-Xms512M -Xmx1024M -XX:MaxPermSize=256M %JAVA_OPTS%
set CATALINA_OPTS=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false %CATALINA_OPTS%
I am not very familiar with JMX so i am just having a play with it to get the feel of it.
The utility i am writing will be running outside of Tomcat. I wrote the following test to try and access datasource Mbean object in Tomcat
but for some reason it is not finding it.
public class GuiMonitor {
public static void main(String[] args){
try{
JMXServiceURL url = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://localhost:9004/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
final List<MBeanServer> servers = new LinkedList<MBeanServer>();
servers.add(ManagementFactory.getPlatformMBeanServer());
servers.addAll(MBeanServerFactory.findMBeanServer(null));
System.out.println("MbeanServers " + servers.size());
for(final MBeanServer server : servers){
System.out.println("Server : " + server.getClass().getName());
}
MBeanServer mbsc = ManagementFactory.getPlatformMBeanServer();
System.out.println(mbsc.queryMBeans(null, null));
ObjectName on = new ObjectName("Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name=\"jdbc/appdb\"");
System.out.println("ObjectName : " + on.toString());
System.out.println(mbsc.getAttribute(on, "Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name=\"jdbc/appdb\""));
} catch (Exception e) {
e.printStackTrace();
}
}
}
I have a JSP page that i found on the internet which when i upload onto the webapps folder and run it, it displays all of the available
MBeans in Tomcat. The object string/name i used above came from the name that was reported on both the jsp page i used and Jconsole so it does exist.
The output to the above program is shown below
MbeanServers 2
Server : com.sun.jmx.mbeanserver.JmxMBeanServer
Server : com.sun.jmx.mbeanserver.JmxMBeanServer
[com.sun.management.OperatingSystem[java.lang:type=OperatingSystem], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Tenured Gen], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Perm Gen], java.util.logging.Logging[java.util.logging:type=Logging], sun.management.CompilationImpl[java.lang:type=Compilation], javax.management.MBeanServerDelegate[JMImplementation:type=MBeanServerDelegate], sun.management.MemoryImpl[java.lang:type=Memory], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Survivor Space], sun.management.RuntimeImpl[java.lang:type=Runtime], sun.management.GarbageCollectorImpl[java.lang:type=GarbageCollector,name=Copy], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Eden Space], sun.management.GarbageCollectorImpl[java.lang:type=GarbageCollector,name=MarkSweepCompact], sun.management.ThreadImpl[java.lang:type=Threading], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Perm Gen [shared-ro]], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Perm Gen [shared-rw]], sun.management.HotSpotDiagnostic[com.sun.management:type=HotSpotDiagnostic], sun.management.ClassLoadingImpl[java.lang:type=ClassLoading], sun.management.MemoryManagerImpl[java.lang:type=MemoryManager,name=CodeCacheManager], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Code Cache]]
ObjectName : Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name="jdbc/appdb"
javax.management.InstanceNotFoundException: Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name="jdbc/appdb"
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getMBean(DefaultMBeanServerInterceptor.java:1094)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getAttribute(DefaultMBeanServerInterceptor.java:662)
at com.sun.jmx.mbeanserver.JmxMBeanServer.getAttribute(JmxMBeanServer.java:638)
at com.bt.c21.c21mon.C21GuiMonitor.main(C21GuiMonitor.java:39)
A couple of questions
Is the URL correct? I know the port number is correct but i am not sure of the service name. The service name "jmxrmi" i am using on the URL is just one that i saw in one of the examples i have been looking at.
I have a feeling that this is connecting to a different MBeanServer. I suspect this because if you look at the output of mbsc.queryMBeans(null, null), there is nothing tomcat specific. What service name do i use for the Tomcat instance?
If the URL is correct then is the service name always jmxrmi? And why does it not find the "Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name=\"jdbc/appdb\"" entry?
I have seen a lot of examples of how to do this and most use a different method to get teh MbeanServer. A couple of examples i have seen are
ManagementFactory.getPlatformMBeanServer()
MBeanServerFactory.findMBeanServer(null)
getMBeanServerConnection()
As mentioned earlier, the utility i am writing is a normal java application that will be running outside of tomcat. Is there any other configuration that i have missed out? I have been looking at several examples and the majority talk about creating MBeans and there is usually references to Listeners. As i am not creating any new Mbeans but only reading the values of existing ones, do i need to configure a listener?
Edit
It seems that getPlatformMbeanServer() is not returning the correct JVM Instance. I tried the following
MBeanServerConnection conn = jmxc.getMBeanServerConnection();
System.out.println("Query2 : " + conn.queryMBeans(null, null));
And this does return some Tomcat specific values. But i am still unable to get the jdbc/appdb datasource.
krtek - I wont be able to use JMX Console as i plan to do it all manually with the intention of automating it.
Edit 2
Ok, i figured out what i was doing wrong. Initially i was trying to retrieve the values as
MBeanServerConnection conn = jmxc.getMBeanServerConnection();
ObjectName on = new ObjectName("Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name=\"jdbc/appdb\"");
mbsc.getAttribute(on, "Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name=\"jdbc/appdb\""));
The above is wrong because the second parameter for mbsc.getAttribute is supposed to be the attribute in the Mbean not the String name.
This gave me the correct attribute values
MBeanServerConnection conn = jmxc.getMBeanServerConnection();
ObjectName on = new ObjectName("Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name=\"jdbc/appdb\"");
mbsc.getAttribute(on, "numIdle")
And i also changed the MBeanServer i was using from getPlatformMbeanServer() to getMBeanserverConnection(). I must admit i dont quite understand the difference because since Tomcat is running on the same JVM as the one returned by getPlatformMbeanServer(). Does it mean that getPlatformMbeanServer() will only return sun specific Mbeans? and getMBeanserverConnection() will include both?
Thanks
That's because you are getting instance of JMX server for your client JVM, not the Tomcat one.
This is right:
JMXServiceURL url = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://localhost:9004/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
But you should continue with something like:
MBeanServerConnection conn = jmxc.getMBeanServerConnection();
Set result = conn.queryMBeans(null,
"Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource");
To test your query string use some tool like JMX console.

Tomcat: Change the Virtual hosts programmatically?

Tomcat offers a build in "Virtual Hosting" Support: An Engine/Web-Application can be configured to be responsible for a list of Domains. These Domains have to be put into the server.xml/context.xml files with a special xml directive.
=> Is there any possibility to change the Tomcat Configuration (in general) and especially the "Virtual Hosts" of a Web-Application/Engine programmatically?
For example if a new user signs up, I have to add his domain to the list of "accepted virtual hosts/domains". The only way I currently think of is changing the xml files via a script and then restart Tomcat.
Is there any way to add them add runtime via some Java-Methods programmatically?
Thank you very much!
Jan
Tomcat provides APIs to create new virtual host. To get access to the wrapper object needed for this, you need to implement a ContainerServlet. You can create virtual host like this,
Context context = (Context) wrapper.getParent();
Host currentHost = (Host) context.getParent();
Engine engine = (Engine) currentHost.getParent();
StandardHost host = new StandardHost();
host.setAppBase(appBase);
host.setName(domainName);
engine.addChild(host);
You need to make sure appBase directory exist and you have to find ways to persist the new host to the server.xml or you lose the host on restart.
However, this approach rarely works. If your users run their own apps, you really want run separate instances of Tomcat so you can sandbox the apps better. e.g. One app running out of memory doesn't kill all other apps.
If you provide the app, you can just use one host (defaultHost). You can get the domain name from Host header and do whatever domain-specific stuff in your code.
You shouldn't change the server environment programmatically and there are no reliable and standard ways to do this. Best is to do and keep it all on the webapp side. To start, a Filter is perfectly suitable for this. Store the names somewhere in a database table or a properties file which you cache in the application scope. Check the HttpServletRequest#getRequestURI() (or the getServerName() if it is a subdomain instead of pathinfo) and do the forwarding task accordingly.
Hope this helps.
Use JMX
ArrayList serverList = MBeanServerFactory.findMBeanServer(null);
MBeanServer server = (MBeanServer) serverList.get(0);
Object[] params = { "org.apache.catalina.core.StandardHost", hostName };
String[] signature = { "java.lang.String", "java.lang.String" };
server.invoke(new ObjectName("Catalina:type=Engine"), "addChild", params, signature);
If needed, retrieve the host object and work with it:
ObjectName host = new ObjectName("Catalina:type=Host,host=" + hostName);
server.setAttribute(host, new Attribute("autoDeploy", false));
server.invoke(host, "start", null, null);
I would suggest you set your application to be the default virtual host in server.xml so your single virtual host can respond to requests addressed to any host name. Tomcat ships with the localhost application set as the default virtual host. So you can see how to do this by simply inspecting the server.xml file of a vanilla tomcat installation. You can programatically determine the host name the user sent the request to using the ServletRequest.getServerName() method.
Tomcat used to ship with a web application called "host-manager". Note: this is different than the "manager" web application that still comes with Tomcat. Host manager allowed for changing configuration or adding new virtual hosts on the fly without restarting the server. You could interact with the host-manager over HTTP (programmatically if desired). However, it had the unfortunate flaw of not committing its changes to server.xml so they were all lost on a web server restart. For whatever reason, starting with version 6, Tomcat no longer ships with the host-manager application. So it doesn't appear to be supported anymore.
To sum up ZZ Coder answer which guided me a lot:
You have to create a servlet that implements ContainerServlet and override setWrapper method to get the org.apache.catalina.Wrapper object.
For doing that you have to have privileged="true" in your context.xml Context tag or it will throw an exception. Then you can use the Wrapper object and:
StandardContext context = (StandardContext) wrapper.getParent();
StandardHost currentHost = (StandardHost) context.getParent();
StandardEngine engine = (StandardEngine) currentHost.getParent();
StandardHost host = new StandardHost();
host.setAppBase(currentHost.getAppBase()); //in my case I created another instance of the same application
host.setDomain(currentHost.getDomain());
host.setAutoDeploy(false); // not restarting app whenever changes happen
host.setName("domain.com");
host.setThrowOnFailure(true);// tell it to throw an exception here if it fails to create the host
host.setDeployOnStartup(true);
host.setStartChildren(true);
host.setParent(engine);
// you can add multiple aliases
host.addAlias(alias);
StandardContext ctx = new StandardContext();
ctx.setDocBase(context.getDocBase()); //again I reused my same application setting
ctx.setPath("");
if(currentHost.getWorkDir() != null)
{//create a working directory based on your new host's name
ctx.setWorkDir(currentHost.getWorkDir().replace(currentHost.getName(), host.getName()));
}
ctx.setName(host.getDomain());
//some extra config that you can use
ctx.setUseHttpOnly(false);
ctx.setReloadable(false);
ctx.setXmlValidation(false);
ctx.setXmlNamespaceAware(false);
ctx.setCrossContext(false);
ctx.setParent(host);
// you have to have this or it will not work!!
ctx.addLifecycleListener(new ContextConfig());
//you can also create resources and add it to the context like so:
final ContextResource res = new ContextResource();
res.setName("name");
res.setAuth("Container");
res.setType("javax.sql.DataSource");
ctx.getNamingResources().addResource(res);
host.addChild(ctx);
engine.addChild(host);
You can add properties to your resource by calling res.setProperty("name", "value")
Some properties that you can use are:
initialSize,maxTotal,maxIdle,maxWaitMillis,removeAbandonedOnBorrow,removeAbandonedTimeout,validationQuery,timeBetweenEvictionRunsMillis,driverClassName,url,username,password.
Another exciting thing to is to get the host from the tomcat engine by calling engine.findChild(domain) and use stop(), start(), getStateName() and have your own Tomcat Admin panel!

Categories

Resources