Accessing PowerDesigner Repository Models via COM - java

I have been trying to get live models directly from the PowerDesigner repository using the COM API without success. Here's what I've been trying in VBA:
Set pd = CreateObject("PowerDesigner.Application")
Set conn = pd.RepositoryConnection
conn.Open "", "", "ShhMahPW"
Set model = conn.FindChildByPath("Program/Project/Logical Models/MahLOM", PdOOM_Classes.cls_Model)
MsgBox model.ShortDescription 'This fails because model is null!
Similarly, I've been trying the same thing in Eclipse with the Java COM bridge:
Application pd = this.getApplicationHook();
//Make live connection to proxy repository
RepositoryConnection conn = new RepositoryConnection( pd.GetRepositoryConnection() );
conn.Open( "", "", ConnectionParams.PASSWORD );
BaseObject model = conn.FindChildByPath( "Program/Project/Logical Models/MahLOM",
PdOOM_Classes.cls_Model );
//Null model, COMException: "Action can not be performed. result = -2147467259"
System.out.println( model.GetShortDescription() )
Can someone please suggest a good way of diving into the repository? I have been able to confirm that I have a connection to the repo and then list the children at that top level. I am struggling to dig into folders beyond the root level. Thanks!

I knew that the model I was looking to pull down from the repo already existed in my local workspace. Really this was a refresh of the local workspaces models. To perform this, the method UpdateFromRepository() can be used!
So what I can do then is get a handle to the local PowerDesigner model and then call for an update before retrieving children. Note the casting from BaseObject to BaseModel for the sake of the refresh...
private BaseObject getModel(){
Application pd = this.getApplicationHook();
model = pd.OpenModel(this.basePath + this.modelName);
System.out.println( "Retrieving model updates from repository... ");
RepositoryConnection conn = new RepositoryConnection( pd.GetRepositoryConnection() );
conn.Open( "", "", ConnectionParams.PASSWORD);
boolean success = new BaseModel(model).UpdateFromRepository();
if( success )
System.out.println( "Update successful!" );
else
System.out.println( "Update failed. Check PowerDesigner settings." );
return this.model;
}

Your main problem is that the search ChildKind should be Cls_RepositoryModel, instead of PdOOM_Class.cls_Model.
option explicit
' assuming we're already connected
if RepositoryConnection.Connected then
Descent RepositoryConnection,""
end if
dim c
set c = RepositoryConnection.FindChildByPath("Folder_7/ConceptualDataModel_1", Cls_RepositoryModel)
if not c is nothing then
output "*** found object " & c.classname
end if
sub Descent(obj,ofs)
output ofs & obj.name & " - " & obj.ObjectType & " - " & obj.ClassName
if obj.ObjectType = "RepositoryModel" then exit sub
if obj.PermanentID = 3 then exit sub ' to save time, don't enter Library
if not obj.HasCollection("ChildObjects") then exit sub
dim c
for each c in obj.ChildObjects
Descent c,ofs & " "
next
end sub

Related

JT400 ProgramCall's run() method is not returning any result

I am new to JT400. I am trying to invoke a test program in AS400 through JT400. Here is my code
public class TestRpg {
public static void main(String[] args){
try{
AS400 sys=new AS400("mydomain","username","password");
String number="asdf <= Return value from Java Input";
String lnsts="";
String amount="";
String lnofcd="";
AS400Text txt80 = new AS400Text(80);
AS400Text txt50 = new AS400Text(50);
ProgramParameter[] parmList = new ProgramParameter[4];
parmList[0] = new ProgramParameter( txt80.toBytes(number),80);
parmList[1] = new ProgramParameter( txt50.toBytes(lnsts),50);
parmList[2] = new ProgramParameter( txt80.toBytes(amount),80);
parmList[3] = new ProgramParameter( txt50.toBytes(lnofcd),50);
ProgramCall pgm = new ProgramCall(sys,"/QSYS.LIB/mylib.LIB/testrpg.PGM",parmList);
if (pgm.run()!=true) {
System.out.println("executed");
}else{
System.out.println("Output Data 0: " + (String)txt80.toObject( parmList[0].getOutputData() ) );
System.out.println("Output Data 1: " + (String)txt50.toObject( parmList[1].getOutputData() ) );
System.out.println("Output Data 2: " + (String)txt80.toObject( parmList[2].getOutputData() ) );
System.out.println("Output Data 3: " + (String)txt50.toObject( parmList[3].getOutputData() ) );
sys.disconnectService(AS400.COMMAND);
}
AS400Message[] messageList = pgm.getMessageList();
System.out.println(messageList.length);
for (int i=0; i < messageList.length; i++)
{
System.out.print ( messageList[i].getID() );
System.out.print ( ": " );
System.out.println( messageList[i].getText() );
}
sys.disconnectService(AS400.COMMAND);
}catch(Exception e) {
System.out.println(e.toString());
}
}
}
I had debug the code it's not giving any response after executing
pgm.run(). It is not even showing any exception. Programme is just holding at pgm.run() and not returning any thing.
As per the comments I got, I want to include the scenario I am trying to work on. In AS400 when we execute the testrpg.pgm program, it displays a screen with four input fields and some function keys to perform operations. My intention is to invoke f2 function key of that program from JT400. Is the approach I am following is the right way? Please suggest me
All program calls happen in batch so your program is most likely in MSGW on the server. Find it with wrkactjob and investigate the message it is waiting for, and give the appropriate action.
This is typically due to incorrectly formed parameters.
This is a common misunderstanding, so just for clarification for other readers:
Calling a Cobol/RPG program from Java is batch, just the same as calling the Cobol/RPG program from a Cobol/RPG/CL.
How to begin: Create a program which you can call from CL:
... declare and fill MYFIELD1, MYFIELD2 ...
CALL PGM(MYPGM) PARM(&MYFIELD1 &MYFIELD2)
...
If this works, it will also work from Java using jt400, if you:
call the right AS400 using correct credentials
call the right program in the right library
use the right number and lentgh of parameters
In case of crash as described (waiting forever), DSPMSG QSYSOPR will show an open message, like "MCH0801 = wrong number of parameters". D=Dump will create a spoolfile where you see which incoming parameters are filled with which content, or you see "undefined".

Can you extract a pl/java class file from an Oracle Database?

I have a pl/java class in a running Oracle database that I have misplaced the source code to.
Is there anyway to get the java bytecode back out the database so I could run it against a decompiler?
I've already checked ALL_SOURCE and Oracle claims it doesn't have the source code.
I had to do this myself recently, and put together a Groovy script to do it. You need to modify the connection details and make sure you've got Oracle database drivers on your classpath
import groovy.sql.Sql
// Change the following to your requirements ...
def extractRoot = "extracted-classes/" // Directory to extract classes into
def user = 'SCOTT' // Schema user
def password = 'tiger' // Yes, it's the password
def host = 'localhost' // Database host
def sid = 'orcl' // Database SID
def port = 1521 // Database listener port
def saveBlob(blob, root, name, extension) {
def byteStream = blob.getBinaryStream()
def bytes = new byte[blob.length()]
byteStream.read(bytes)
def dir = root + name.replaceAll(/\/[^\/]+$/, '')
if (dir != name) {
new File(dir).mkdirs()
}
def f = new File(root + name + extension)
println "Writing ${f.getCanonicalPath()}"
f.delete()
f.withOutputStream { s ->
s.write(bytes)
}
}
sql = Sql.newInstance("jdbc:oracle:thin:#${host}:${port}:${sid}", user,
password, 'oracle.jdbc.driver.OracleDriver')
sql.eachRow("select name from all_java_classes where owner = ${user}") {
sql.call('''
declare
b blob;
begin
dbms_lob.createtemporary(b, FALSE);
dbms_java.export_class(?, ?, b);
? := b;
end;''', [it.name, user, Sql.BLOB]) { blob ->
saveBlob(blob, extractRoot, it.name, '.class')
}
}
sql.eachRow("select dbms_java.longname(object_name) \"name\" from all_objects where object_type = \'JAVA RESOURCE\' and owner = ${user}") {
sql.call('''
declare
b blob;
begin
dbms_lob.createtemporary(b, FALSE);
dbms_java.export_resource(?, ?, b);
? := b;
end;''', [it.name, user, Sql.BLOB]) { blob ->
saveBlob(blob, extractRoot, it.name, '')
}
}
If you know some information about it then you can use dbms_java.
Specifically, judging by your question, export_class; though this may require more detective work on your part. If your code is stored in a actual package, dbms_metadata should be able to help as well.
Java objects should also show up in all_objects, which'll help you track down the schema and/or name if needs be.

Getting output parameter value set by VBScript (WMI) method in java via JACOB

Am trying to convert a VBScript to java using JACOB - Java COM bridge library.
'Create' method in VBScript accepts a [out] param in it's method and it sets it upon method execution and i couldn't figure out how to retrieve it back via JACOB.
VBScript in question:
Function CreateProcess(strComputer, strCommand)
Dim objWMIService, objProcess
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set objProcess = objWMIService.Get("Win32_Process")
errReturn = objProcess.Create (strCommand, Null, Null, intProcessID)
Set objWMIService = Nothing
Set objProcess = Nothing
CreateProcess = intProcessID
End Function
intProcessID is [out] param set after method execution. (Create API contract)
Converted java code(incomplete and modified slightly for demonstration):
public static void createProcess() {
String host = "localhost";
String connectStr = String
.format("winmgmts:{impersonationLevel=impersonate}!\\\\%s\\root\\CIMV2",
host);
ActiveXComponent axWMI = new ActiveXComponent(connectStr);
Variant vCollection = axWMI.invoke("get", new Variant("Win32_Process"));
Dispatch d = vCollection.toDispatch();
Integer processId = null;
int result = Dispatch.call(d, "Create", "notepad.exe", null, null, processId)
.toInt();
System.out.println("Result:" + result);
// WORKS FINE until here i.e. notepad launches properly, however processId still seems to be null. Following commented code is wrong - doesn't work
//Variant v = Dispatch.get(d, "processId"); // even ProcessId doesn't work
//int pId = v.getInt();
//System.out.println("process id:"
// + pId);
// what is the right way to get the process ID set by 'Create' method?
}
Would be great if you could provide some pointers or relevant code. Ask me more if needed. Thanks in advance.
Replacing
Integer processId = null;
with
Variant processId = new Variant(0, true);
should solve the problem. You should then have process ID of the notepad.exe process in the processId variant, and it can be fetched by
processId.getIntRef()

DJ Native Swing javascript command problems

Using DJ Native Swing it is possible to show a web page within a java application. When you do this it is also possible to communicate from the browser to the java runtime environment using the "command" protocol. The documentation has a code snippet which demonstrates it's usage:
function sendCommand( command ){
var s = 'command://' + encodeURIComponent( command );
for( var i = 1; i < arguments.length; s+= '&' + encodeURIComponent( arguments[i++] ) );
window.location = s;
}
As it looks here it seems to be a regular GET request to an url using the command protocol instead of http. Although when I create and image, script tag or just and ajax get request there is no response and the breakpoint in the java runtime isn't triggered.
I don't want to set the window.location because I don't want to navigate away from the page I am currently at. Using the link to navigate to a command url does work though but it also navigates away from the current page. The page uses OpenLayers and dojo. (I have also tried dojo.io.script)
After some work I have found a neat way to communicate with the java runtime which doesn't trigger a refresh of the page every time there is communication. It is inspired on the way JSONP works to get around the cross domain restriction in most browsers these days. Because an iFrame will also trigger a command:// url it possible to do a JSONP like action using this technique. The code on the client side (browser):
dojo.provide( "nmpo.io.java" );
dojo.require( "dojo.io.script" );
nmpo.io.java = dojo.delegate( dojo.io.script, {
attach: function(/*String*/id, /*String*/url, /*Document?*/frameDocument){
// summary:
// creates a new tag pointing to the specified URL and
// adds it to the document.
// description:
// Attaches the script element to the DOM. Use this method if you
// just want to attach a script to the DOM and do not care when or
// if it loads.
var frame = dojo.create( "iframe", {
id: id,
frameborder: 0,
framespacing: 0
}, dojo.body( ) );
dojo.style( frame, { display: "none" } );
dojo.attr( frame, { src: url } );
return frame;
},
_makeScriptDeferred: function(/*Object*/args){
//summary:
// sets up a Deferred object for an IO request.
var dfd = dojo._ioSetArgs(args, this._deferredCancel, this._deferredOk, this._deferredError);
var ioArgs = dfd.ioArgs;
ioArgs.id = dojo._scopeName + "IoScript" + (this._counter++);
ioArgs.canDelete = false;
//Special setup for jsonp case
ioArgs.jsonp = args.callbackParamName || args.jsonp;
if(ioArgs.jsonp){
//Add the jsonp parameter.
ioArgs.query = ioArgs.query || "";
if(ioArgs.query.length > 0){
ioArgs.query += "&";
}
ioArgs.query += ioArgs.jsonp
+ "="
+ (args.frameDoc ? "parent." : "")
+ "nmpo.io.java.jsonp_" + ioArgs.id + "._jsonpCallback";
ioArgs.frameDoc = args.frameDoc;
//Setup the Deferred to have the jsonp callback.
ioArgs.canDelete = true;
dfd._jsonpCallback = this._jsonpCallback;
this["jsonp_" + ioArgs.id] = dfd;
}
return dfd; // dojo.Deferred
}
});
When a request is sent to the java runtime a callback argument will be supplied and a webBrowser.executeJavascript( callbackName + "(" + json + ");" ); action can be executed to trigger the callback in the browser.
Usage example client:
dojo.require( "nmpo.io.java" );
nmpo.io.java.get({
// For some reason the first paramater (the one after the '?') is never in the
// paramater array in the java runtime. As a work around we stick in a dummy.
url: "command://sum?_",
callbackParamName: "callback",
content: {
numbers: [ 1, 2, 3, 4, 5 ].join( "," )
},
load: function( result ){
console.log( "A result was returned, the sum was [ " + result.result + " ]" );
}
});
Usage example java:
webBrowser.addWebBrowserListener(new WebBrowserAdapter() {
#Override
public void commandReceived(WebBrowserCommandEvent e) {
// Check if you have the right command here, left out for the example
// Parse the paramaters into a Hashtable or something, also left out for the example
int sum = 0;
for( String number : arguments.get( "numbers" ).split( "," ) ){
sum += Integer.parseInt( number );
}
// Execute the javascript callback like would happen with a regular JSONP call.
webBrowser.executeJavascript( arguments.get( "callback" ) + "({ result: " + sum + " });" );
}
});
Also with IE in the frame I can highly recommend using firebug lite, the dev tools for IE are not available.

How do I access Windows Event Viewer log data from Java

Is there any way to access the Windows Event Log from a java class. Has anyone written any APIs for this, and would there be any way to access the data from a remote machine?
The scenario is:
I run a process on a remote machine, from a controlling Java process.
This remote process logs stuff to the Event Log, which I want to be able to see in the controlling process.
Thanks in advance.
http://www.j-interop.org/ is an open-source Java library that implements the DCOM protocol specification without using any native code. (i.e. you can use it to access DCOM objects on a remote Windows host from Java code running on a non-Windows client).
Microsoft exposes a plethora of system information via Windows Management Instrumentation (WMI). WMI is remotely accessible via DCOM, and considerable documentation on the subject exists on Microsoft's site. As it happens, you can access the Windows Event Logs via this remotely accessible interface.
By using j-interop you can create an instance of the WbemScripting.SWbemLocator WMI object remotely, then connect to Windows Management Instrumentation (WMI) services on the remote Windows host. From there you can submit a query that will inform you whenever a new event log entry is written.
Note that this does require that you have DCOM properly enabled and configured on the remote Windows host, and that appropriate exceptions have been set up in any firewalls. Details on this can be searched online, and are also referenced from the j-interop site, above.
The following example connects to a remote host using its NT domain, hostname, a username and a password, and sits in a loop, dumping every event log entry as they are logged by windows. The user must have been granted appropriate remote DCOM access permissions, but does not have to be an administrator.
import java.io.IOException;
import java.util.logging.Level;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.common.JISystem;
import org.jinterop.dcom.core.JIComServer;
import org.jinterop.dcom.core.JIProgId;
import org.jinterop.dcom.core.JISession;
import org.jinterop.dcom.core.JIString;
import org.jinterop.dcom.core.JIVariant;
import org.jinterop.dcom.impls.JIObjectFactory;
import org.jinterop.dcom.impls.automation.IJIDispatch;
public class EventLogListener
{
private static final String WMI_DEFAULT_NAMESPACE = "ROOT\\CIMV2";
private static JISession configAndConnectDCom( String domain, String user, String pass ) throws Exception
{
JISystem.getLogger().setLevel( Level.OFF );
try
{
JISystem.setInBuiltLogHandler( false );
}
catch ( IOException ignored )
{
;
}
JISystem.setAutoRegisteration( true );
JISession dcomSession = JISession.createSession( domain, user, pass );
dcomSession.useSessionSecurity( true );
return dcomSession;
}
private static IJIDispatch getWmiLocator( String host, JISession dcomSession ) throws Exception
{
JIComServer wbemLocatorComObj = new JIComServer( JIProgId.valueOf( "WbemScripting.SWbemLocator" ), host, dcomSession );
return (IJIDispatch) JIObjectFactory.narrowObject( wbemLocatorComObj.createInstance().queryInterface( IJIDispatch.IID ) );
}
private static IJIDispatch toIDispatch( JIVariant comObjectAsVariant ) throws JIException
{
return (IJIDispatch) JIObjectFactory.narrowObject( comObjectAsVariant.getObjectAsComObject() );
}
public static void main( String[] args )
{
if ( args.length != 4 )
{
System.out.println( "Usage: " + EventLogListener.class.getSimpleName() + " domain host username password" );
return;
}
String domain = args[ 0 ];
String host = args[ 1 ];
String user = args[ 2 ];
String pass = args[ 3 ];
JISession dcomSession = null;
try
{
// Connect to DCOM on the remote system, and create an instance of the WbemScripting.SWbemLocator object to talk to WMI.
dcomSession = configAndConnectDCom( domain, user, pass );
IJIDispatch wbemLocator = getWmiLocator( host, dcomSession );
// Invoke the "ConnectServer" method on the SWbemLocator object via it's IDispatch COM pointer. We will connect to
// the default ROOT\CIMV2 namespace. This will result in us having a reference to a "SWbemServices" object.
JIVariant results[] =
wbemLocator.callMethodA( "ConnectServer", new Object[] { new JIString( host ), new JIString( WMI_DEFAULT_NAMESPACE ),
JIVariant.OPTIONAL_PARAM(), JIVariant.OPTIONAL_PARAM(), JIVariant.OPTIONAL_PARAM(), JIVariant.OPTIONAL_PARAM(), new Integer( 0 ),
JIVariant.OPTIONAL_PARAM() } );
IJIDispatch wbemServices = toIDispatch( results[ 0 ] );
// Now that we have a SWbemServices DCOM object reference, we prepare a WMI Query Language (WQL) request to be informed whenever a
// new instance of the "Win32_NTLogEvent" WMI class is created on the remote host. This is submitted to the remote host via the
// "ExecNotificationQuery" method on SWbemServices. This gives us all events as they come in. Refer to WQL documentation to
// learn how to restrict the query if you want a narrower focus.
final String QUERY_FOR_ALL_LOG_EVENTS = "SELECT * FROM __InstanceCreationEvent WHERE TargetInstance ISA 'Win32_NTLogEvent'";
final int RETURN_IMMEDIATE = 16;
final int FORWARD_ONLY = 32;
JIVariant[] eventSourceSet =
wbemServices.callMethodA( "ExecNotificationQuery", new Object[] { new JIString( QUERY_FOR_ALL_LOG_EVENTS ), new JIString( "WQL" ),
new JIVariant( new Integer( RETURN_IMMEDIATE + FORWARD_ONLY ) ) } );
IJIDispatch wbemEventSource = (IJIDispatch) JIObjectFactory.narrowObject( ( eventSourceSet[ 0 ] ).getObjectAsComObject() );
// The result of the query is a SWbemEventSource object. This object exposes a method that we can call in a loop to retrieve the
// next Windows Event Log entry whenever it is created. This "NextEvent" operation will block until we are given an event.
// Note that you can specify timeouts, see the Microsoft documentation for more details.
while ( true )
{
// this blocks until an event log entry appears.
JIVariant eventAsVariant = (JIVariant) ( wbemEventSource.callMethodA( "NextEvent", new Object[] { JIVariant.OPTIONAL_PARAM() } ) )[ 0 ];
IJIDispatch wbemEvent = toIDispatch( eventAsVariant );
// WMI gives us events as SWbemObject instances (a base class of any WMI object). We know in our case we asked for a specific object
// type, so we will go ahead and invoke methods supported by that Win32_NTLogEvent class via the wbemEvent IDispatch pointer.
// In this case, we simply call the "GetObjectText_" method that returns us the entire object as a CIM formatted string. We could,
// however, ask the object for its property values via wbemEvent.get("PropertyName"). See the j-interop documentation and examples
// for how to query COM properties.
JIVariant objTextAsVariant = (JIVariant) ( wbemEvent.callMethodA( "GetObjectText_", new Object[] { new Integer( 1 ) } ) )[ 0 ];
String asText = objTextAsVariant.getObjectAsString().getString();
System.out.println( asText );
}
}
catch ( Exception e )
{
e.printStackTrace();
}
finally
{
if ( null != dcomSession )
{
try
{
JISession.destroySession( dcomSession );
}
catch ( Exception ex )
{
ex.printStackTrace();
}
}
}
}
}
~
On the Java side, you'll need a library that allows you to make native calls. Sun offers JNI, but it sounds like sort of a pain. Also consider:
https://github.com/twall/jna/
http://johannburkard.de/software/nativecall/
http://www.jinvoke.com/
On the Windows side, the function you're after is OpenEventLog. This should allow you to access a remote event log. See also Querying for Event Information.
If that doesn't sound right, I also found this for parsing the log files directly (not an approach I'd recommend but interesting nonetheless):
http://msdn.microsoft.com/en-us/library/bb309026.aspx
http://objectmix.com/java/75154-regarding-windows-event-log-file-parser-java.html
Read this article.
JNA 3.2.8 has both methods to read and write from the Windows event log.
You can see an example of write in log4jna.
Here's an example of read:
EventLogIterator iter = new EventLogIterator("Application");
while(iter.hasNext()) {
EventLogRecord record = iter.next();
System.out.println(record.getRecordNumber()
+ ": Event ID: " + record.getEventId()
+ ", Event Type: " + record.getType()
+ ", Event Source: " + record.getSource());
}
If you want true event log access from a remote machine, you will have to find a library which implements the EventLog Remoting Protocol Specification. Unfortunately, I have not yet found any such library in Java. However, much of the foundation for implementing this protocol has already been laid by the JCIFS and JARAPAC projects. The protocol itself (if I'm not mistaken) runs on top of the DCE/RPC protocol (implemented by JARAPAC) which itself runs on top of the SMB protocol (implemented by JCIFS).
I have already been using JCIFS and JARAPAC to implement some of EventLog's cousin protocols, such as remote registry access. I may be blind, but documentation seemed a little scarce regarding JARAPAC. If you are interested in implementing this, I can share with you what I have learned when I get some spare time!
Later!
there are a million (and one) options here ;)
you could look at sigar
http://cpansearch.perl.org/src/DOUGM/hyperic-sigar-1.6.3-src/docs/javadoc/org/hyperic/sigar/win32/EventLog.html
mind the licensing though....
or you could be quick and dirty and just periodically execute (and capture the output)
D:>cscript.exe c:\WINDOWS\system32\eventquery.vbs /v
then use event filtering params to refine the results etc...
http://technet.microsoft.com/en-us/library/cc772995(WS.10).aspx

Categories