how can I Print adittional information to Command line Console?
Output now is:
C:\Users\admin\Desktop\java>java -jar pdf.jar
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
at readDataIn.main(readDataIn.java:31)
Code:
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
try {
String arg = args[0];
fileNameSource = "import/" + arg + ".xml";
fileNameTarget = "export/" + arg + ".pdf";
} catch (Exception e) {
// TODO: handle exception
**System.out.println("Personal-Number is missing");**
e.printStackTrace();
}
How can i give the information out, that the Personal Number ist Missing?
First of all, as a general rule you should check for possible exceptions before they actually occur if that is possible, which in your case it definitely is.
So instead of catching the ArrayIndexOutOfBounds insert an if statement that checks the length of the args array before accessing it.
if(args.length == 0){
// no argument has been provided
// handle error here
}
In terms of how to handle the error, there are many options available and depending of what you want to do either could be a good fit.
IllegalArgumentException
It is a common idiom in Java that whenever a function receives an invalid/ illegal argument to throw an IllegalArgumentException.
if (args.length == 0){
throw new IllegalArgumentException("Personal number is missing");
}
This will print the message that you have provided and the stack trace. However if your application should be a Command Line Interface (CLI) you should not use this kind of error handling.
Print message & exit program
if (args.length == 0){
// notice: "err" instead of "out": print to stderr instead of stdout
System.err.println("Personal number is missing");
// exit program with non-zero exit code as exit code == 0 means everything is fine
System.exit(1);
}
For more information on stdout and stderr see this StackOverflow question.
This is what many CLI applications and e.g. java itself does. When you type java fdsdfsdfs or some similar nonsense as an argument Java will give you an error message and exit with some non-zero return code ("1" in this case).
It is also common that CLI applications print an error message and following some usage information on how to correctly use the application or provide a help command so a user can get more information. This happens for example if you just enter java without any parameters.
So it is really up to you what you want to do.
If you are thinking of implementing a full featured CLI application with more (complex) commands with multiple options etc. you should consider using a CLI library like JCommander or Apache Commons CLI as parsing command line arguments can quickly get ugly. All these common things are already handled there.
Logging
In case your application is some script that will be executed in a non-interactive way logging the error to a file and exiting with a non-zero exit code might also be an option.
PS
Your code looks to me like it should not compile at all as you are not declaring a type for your variables fileNameSource and fileNameTarget.
Use String or var here (assuming you're running > Java 11).
String fileNameSource = "import/" + arg + ".xml";
var fileNameTarget = "export/" + arg + ".pdf";
You might also need to consider that your program name is part of the args array, so you might have more than 0 values in the array and therefore might need to adjust the if statements above.
You may be interested in picocli, which is a modern CLI library for Java and other JVM languages.
Picocli does some basic validation automatically, and results in very compact code that produces user-friendly applications. For example:
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
#Command(name = "myapp", mixinStandardHelpOptions = true, version = "1.0",
description = "This command does something useful.")
class MyApp implements Runnable {
#Parameters(description = "File name (without extension) of the file to import and export.")
private String personalNumber;
#Override
public void run() {
String fileNameSource = "import/" + personalNumber + ".xml";
String fileNameTarget = "export/" + personalNumber + ".pdf";
// remaining business logic
}
public static void main(String[] args) {
System.exit(new CommandLine(new MyApp()).execute(args));
}
}
If I run this class without any parameters, the following message is printed to the standard error stream, and the process finished with exit code 2. (Exit codes are customizable.)
Missing required parameter: '<personalNumber>'
Usage: myapp [-hV] <personalNumber>
This command does something useful.
<personalNumber> File name (without extension) of the file to import
and export.
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
The usage help message is created automatically from the descriptions of the command, and the descriptions of its options and positional parameters, but can be further customized.
Note how the mixinStandardHelpOptions = true annotation adds --help and --version options to the command. These options are handled by the library without requiring any further logic in the application.
Picocli comes with an annotation processor that makes it very easy to turn your application into a native image with GraalVM. Native images have faster startup time and lower runtime memory overhead compared to a Java VM.
Related
I invoke the external command within my Java app with Runtime.getRuntime().exec() or ProcessBuilder. Works fine but Veracode complains on it with CWE-78. I'm trying to use ESAPI wrapper to sanitise the input and path the check.
The arfifact is the latest
<dependency>
<groupId>org.owasp.esapi</groupId>
<artifactId>esapi</artifactId>
<version>2.2.3.1</version>
</dependency>
ESAPI.properties are
ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory
Logger.LogEncodingRequired=false
Logger.UserInfo=false
Logger.ClientInfo=false
Logger.LogApplicationName=false
Logger.ApplicationName=my-app
Logger.LogServerIP=false
IntrusionDetector.Disable=true
Executor.ApprovedExecutables=/usr/bin/less
The code is:
#Test
void esapiTest() throws ExecutorException {
Executor executor = DefaultExecutor.getInstance();
ExecuteResult executeResult = executor.executeSystemCommand(
new File("/usr/bin/less"),
new ArrayList<>(Collections.singletonList("/etc/hosts"))
);
System.out.println("out = " + executeResult.getOutput());
System.out.println("err = " + executeResult.getErrors());
}
The output is
out =
err = \/etc\/hosts: No such file or directory
As far as I got the issue is that ESAPI's UnixCodec sanitises all non-alpha character with the backslash. This is fine for the shell i.e.
/usr/bin/less \/etc\/hosts
but not for the ProcessBuilder that is under the hood.
What am I doing wrong? How to invoke the command?
I think your main "problem" is misunderstanding that the ESAPI Codecs that are used with the DefaultExecutor class are assuming that any "OS command injection" is being interpreted via a "command line interpreter", i.e., a "shell". You are not invoking a shell here. If you were, the shell would remove the (in this case) backslash escaped argument from your path for "/etc/hosts". So if this were written as (say) the command:
/bin/sh -c /usr/bin/less /etc/hosts
it would [sort of] work (except if you tried running it over HTTP, the input to 'less' my be hosed; but "/bin/cat" ought to work fine).
Instead, try writing your test something like this:
#Test
void esapiTest() throws ExecutorException {
Executor executor = ESAPI.executor();
File binSh = new File("/bin/sh").getCanonicalFile();
List params = new ArrayList();
params.add("-c");
//Use '/bin/cat' because 'less' may be troublesome
params.add("\"" + "/bin/cat" + "/etc/hosts" + "\"");
ExecuteResult executeResult = executor.executeSystemCommand(binSh, params);
System.out.println("out = " + executeResult.getOutput());
System.out.println("err = " + executeResult.getErrors());
}
Note 1: If you I'm not sure how user-friendly ProcessBuilder is with commands that eventually try to do ioctl system calls to set the tty device in 'raw' mode, like commands such as "vim" or "less", which is why I changed your "/usr/bin/less" to "/bin/cat". YMMV.
Note 2: In your ESAPI.properties file, you'd have to make sure that the property 'Executor.ApprovedExecutables' is set to whatever the canonical name of "/bin/sh" is on your system. E.g., on my system, "/bin/sh" is a symbolic link to "/bin/dash", so you would have to include something like
Executor.ApprovedExecutables=/bin/bash,/bin/dash
(or at least "/bin/dash") should work.
The below program reads the XML file and compress into gzip.
I have a couple of questions here.
Can I use the following program directly in JMeter BeanShell pre-processor?
I want to use the output variable as input to JSON request. Is it possible in Jmeter?
Screen shot and details will be appreciated.
public static void main(String[] args) throws Exception {
String line = null;
String sb = "";
File f=new File("D:\\RetailTransactionLog_9419_001_590.xml");
FileReader fr=new FileReader(f);
BufferedReader br=new BufferedReader(fr);
while((line=br.readLine())!=null)
{
sb= sb + line;
}
br.close();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzos = new GZIPOutputStream(baos);
gzos.write(sb.getBytes("UTF-8"));
gzos.close();
String base64CompressedString = Base64.getEncoder().encodeToString(baos.toByteArray());
System.out.println(base64CompressedString);
Of course, you can put your piece of java code directly in a JMeter BeanShell pre-processor and much more !
Insert you pre-processor component as child of your JSON request (as in my script example in attachment).
You don’t need to import java.io package like BufferedReader, ByteArrayOutputStream, File, FileReader, IOException, Base64…
Remove also the main signature public static void main(String[] args) …
You only have to import "java.util.zip.GZIPOutputStream" (A)
I’ve also replace your System.out.println(base64CompressedString) by log.info(base64CompressedString) just to visualize in the jmeter console your output (B)…
And finally add at the end, the code (C) to reference your result in the variable of your choice ("a" in my example).
You just have to call your variable after with ${a} in your json request like in my JMX script :
Unzip attachments http://uplea.com/dl/9F734367B43FB93 :
"ReadAndCompressMyFile.jmx" under /bin and put "test.xml" under C: or change the path in your code.
I’ve used a dummy sampler instead of your json request.
After running my script, you can see in View Result Tree (Request tab) and in the console, the value of "a" (corresponding to base64CompressedString).
PS : To run my script with the "dummy sampler", you need to add jmeter-plugins-dummy-0.1.jar under /lib/ext of your jmeter directory.
Hope to help you...
GZIP compression is rather "heavy" operation, if you will have lots of virtual users - it may become a bottleneck so consider using other approaches:
There is __base64Encode() function available via JMeter plugins
If for any reason it is not enough - go for JSR223 Sampler and Groovy language, Groovy is mostly compatible with Java so your code should work fine (just remove main method)
Specifically, how does it interpret arguments that are in quotes or that feature redirects from standard input (e.g. <)?
I've got the following string:
string cmd = "mail -s 'Work Order #%s' -c %s -r email#server.com %s < email.txt".printf(wo.get_text(), ownmail, outmail.get_text());
When I use
Posix.system(cmd);
The command runs as expected and an email is sent, with the body taken from email.txt.
When I use
Process.spawn_command_line_async(cmd);
I get the error from the mail command that 'option -c is not found' or words to that effect. When I lose the quotes around Work Order #%s and instead escape the spaces, the email sends (with the subject line containing the back slashes) but instead of getting the body of the message from email.txt, it treats email.txt as another recipient of the email (it shows up in my inbox with 'email.txt' under the To: section). The < is being ignored or dropped. To check things out, I used
Process.spawn_command_line_async("echo %s".printf(cmd));
This showed me that the quotes around the subject line were being dropped but the < was still there. I can use Posix.system() in my program but for the sake of simplicity and reducing dependencies (and being more idiomatic), I'd prefer to use Process.spawn_command_line(). What am I missing?
Thank you!
You probably want to play around with Shell.quote() and Shell.unquote() in your "".printf() arguments.
The Vala Process.spawn_command_line_async() function is bound to GLib's g_spawn_command_line_async () function. So a good place to start looking for more details is the GLib documentation. The GLib documentation states g_spawn_command_line_async() uses g-shell-parse-argv to parse the command line. This parses the command line so the "results are defined to be the same as those you would get from a UNIX98 /bin/sh, as long as the input contains none of the unsupported shell expansions."
Also on that page are g_shell_quote () and g_shell_unquote (). These functions are bound to Vala as Shell.quote () and Shell.unquote ().
mail only accepts the body of the message from STDIN and g_spawn_command_line_async() won't handle the redirect. So you will either need a command line tool that takes the body as an argument or using something like Subprocess instead.
Thanks to both AIThomas and Jens sending me looking in the right direction, I was able to get it working with the following code:
static int main(string[] args) {
string subject = "-s " + Shell.quote("Work Order #123131");
string cc = "-c ccemail#org.org";
string frommail = "-r " + "senderemail#org.org";
string[] argv = {"mail", subject, cc, frommail, "destinationemail#org.org"};
int standard_input;
int child_pid;
Process.spawn_async_with_pipes (
".",
argv,
null,
SpawnFlags.SEARCH_PATH,
null,
out child_pid,
out standard_input,
null,
null);
FileStream instream = FileStream.fdopen(standard_input, "w");
instream.write("This is what will be emailed\n".data);
return 0;
}
i have a new problem in image magick that look strange ..
i'm using mac osx snow leopard and i've installed image magick on it and it's working fine on command ..
but when i call it from the grails class like the following snippet it gives me
"Cannot run program "convert": error=2, No such file or directory"
the code is :-
public static boolean resizeImage(String srcPath, String destPath,String size) {
ArrayList<String> command = new ArrayList<String>(10);
command.add("convert");
command.add("-geometry");
command.add(size);
command.add("-quality");
command.add("100" );
command.add(srcPath);
command.add(destPath);
System.out.println(command);
return exec((String[])command.toArray(new String[1]));
}
private static boolean exec(String[] command) {
Process proc;
try {
//System.out.println("Trying to execute command " + Arrays.asList(command));
proc = Runtime.getRuntime().exec(command);
} catch (IOException e) {
System.out.println("IOException while trying to execute " );
for(int i =0 ; i<command.length; i++) {
System.out.println(command[i]);
}
return false;
}
//System.out.println("Got process object, waiting to return.");
int exitStatus;
while (true) {
try {
exitStatus = proc.waitFor();
break;
} catch (java.lang.InterruptedException e) {
System.out.println("Interrupted: Ignoring and waiting");
}
}
if (exitStatus != 0) {
System.out.println("Error executing command: " + exitStatus);
}
return (exitStatus == 0);
}
i've tried normal command like ls and it's ok so the problem is that grails can't find convert command itself.. is it a os problem or something?
(see lower for the answer)
I have run into the same problem. The issue appears to be something with Mac OS X specifically, as we have several Linux instances running without error. The error looks similar to the following:
java.io.IOException: Cannot run program "/usr/bin/ImageMagick-6.7.3/bin/convert /a/temp/in/tmpPic3143119797006817740.png /a/temp/out/100000726.png": error=2, No such file or directory
All the files are there, and in chmod 777 directories - and as you pointed out, running the exact command from the shell works fine.
My theory at this point is that imagemgick can not load some sort of library itself, and the "no such file" is in reference to an dylib or something along those lines.
I have tried setting LD_LIBRARY_PATH and a few others to no avail.
I finally got this working. Here is how I have it setup. I hope this helps.
The crux of the fix, for me, was I wrapped the 'convert' into a shell script, set a bunch of environment variables, and then call that shell script instead of convert directly:
(convertWrapper.sh)
export MAGICK_HOME=/usr/local/ImageMagick-6.7.5
export MAGICK_CONFIGURE_PATH=${MAGICK_HOME}/etc/ImageMagick:${MAGICK_HOME}/share/doc/ImageMagick/www/source
export PATH=${PATH}:${MAGICK_HOME}/bin
export LD_LIBRARY_PATH=${MAGICK_HOME}/lib:${LD_LIBRARY_PATH}
export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:${MAGICK_HOME}/lib
export MAGICK_TMPDIR=/private/tmp
echo "$#" >> /private/tmp/m.log 2>&1
/usr/local/ImageMagick-6.7.5/bin/convert -verbose "$#" >> /private/tmp/m.log 2>&1
(convertWrapper.sh)
Additionally, the convert call was doing some rather complicated stuff, so I added the parameter '-respect-parenthesis' (which may or may not have had an effect).
I am not sure how much of the environment variable setting is needed as I was stabbing in the dark for a while, but since this is only for my development box...
You need to work out what your PATH is set to when you run a command from Java. It must be different to the one you have when running from the terminal.
Are you running Grails (via Tomcat?) as a different user? It might have a different path to your normal user.
you might want to try one of the Image Plugins that are part of the grails ecosystem
http://www.grails.org/ImageTools+plugin
the grails path when the app is running in the server is probably different from running java from the command line
I do so:
Put "convert" file to /usr/bin
Then add to Config.groovy:
gk {
imageMagickPath = "/usr/bin/convert"
}
Then in my ImageService.groovy:
import org.springframework.web.context.request.RequestContextHolder as RCH
[..]
def grailsApplication = RCH.requestAttributes.servletContext.grailsApplication
def imPath = grailsApplication.config.gk.imageMagickPath
def command = imPath + " some_properties"
def proc = Runtime.getRuntime().exec(command)
So this way you get command like: /usr/bin/convert some_properties
And it works, but don't forget to put file "convert" to you location and use it with this location.
I want to launch a browser and load a web page using Java's Runtime exec. The exact call looks like this:
String[] explorer = {"C:\\Program Files\\Internet Explorer\\IEXPLORE.EXE",
"-noframemerging",
"C:\\ ... path containing unicode chars ... \\Main.html"};
Runtime.getRuntime().exec(explorer);
In my case, the path contains "\u65E5\u672C\u8A9E", the characters 日本語.
Apparently it's a java bug:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4947220
My question is: is there a viable workaround that can be done solely using Java? It appears that it is possible to write a JNI library for this, but I'd like to avoid that if possible. I have tried URI-encoding the path as ascii and writing the commands to a batch file, without success.
At the mentioned Java bug page you will find a workaround that is reported to work using ProcessBuilder and wrapping the parameters in environment variables. Here is the source code from Parag Thakur:
String[] cmd = new String[]{"yourcmd.exe", "Japanese CLI argument: \ufeff\u30cb\u30e5\u30fc\u30b9"};
Map<String, String> newEnv = new HashMap<String, String>();
newEnv.putAll(System.getenv());
String[] i18n = new String[cmd.length + 2];
i18n[0] = "cmd";
i18n[1] = "/C";
i18n[2] = cmd[0];
for (int counter = 1; counter < cmd.length; counter++)
{
String envName = "JENV_" + counter;
i18n[counter + 2] = "%" + envName + "%";
newEnv.put(envName, cmd[counter]);
}
cmd = i18n;
ProcessBuilder pb = new ProcessBuilder(cmd);
Map<String, String> env = pb.environment();
env.putAll(newEnv);
final Process p = pb.start();
Create a .bat/.sh file. Write your commands to that file and execute it. Make sure that you have changed the code page to unicode in case of windows(chcp 65001).
For example to execute the below command in windows:
String[] command ={"C:\\aconex\\学校\\mysql\\bin\\mysql", "-esource", "大村箕島a\\data.sql"};
Create a temp file called temp.bat and execute with the Runtime.getRuntime().exec
temp.bat
chcp 65001
C:\aconex\学校\mysql\bin\mysql -esource 大村箕島a\data.sql
These are the two solutions I considered, each of which are more or less workarounds:
Create a temp html redirect file which will redirect the browser to the proper page.
Note that IE will expect unencoded unicode for local files, while other browsers may accept only uri-encoded file paths
Use the short filename for the windows file. It won't contain unicode characters.
We've been using a JNI to start processes from Java for years. Neither Runtime.exec or ProcessBuilder will work, and it seems unlikely that they will fix this, given how long it's been already.
However, you should be able to work around the issue by using the input stream, a socket, or environment variables to pass parameters. If you don't have direct control over the executable, you'll have to make a wrapper.
You could use JNA. With version 3.3.0 or later call CreateProcess:
WinBase.PROCESS_INFORMATION.ByReference processInfo =
new WinBase.PROCESS_INFORMATION.ByReference();
WinBase.STARTUPINFO startupInfo = new WinBase.STARTUPINFO();
String command = "C:\\Program Files\\Internet Explorer\\IEXPLORE.EXE " +
"-noframemerging \"C:\\\u65E5\u672C\u8A9E\\Main.html\"";
if (!Kernel32.INSTANCE.CreateProcess(
null, // Application name, not needed if supplied in command line
command, // Command line
null, // Process security attributes
null, // Thread security attributes
true, // Inherit handles
0, // Creation flags
null, // Environment
null, // Directory
startupInfo,
processInfo))
{
throw new IllegalStateException("Error creating process. Last error: " +
Kernel32.INSTANCE.GetLastError());
}
// The CreateProcess documentation indicates that it is very important to
// close the returned handles
Kernel32.INSTANCE.CloseHandle(processInfo.hThread);
Kernel32.INSTANCE.CloseHandle(processInfo.hProcess);
long pid = processInfo.dwProcessId.longValue();
Redirecting output from the child process is a bit harder but not impossible.
I think you can use Apache Commons Exec library or ProcessBuilder to give a try;)