Oracle: Java stored procedure sending JMS Message - java

I am attempting to send a Point-to-Point JMS message from an oracle database stored procedure to a java application. The two 'points' sit on different machines, which I've confirmed can talk to each other via ping.
I've created a java application able to successfully take messages off a queue within the application server. The application is running within a JBoss v4.2.3 server. I've been able to successfully send a JMS message from a remote java application, so I'm sure the code running within the server is ok.
I've taken code from the working remote java application and loaded this successfully into an oracle stored procedure. I've also managed to (I believe!) load into oracle the required jar files using the loadjava utility. The three jar files I've loaded in are:
* jms-1.1
* jbossmq-3.2.3
* jboss-client-4.0.2
The three jars are used within the working remote java application and appear to be all that's required. The code contained loaded into the stored procedure is as follows:
package com.base.jms.client;
import java.util.Hashtable;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
public class StandAloneClient {
public static String send() throws Exception {
String result = "Starting -> ";
try {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
env.put(Context.PROVIDER_URL, "192.168.111.242:1099");
env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
result = result + "Environment -> ";
// set up stuff
Context ic = new InitialContext(env);
result = result + "Context -> ";
QueueConnectionFactory connectionFactory = (QueueConnectionFactory) ic.lookup("ConnectionFactory");
result = result + "Factory -> ";
Queue queue = (Queue) ic.lookup("queue/A");
result = result + "Queue -> ";
QueueConnection connection = connectionFactory.createQueueConnection();
result = result + "Connection -> ";
QueueSession session = connection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
result = result + "Session -> ";
QueueSender sender = session.createSender(queue);
connection.start();
result = result + "Sender -> ";
TextMessage myMessage = session.createTextMessage();
myMessage.setText(result);
sender.send(myMessage);
result = result + "Sending Message -> ";
sender.close();
session.close();
connection.close();
result = result + "Close";
} catch (JMSException e) {
result = result + "JMS Exception";
/*
if(e.getMessage() != null) {
result = result + ":" + e.getMessage();
}*/
} catch (Exception e) {
result = result + "Exception";
/*
if(e.getMessage() != null) {
result = result + ":" + e.getMessage();
}*/
}
return result;
}
}
I've added the result string in so I can try and determine where in the code it's falling over. To create and test this procedure, I'm executing the following commands in sqlplus:
create or replace function send_jms return VARCHAR2 as language java name 'com.base.jms.client.StandAloneClient.send() return java.lang.String';
variable myString varchar2(20);
call send_jms() into :myString;
Call completed.
print myString;
Everything seems to be loaded and compiling correctly, however the message is not being sent. The result string returned implicates it's falling over when attempting to retrieve the QueueConnectionFactory class from the InitialContext. The result string returned is:
Starting -> Environment -> Context -> Exception
I'm at a loss as to why this is not working, and have been unable to glean more from the Exception thrown. Can anyone confirm that I am doing this correctly, and if I am, see what I am doing wrong?
Apologies for the long post but thank you in advance for looking at it!

I'm not exactly an expert about running Java and JMS within the Oracle database (though I know each of the three components separately). But from your description it seems you haven't taken the Oracle security model for Java into consideration.
Oracle will not let any component access the network (or the file system etc.) without having explicitly being granted the right to. So start reading about Oracle JVM security to learn how you might need to configure Oracle for letting you connect to a remote machine.
Granting the permissions could involve the following statement:
EXEC DBMS_JAVA.GRANT_PERMISSION('YOUR_SCHEMA', 'SYS:java.net.SocketPermission', '192.168.111.242', 'connect,accept,resolve');

I've finally got this all working, thanks in large part to the help offered by Cody. I'm just putting this answer up in case someone else comes across the same problems and wants to know exactly how I solved it.
The statements I ran to grant the necessary permissions to oracle were:
GRANT JAVASYSPRIV to NTIS
EXEC dbms_java.grant_permission ('JAVASYSPRIV', 'SYS:java.net.SocketPermission', '*', 'accept,connect,listen,resolve');
EXEC DBMS_JAVA.GRANT_PERMISSION('YOUR SCHEMA', 'SYS:java.net.SocketPermission', '*', 'connect,accept,resolve');
You have to been logged in as 'SYSTEM' user when executing these commands.
I had some further problems with the stored procedure which were down to issues with the libraries I loaded into oracle. The jars I used were:
jms
jbossmq-client
jboss-client
The jbossmq-client and jboss-client jars must be the same versions as those used in the application server itself. I was using JBoss 4.2.3, so I copied these jars from the 'client' folder within the JBoss directory.
I also had to put the ipaddress referenced in the java class/stored procedure in the hosts file as it appears oracle does a reverse lookup on the ipaddress. If you don't do this you get an unknown host exception. In the hosts file, just put your computers name and the ipaddress.
After doing all this is, its now working correctly for me! Hope this helps someone if they come across the same problem.

I found that even though I was trying to grant permission as SYS, and the policy belonged to SYS, then SYS didn't have permission to grant that policy.
So this worked for me:
exec dbms_java.grant_policy_permission('SYS','SYS','java.net.SocketPermission','*');
exec dbms_java.grant_permission( 'MY_SCHEMA', 'SYS:java.net.SocketPermission', '*', 'connect,resolve' );

Related

dbms_java.grant_permission is required for every new session

i am trying to implement java RMI client server connection, i made my server in NetBeans IDE and client class made in oracle database as java store procedure and use this via oracle function in my PLSQL block, everything is working fine but I stuck in little issue below:
I made this java store procedure in SYSTEM user and give dbms_java.grant_permission('SYSTEM','java.net.SocketPermission','192.168.43.25:*','connect,resolve') to SYSTEM user via SYSDBA, "*" is used for all available ports. I get perfect response from my server but till specific session expiry.
Whenever I logout from SYSTEM user and login back again, its again throws me an exception the Permission ("java.net.SocketPermission" "192.168.43.25:56792" "connect,resolve") has not been granted please guide me how can I get rid from this repeated task? because in development environment I'll manage it but how can deploy it on production with this issue. My Environment is Oracle DB 18c XE, Thank You!
Following is my java store procedure:
create or replace and compile java source named rmi as
package rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.Naming;
import java.security.Policy;
public class RmiClient {
public RmiClient(){
}
public static String getRequestFromClient(){
Policy.setPolicy(new MyPolicy());
String result = "";
try {
IServer server = getServer();
result = server.getRequestFromClient();
} catch (Exception ex) {
result = ex.getMessage().toString();
}
return result;
}
private static IServer getServer() throws Exception {
Parameter configurationParameters = new Parameter();
IServer server = (IServer) Naming.lookup( configurationParameters.objectName);
return server;
}
}
Granting permissions with DBMS_JAVA is a little different than a typical Oracle grant. After granting a permission using DBMS_JAVA.GRANT_PERMISSION, you will need to COMMIT at some point after the grant as been executed for it to take affect permanently. Notice how there is a COMMIT after the DBMS_JAVA call in the Oracle documentation.

Directory does not exists (Lotus Notes)

I'm trying to get all the database on my server. But specifying my server name in getDbDirectory() parameter as NALLN304/40/LLN/IBM gives me a error.
Directory NALLN304/40/LLN/IBM!! does not exist
it always add two exclamation mark at the end. I tried also as server name and mail file adding .nsf format at the end of mail file. NALLN304/40/LLN/IBM!!data0\126\1000031540.nsf also gives me the same error.
Snipper code below:
Session session = null;
Database db = null;
DbDirectory dir = null;
try
{
NotesThread.sinitThread();
session = NotesFactory.createSession();
System.out.println("User = " + session.getUserName());
dir = session.getDbDirectory("NALLN304/40/LLN/IBM");
System.out.println(dir.getName());
db = dir.getFirstDatabase(DbDirectory.DATABASE);
do
{
System.out.println("Title: " +db.getTitle());
}
while(dir.getNextDatabase() != null);
}
catch(NotesException ex)
{
ex.printStackTrace();
}
The error always points out to the db = dir.getFirstDatabase(DbDirectory.DATABASE); because dir.getFirstDatabase(DbDirectory.DATABASE) expects .nsf file even I specify the file format. Any reasons why I got this error?
Your problem is that your ID is not authenticating with the server. You need to take the output from this line:
System.out.println("User = " + session.getUserName());
And take it to your server administrators, ask why it is not being allowed to access the server, and ask them to assist you either by granting the necessary permissions or by giving you another ID that you can use.
Check the logs for server connection errors. Even if the server has to connect to itself. Faced the same problem. The server gave an error because it did not find a route for the connection. Added a new connection for the server to itself and everything was fixed

Trigger Snapshots on Remote Server from local

I am trying to remotely profile alfresco running on a 64-bit linux server running a 1.8 JVM and Apache Tomcat 7.xx from my testing code but can't figure out how to programatically trigger snapshots.
What I want to do is to connect to the remote server, start profiling, and save a snapshot of that server's performance onto my local machine from my testing code which is written in Java.
I've already installed JProfiler 9.2 onto the linux server and can connect and take snapshots via the JProfiler GUI. The server also requires an SSH connection for security. I'd like to do this from my code, similar to how Controller.saveSnapshot(file) works for local JVMs.
Is this possible?
I know I can set triggers and have the remote profiler save snapshots on the server, but this isn't what I want to.
Additionally, I looked into using the command line controller but was unable to get it to connect to the server even with the correct arguments in the remote VM options.
I also tried to use ConnectionFactor.createRemoteConnection(), but don't see an argument that allows for a password to be input, so it fails.
You can access the JProfiler MBean programmatically. Below is an example on how to do that. I would run such a program on the remote machine and start it via SSH, because JMX connnections are difficult to tunnel through SSH.
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
// Shows how to connect to the the JProfiler MBean programatically
// The profiled process has to be in offline profiling mode and the JMX server
// has to be started by passing -Djprofiler.jmxServerPort=[port] to the profiled JVM.
// This will not work in nowait mode because the MBean is not registered in that case.
public class MBeanProgrammaticAccessExample {
public static void main(String[] args) throws Exception {
if (args.length == 0) {
System.out.println("Specify the port as an argument that was passed to " +
"the profiled JVM with the VM parameter " +
"-Djprofiler.jmxServerPort=[port]");
}
String port = args[0];
// In this case the connection is made to a process on localhost, but it could
// be on a remote system as well. Note that the connection is made via JMX which
// does not work well with firewalls
System.out.println("Connecting to localhost:" + port);
JMXServiceURL jmxUrl = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://localhost:" + port + "/jmxrmi");
JMXConnector connector = JMXConnectorFactory.newJMXConnector(jmxUrl,
Collections.<String, Object>emptyMap());
Map<String, Object> env = new HashMap<>();
// If you have protected the JMX server with a JMX password file by passing
// -Djprofiler.jmxPasswordFile=[file] to the profiled JVM, you can specify
// the password like this:
//env.put(JMXConnector.CREDENTIALS, new String[] {"username", "password"});
connector.connect(env);
MBeanServerConnection connection = connector.getMBeanServerConnection();
ObjectName objectName = new ObjectName(
"com.jprofiler.api.agent.mbean:type=RemoteController");
if (!connection.isRegistered(objectName)) {
throw new RuntimeException("JProfiler MBean not found.");
}
RemoteControllerMBean mbeanProxy = JMX.newMBeanProxy(connection,
objectName, RemoteControllerMBean.class, true);
// You can look up all available operations in the javadoc of
// com.jprofiler.api.agent.mbean.RemoteControllerMBean
System.out.println("Recording CPU data for 5 seconds ....");
mbeanProxy.startCPURecording(true);
// If you do not want a dependency on the JProfiler classes
// you can make the above call like this:
//connection.invoke(objectName, "startCPURecording", new Object[] {true},
// new String[] {Boolean.TYPE.getName()});
Thread.sleep(5000);
System.out.println("Saving snapshot to the working directory " +
"of the profiled JVM ....");
mbeanProxy.saveSnapshot("snapshot.jps");
connector.close();
System.out.println("Success");
}
}

Getting the Server Name from the session

In a managed bean that resides in a Database on the server Development I have this code:
s = ExtLibUtil.getCurrentSession();
theMap.put("Server Name", s.getServerName());
when I look at theMap after this has run I see Server Name and the value is blank. After this I get a datbase RepID and then try to open the database by RepID with
appDB = s.getDbDirectory(null).openDatabaseByReplicaID(repID);
if (appDB.isOpen()){
theMap.put(thisKey, repID);
}else{
theMap.put("DB " + thisKey, "Is Not Open");
}
if I have a rep copy of the database locally it opens it, if I remove the local Replica the open fails. If I change the line to:
appDB = s.getDbDirectory("Development").openDatabaseByReplicaID(repID);
the proper appDB opens. So it looks like the session thinks it is running locally because it return null for the server name. This is really strange, am I missing something? For the moment i have just hard coded the server name in the getDbDirectory but that wont work in the real world.
Is this XPiNC? That would consider the database to be running locally unless you've set the application property "Run server-based XPages on server"
String serverName = s.getEnvironmentString("ServerName", true);
or
String serverName = s.getEnvironmentString("ServerKeyFileName_Owner", true);

Issues with connecting to ibm mq 7.5 using java

I'm very new to ibm mq, I find out the documents or books related to mb are so few, the only one I found is 'WebSphere MQ Using Java' written in 2004. But the real world has changed a lot.
I installed and verified mq server 7.5 on redhat linux 64 bit successfully according to this
I also created queue manager myqm1, queue LQ.TEST, channel JAVA.CHANNEL and did some test through command lines on server to ensure they work well. However when I installed a mq client on windows xp and wrote below java code, it always throw a exception:com.ibm.mq.MQException: MQJE001: Completion Code '2', Reason '2035'
my code:
import com.ibm.mq.*; import com.ibm.mq.constants.MQConstants;
/** * Simple example program */ public class MQSample {
// code identifier
static final String sccsid = "#(#) MQMBID sn=p000-L120604 su=_H-IvIK4nEeGko6IWl3MDhA pn=MQJavaSamples/wmqjava/MQSample.java";
// define the name of the QueueManager
private static final String qManager = "myqm1";
// and define the name of the Queue
private static final String qName = "LQ.TEST";
/**
* Main entry point
*
* #param args - command line arguments (ignored)
*/
public static void main(String args[]) {
try {
MQEnvironment.hostname = "58.2.221.196";
MQEnvironment.channel = "JAVA.CHANNEL";
MQEnvironment.port = 1414;
MQEnvironment.properties.put(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES);
MQEnvironment.userID = "mqm";
MQEnvironment.password = "mqm";
MQEnvironment.CCSID = 1208;
// Create a connection to the QueueManager
System.out.println("Connecting to queue manager: " + qManager);
MQQueueManager qMgr = new MQQueueManager(qManager);
// Set up the options on the queue we wish to open
int openOptions = MQConstants.MQOO_INPUT_AS_Q_DEF | MQConstants.MQOO_OUTPUT;
// Now specify the queue that we wish to open and the open options
System.out.println("Accessing queue: " + qName);
MQQueue queue = qMgr.accessQueue(qName, openOptions);
// Define a simple WebSphere MQ Message ...
MQMessage msg = new MQMessage();
// ... and write some text in UTF8 format
msg.writeUTF("Hello, World!");
// Specify the default put message options
MQPutMessageOptions pmo = new MQPutMessageOptions();
// Put the message to the queue
System.out.println("Sending a message...");
queue.put(msg, pmo);
// Now get the message back again. First define a WebSphere MQ
// message
// to receive the data
MQMessage rcvMessage = new MQMessage();
// Specify default get message options
MQGetMessageOptions gmo = new MQGetMessageOptions();
// Get the message off the queue.
System.out.println("...and getting the message back again");
queue.get(rcvMessage, gmo);
// And display the message text...
String msgText = rcvMessage.readUTF();
System.out.println("The message is: " + msgText);
// Close the queue
System.out.println("Closing the queue");
queue.close();
// Disconnect from the QueueManager
System.out.println("Disconnecting from the Queue Manager");
qMgr.disconnect();
System.out.println("Done!");
} catch (MQException ex) {
ex.printStackTrace();
System.out.println("A WebSphere MQ Error occured : Completion Code " + ex.completionCode
+ " Reason Code " + ex.reasonCode);
} catch (java.io.IOException ex) {
System.out.println("An IOException occured whilst writing to the message buffer: " + ex);
}
return;
} }
Can anybody throw a light to me on that? I'm totally down.
To expand on Shashi's answer, since WMQ V7.1 the default CHLAUTH rules block all access on all SVRCONN channels and they block administrative access on all SVRCONN channels. If you really want to connect to JAVA.CHANNEL as mqm then you will need to override both of these behaviors.
If you are actually willing to allow remote, unauthenticated connections to the QMgr with an administrative user ID, then you have the option of disabling CHLAUTH rules altogether. You can do this by issuing the ALTER QMGR CHLAUTH(DISABLED) command in runmqsc however this is HIGHLY discouraged because it leaves the QMgr open to anonymous remote code execution using the WMQ administrative user ID. This is, however, what you appear to be trying to do.
The recommended approach would be to use an ID that is not administrative. For example, if you made an ID called mquser with a private group also called mquser then you could grant it rights to connect and inquire on the QMgr and to open the designated queue for put, get, browse and inquire. Since the ID is not administrative, it would be relatively safe to use on unauthenticated channels. You could change your code to specify the ID as mquser instead of mqm and then use a CHLAUTH rule to allow the connection. For example:
SET CHLAUTH('JAVA.CHANNEL') TYPE(USERMAP) +
CLNTUSER('mquser') USERSRC(MAP) +
MCAUSER('mquser') ACTION(ADD)
The above rule tells the QMgr "when you see a connection from the mquser ID on JAVA.CHANNEL, then set MCAUSER to mquser and allow the connection."
When you grant permissions, remember to grant them on the group and not the user. For example, if using setmqaut use the -g option and not the -p option. If there are any issues with authorization errors, you can sort these out easily using event messages. First, enable events using the ALTER QMGR AUTHOREV(ENABLED). This will cause the QMgr to emit an event message into the SYSTEM.ADMIN.QMGR.EVENT queue. You can use SupportPac MH05 or SupportPac MS0P to parse the event messages. For any given authorization event the message tells you the ID that requested access, the API call (connect, open, close etc.), the object the call was made against and the exact options that were used.
Prior to WMQ V7.1, WebSphere MQ allowed all remote connections, even anonymous, administrative ones. Although this allowed you to connect easily, in today's more hostile network environment the ability to remotely and anonymously execute code on the QMgr's host server is seen as an unacceptable security risk. So now a new QMgr is set to not allow any remote administrative access by default. As the administrator this requires you to explicitly disable security to get the old behavior or to explicitly provision secure access.
In MQ v7.5, by default access to queue manager is blocked. You need to create channel authentication records for the channel you created, JAVA.CHANNEL to allow user to access queue manager. Please follow this link for more details on Channel Authentication Records

Categories