Invoking AS400 RPG From Java - java

I have a very limitied (0) knowledge on AS400 and RPG. But we have a urgent requirement where we need to invoke a RPG program from a java class. So I found that we can achieve it through JTOpen. But I am stuck at declaring the ProgramParameter list. I have the following information about RPG Program
Program name: ZM30000R
Parameters:
Branch 7,0 (Numeric)
Account type 2 (01-cheque,02 savings)
Account Number 20 (character)
Error code 7 (character)
DR/CR indicater 1 (character D,C)
But no clue about what is the intput and output .How to declare the ProgramParameter. I have done as below. I cannot test as well because I dont have connectivity to these systems.
// Create AS400 Text objects for the different lengths
// of parameters you are sending in.
AS400Text branchTxt = new AS400Text(7);
AS400Text accntTypeTxt = new AS400Text(2);
AS400Text accntNumberTxt = new AS400Text(20);
AS400Text errorCodeTxt = new AS400Text(7);
AS400Text DCIndicatorTxt = new AS400Text(1);
// declare and instantiate your parameter list.
ProgramParameter[] parmList = new ProgramParameter[5];
// assign values to your parameters using the AS400Text class to convert to bytes
// the second parameter is an integer which sets the length of your parameter output
parmList[0] = new ProgramParameter( branchTxt.toBytes(branch),7);
parmList[1] = new ProgramParameter( accntTypeTxt.toBytes(accntTypeTxt),2);
parmList[2] = new ProgramParameter( accntNumberTxt.toBytes(accntNumberTxt),20);
parmList[3] = new ProgramParameter( errorCodeTxt.toBytes(""),7);
parmList[4] = new ProgramParameter( DCIndicatorTxt.toBytes(indicator),5);
Any help will be really highly useful.
Thanks and Regards,
Srinivas

Well, I do have a clue just by the description of the parameters. Branch, account type and account number are IN. You need that information for a financial booking or transaction. The error code is appearently OUT. In my experience with financial systems it's reasonable normal that the API returns the way the amount is booked. Normally one would use the sign, but in financial systems the (D)ebit or (C)redit is the better way.
The API is very likely the API of a financial system. If that is true, then I'm missing the amount. Are you sure you've the complete description?
Notice that the first parameter is numeric. You're not in luck. The iSeries and RPG are not very forgiving about the type of a numeric. One can choose from Bit, Zoned, Packed, Decimal, Integer, Float and so on. If the RPG is really RPG instead of ILE RPG, then you can bring that down to Zoned, Packed and Byte.
I assume you've access to the iSeries. Then you can watch the program call, debug information and dump information. That will help you if you have to do "trial and error". If you don't have access, the road will be very hard. You'll receive an error in your java class if the program call is not succesfull. But it will be hard to identify the real error without the information from the iSeries yourself. Therefore, access is really required.

Your sample is mostly on the right track. But your branch parameter is numeric. So you should use AS400ZonedDecimal instead of AS400Text:
AS400ZonedDecimal branchNbr = new AS400ZonedDecimal(7,0)
The RPG program may be expecting packed instead of zoned. No big deal, just use AS400PackedDecimal instead.
As you construct your ProgramParameter object, your constructor requirements are different depending on if they are input or output parameters to your program. For input parameters, just pass the toBytes() results. There is no need to include the length. For output-only parameters, just pass the length.
I agree with Robert's answer that there is some missing information, but his assumptions on the outputness of the error code seems valid. I would guess, however, that the DCIndicator parameter is input since your sample passes a value. For the error code parameter, after your program call, you'll need to extract the value and do something with it. Given what you have already, here is how the program call would work. Take note that I specified a library name of "MyLibrary". That is for example purposes. You will have to determine which library your program object is in.
ProgramCall pgm = new ProgramCall(as400, QSYSObjectPathName.toPath("MyLibrary","ZM30000R","PGM"), parmList);
if (pgm.run() == true) {
String sErrorCode = (String) errorCodeTxt.toObject(parmList[3].getOutputData());
//Do something with your output data.
}
else {
AS400Message[] messageList = pgm.getMessageList();
for (int i=0; i<messageList.length; i++) {
String sMessageID = messageList[i].getID();
String sMessageText = messageList[i].getText();
//Do something with the error messages
}
}
Something else to consider is library lists. Does the RPG program expect certain libraries to be in the library list? If so, you should issue CommandCalls to add the libraries to the library list before calling the program.

FWIW: It's a lot easier to call IBM i host programs & service programs using PCML rather than ProgramCall.
The compilers will even generate the PCML document for you.
See http://javadoc.midrange.com/jtopen/com/ibm/as400/data/ProgramCallDocument.html for details.

If you don't have connectivity, then you really can't do what is asked. How do you test it? Is there numeric parameters or are they all character?

Related

Call a method from a string

I am making a chat. and i have everything working. But i would like to implement commands that the user can run by typing / then the command. I was thinking i would test for the '/' char at index 0. And if thats true i would run that "code". So the user would essentially be calling a method. but i cant figure out how to convert the string into java code. Help would be appreciated. And if anyone has an easier method of how to implement commands feel free to let me know.
Where
String userinput = "/test()";
Thanks
Letting the user actually run code via his input would require 'reflection' (you will find a lot about this by just googling e.g. 'java reflection').
BUT ATTENTION: Letting the user execute Java code this way is a big security problem!
I think the better approach for you would be something like this:
public void executeUserCommand(final String userInput) {
String commandFromUser = // parse fromUserInput
String param = // parse fromUserInput
switch (commandFromUser) {
case "command1":
// call method with 'param'
break;
case "command2":
// call method with 'param'
break;
default:
// tell the user that his command was not found
break;
}
}
You're going to need to look into the Java reflection API to really do anything more than very basic stuff.
At a high level, what I would do is something like this:
/test
or
/msg "John Smith" "XYZ"
You can then parse that into three tokens which gives you msg and two parameters.
What you'll want to do is use reflection to look at the object that handles the chat functions and look up any method named msg that takes two String paramters.
If you decide that allowing numbers and booleans and such is going to be part of it, you're going to get into the weeds pretty fast with the parsing side so be aware of that.
Update: saw this:
"/rep(5,"Hello");
That's going to be a lot harder to parse than /rep 5 "Hello" or /rep 5, "Hello"

google prediction api, java giving different result to web interface

I am using Google's prediction API. I have trained a few models and made predictions using Google's web interface. I want to make a few thousand predictions but the web interface only lets you make one prediction at a time. I have thus slightly adapted the "prediction-cmdline-sample" which is a sample for using the Java library to interface with the Google prediction API. However the results I am getting using the Java library are different to the web interface.
The code which I use to make a prediction is:
private static String predict(Prediction prediction, String text) throws IOException {
Input input = new Input();
InputInput inputInput = new InputInput();
inputInput.setCsvInstance(Collections.<Object>singletonList(text));
input.setInput(inputInput);
Output output = prediction.trainedmodels().predict(PROJECT_ID, MODEL_ID, input).execute();
return output.getOutputValue();
}
The method returns 0.500305 irrespective of what input I give (0.500305 is roughly the average value of the first column of the training data).
Any suggestions to fix this issue would be greatly appreciated.
If anyone knows of another way to make a few thousand predictions please also let me know.
I found my problem: I wasn't formatting my input correctly. The code
Collections.singletonList(text)
is incorrect for double type inputs, it is only correct for String inputs. What made it difficult to find this error is that the prediction API doesn't throw an error if incorrect inputs are given, it just returns some result.

Bukkit Minecraft setIngredient Material id colon issue

I am trying to use a full id of a block in the getmaterial part of the code below. this does not work any way that i try.
I cannot find any documentation supporting this issue of handling an id which contains a 'colon :' .
Snip: (Example the 5758:6 below does not work and the string name neither.)
emerald.setIngredient('L', Material.getMaterial("5758:6"));
Material.getMaterial(406) //this is expecting an integer so i cannot give it two numbers
Material.getMaterial(406:1) //this fails as is expecting int
Assuming that emerald is a ShapedRecipe object (since you're using the setIngredient(char, Material) method), then you can also use the setIngredient(char, MaterialData) method instead. You could construct the MaterialData object you want using the (deprecated...) MaterialData(int, byte) constructor. Your new code would look like:
emerald.setIngredient('L', new MaterialData(5758, 6));
The colon in the "full id of a block" is just separating the "id" and "data" values. I think this will do what you're looking for, but if not, let me know so I can clarify.
I don't think you're supposed to be dealing with that number colon thing. Instead, if you want to get to, say, the BRICK material, use Material.BRICK or Material.valueOf("BRICK"). If you want to find the name of a Material m, use m.name() which returns a String.

Output of JESS in Java

I want to send a "fact" to a JESS file within java and get the results back. I basicly batch the JESS file and then send my data (structure in here) into the engine by .add(). I tried to get the JESS results, which should be a string, into a "Value".
Rete engine = new Rete();
engine.batch("file.clp");
Value = AAAnull;
try{
engine.add(structure)
AAA = engine.eval("(run)");
} catch ...
System.out.println(AAA);
The result is always a number, although the result should be a string. I have worked it out in a simple java project and the AAA is returning the string, but here it is not working.
The (run) function returns the number of rules fired; that's the number you're seeing here.
The real results of running your program are the side effects it causes; getting the result in Java depends on what side effects you're expecting. That may mean anything from collecting output printed to the screen, finding newly created facts in working memory, or having your Jess program call Java methods that effect the outside world. Without seeing the contents of file.clp I can't say what you're expecting, but all of these things listed are covered in the Jess manual; the phrases above are links to the appropriate sections. I'm happy to answer any followup questions you might have.

Benefits of MessageFormat in Java

In a certain Java class for a Struts2 web application, I have this line of code:
try {
user = findByUsername(username);
} catch (NoResultException e) {
throw new UsernameNotFoundException("Username '" + username + "' not found!");
}
My teacher wants me to change the throw statement into something like this:
static final String ex = "Username '{0}' not found!" ;
// ...
throw new UsernameNotFoundException(MessageFormat.format(ex, new Object[] {username}));
But I don't see the point of using MessageFormat in this situation. What makes this better than simple string concatenation? As the JDK API for MessageFormat says:
MessageFormat provides a means to produce concatenated messages in language-neutral way. Use this to construct messages displayed for end users.
I doubt that the end users would see this exception since it would only be displayed by the application logs anyway and I have a custom error page for the web application.
Should I change the line of code or stick with the current?
Should I change the line of code or stick with the current?
According to your teacher your should.
Perhaps he wants you to learn different approaches for the same thing.
While in the sample you provided it doesn't make much sense, it would be useful when using other types of messages or for i18n
Think about this:
String message = ResourceBundle.getBundle("messages").getString("user.notfound");
throw new UsernameNotFoundException(MessageFormat.format( message , new Object[] {username}));
You could have a messages_en.properties file and a messages_es.properties
The first with the string:
user.notfound=Username '{0}' not found!
And the second with:
user.notfound=¡Usuario '{0}' no encontrado!
Then it would make sense.
Another use of the MessageFormat is described in the doc
MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
double[] filelimits = {0,1,2};
String[] filepart = {"no files","one file","{0,number} files"};
ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
form.setFormatByArgumentIndex(0, fileform);
int fileCount = 1273;
String diskName = "MyDisk";
Object[] testArgs = {new Long(fileCount), diskName};
System.out.println(form.format(testArgs));
The output with different values for fileCount:
The disk "MyDisk" contains no files.
The disk "MyDisk" contains one file.
The disk "MyDisk" contains 1,273 files.
So perhaps your teacher is letting you know the possibilities you have.
Teachers way allows for easier localisation as you can extract a single string rather than several little bits.
But I don't see the point of using
MessageFormat in this situation
In that specific situation it doesn't buy you much. In general, using MessageFormat allows you to externalize those messages in a file. This allows you to:
localize the messages by language
edit the messages outside without
modifying source code
Personally, I would stick with the concatenation way, but it's just a matter of preference. Some people think it's cleaner to write a string with variables as one string, and then pass the params as a list after the string. The more variables you have in the string, the more sense using MessageFormat makes, but you only have one, so it's not a big difference.
Of course if you don't need internationalization, it is overhead, but basically the code as the teach wants it is more "internationalizable" (although not actually internationalized as the string is still hard coded).
Since this is a teaching situation, though he may be doing it just to show you how to use those classes rather than as the best way to program for this specific example.
In terms of the best way to program, if internationalization is a requirement, then you need to code for it, if not then don't. I just adds overhead and time (to write the code) for no reason.
Pace the other answers, the importance of MessageFormat for internationalizion is not just that it makes it easier to make an external file. In other languages the location of the parameter may be different in the sentence structure of the messages, so using MessageFormat allows you to change that per language, something that string concatenation would not.
One advantage that I see in using MessageFormat is that when you decide to externalize your strings, it would be much easier to build the message and also, it makes more sense to see "Username '{0}' not found!" in your resource file as one string accessed by only one ID.

Categories