Execute command from Java code deployed on Apache Tomcat - java

I was trying to convert a DOCX file to PDF file, found this vb script code which perfectly converts DOCX to PDF file, it uses .bat file for file generation. The code can be executed through java.
I am facing a strange problem, when I execute the code on my local machine, the file is generated, but when I deployed the app on Server , the code executes with no errors but the file is not generated.
Do we need any permission to execute commands through java?
Following is the details:
Server Operating system : Windows Server 2012 R2 Standard
Application server :Apache Tomcat 7.0.75
Code:
1)Java
public static void generatePDF() {
try {
File file = new File("C:\\Docx_To_Pdf_Converter\\errorLog.txt");
PrintStream printStreamToFile = new PrintStream(file);
System.setOut(printStreamToFile);
String docToPdf = "C:\\Docx_To_Pdf_Converter\\doc2pdf.bat";
File docPath = new File("C:\\Docx_To_Pdf_Converter\\Letter1.docx");
File pdfPath = new File("C:\\Docx_To_Pdf_Converter\\LetterPDF.pdf");
String command = String.format("%s %s %s", docToPdf, docPath, pdfPath);
Process process = Runtime.getRuntime().exec(command);
// The next line is optional and will force the current Java
//thread to block until the script has finished its execution.
process.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
and the .bat file code:
#Echo off
pushd %~dp0
cscript C:\Docx_To_Pdf_Converter\doc2pdf.vbs %1 %2
vbscript code which actually converts the docx to pdf
Const wdFormatPDF = 17 ' PDF format.
Const wdFormatXPS = 18 ' XPS format.
Const WdDoNotSaveChanges = 0
Dim arguments
Set arguments = WScript.Arguments
Function CheckUserArguments()
If arguments.Unnamed.Count < 1 Or arguments.Unnamed.Count > 2 Then
WScript.Echo "Use:"
WScript.Echo "<script> input.doc"
WScript.Echo "<script> input.doc output.pdf"
WScript.Quit 1
End If
End Function
// Transforms a doc to a pdf
Function DocToPdf( docInputFile, pdfOutputFile )
Dim fileSystemObject
Dim wordApplication
Dim wordDocument
Dim wordDocuments
Dim baseFolder
Set fileSystemObject = CreateObject("Scripting.FileSystemObject")
Set wordApplication = CreateObject("Word.Application")
Set wordDocuments = wordApplication.Documents
docInputFile = fileSystemObject.GetAbsolutePathName(docInputFile)
baseFolder = fileSystemObject.GetParentFolderName(docInputFile)
If Len(pdfOutputFile) = 0 Then
pdfOutputFile = fileSystemObject.GetBaseName(docInputFile) + ".pdf"
End If
If Len(fileSystemObject.GetParentFolderName(pdfOutputFile)) = 0 Then
pdfOutputFile = baseFolder + "\" + pdfOutputFile
End If
//' Disable any potential macros of the word document.
wordApplication.WordBasic.DisableAutoMacros
// 'from below line the code does not executes
Set wordDocument = wordDocuments.Open(docInputFile)
wordDocument.SaveAs pdfOutputFile, wdFormatPDF
wordDocument.Close WdDoNotSaveChanges
wordApplication.Quit WdDoNotSaveChanges
Set wordApplication = Nothing
Set fileSystemObject = Nothing
End Function
// ' Execute script
Call CheckUserArguments()
If arguments.Unnamed.Count = 2 Then
Call DocToPdf( arguments.Unnamed.Item(0), arguments.Unnamed.Item(1) )
Else
Call DocToPdf( arguments.Unnamed.Item(0), "" )
End If
Set arguments = Nothing

It is not possible to give you a 100% guaranteed answer as we don't have access to your deployment server, but here's what I think is happening there.
If the .bat file were missing or not executable for some reason, then you would get an IOException in your Java code. Since you didn't get an exception, clearly the .bat file was found and executed.
However, whatever is within the .bat file is not being executed as you expect. Either cscript.exe is missing, or the .vbs file is missing. The way your code is written, you would not be aware of this. All you would see is a non-zero status return from the waitfor() method, and you don't bother to check that. Therefore you have no knowledge of what actually happened.
At the very minimum you should change the waitfor() method invocation to:
int rc = process.waitFor();
System.out.printf("Process returned %d\n", rc);
This will tell you the return status from the attempt to execute the .bat file. If it's not zero, then you have a problem, and I'm 99.999% sure you will find this to be non-zero. To troubleshoot this you will need to capture the output from the command. The following is a highly simplified (as in no error handling; that's up to you) example:
Process proc = Runtime.getRuntime().exec(command);
BufferedReader procOutput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line;
while((line = procOutput.readLine()) != null)
{
System.out.println(line);
// Or whatever you need to do to in your environment, such
// as log the output or examine it to ensure the script did
// what you want
}
int rc = proc.waitFor();
System.out.printf("Process returned %d\n", rc);
NOTE The above is not production-level code, it is only an example of how to use the API. This works ONLY if the external command requires no redirected input; if it does require input then you need to handle input and output in separate threads to prevent deadlocks.
Also, you should consider using ProcessBuilder as it is much more flexible. For example, it allows you to redirect the process' output to append to a log file, which you should probably implement.

Related

How can I get all errors when executing a command?

I'm developing a java program, at a certain point in the program I need to execute some commands and show all the errors returned by that command. But I can only show the first one.
This is my code:
String[] comando = {mql,"-c",cmd};
File errorsFile = new File("C:\\Users\\Administrator2\\Desktop\\errors.txt");
ProcessBuilder pb = new ProcessBuilder(comando);
pb.redirectError(errorsFile);
Process p = pb.start();
p.waitFor();
String r = errorsFile.getAbsolutePath();
Path ruta = Paths.get(r);
Charset charset = Charset.forName("ISO-8859-1");
List<String> fileContents = Files.readAllLines(ruta,charset);
if (fileContents.size()>0){
int cont = 1;
for(String str : fileContents){
System.out.println("Error"+cont);
System.out.println("\t"+str);
cont++;
}
}
else{
//other code
}
In this case I know that there are more than one errors, so I expect more than one output but as you can see in the photo I get only one.
I think the key here might be that ProcessBuilder's redirectError(File file) is actually redirectError (Redirect.to(file)) .
From Oracle's documentation of ProcessBuilder class:
This is a convenience method. An invocation of the form redirectError(file) behaves in exactly the same way as the invocation redirectError (Redirect.to(file)).
Most example's I have seen use Redirect.appendTo(File file) rather than Redirect.to(file). The documentation may explain why.
From Oracle's documentation of ProcessBuilder.Redirect :
public static ProcessBuilder.Redirect to(File file)
Returns a redirect to write to the specified file. If the specified file exists when the subprocess is started, its previous contents will be discarded.
public static ProcessBuilder.Redirect appendTo(File file)
Returns a redirect to append to the specified file. Each write operation first advances the position to the end of the file and then writes the requested data.
I would try replacing
pb.redirectError(errorsFile)
with
pb.redirectError(Redirect.appendTo(errorsFile))
and see if you get more lines that way.
Have you debugged and checked the contents of fileContents?
EDIT: Sorry, it should be a comment, but can't do it yet :(

Java - How can i request Administrator privileges during runtime? [duplicate]

A nasty problem popped out with my software. I am making a program that interacts with another existing software (a game). User has reported that he runs the game with administrator privileges and under that circumstances, my program stops working for him.
Short investigation revealed that some people really need to run the game under administrator account and some don't. It would be great if my program would be able to detect this and warn user if the game is running under administrator account:
If the user clicks "Elevate", I'd like to ask windows to elevate the java.exe running my jar file and invoke the typical UAC dialog.
Obviously, this time the question would not be about java updater but JRE
My question is: Is this possible? Can windows elevate my java.exe instance's privilege? Does java have a way to do it? Or can I use command line command?
I want to avoid restarting the program (though it wouldn't probably be such a big deal).
Edit:
If you look in the comments, you'll see that there's no avoiding the restart of an application - process can only start elevated, not become elevated. This kinda shifts the question, unfortunately. Basically, it now sounds more like: "How to restart my application with admin rights?". Unless, of course, there's a trick like two java.exe sharing one jar...
If still of interest: In Windows 7 my JavaElevator works. It elevates a running Java process when used in the main method of the Java application. Simply add -elevate as last program parameter and use the elevator in the main method.
The elevator class:
package test;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Kernel32Util;
import com.sun.jna.platform.win32.ShellAPI;
import com.sun.jna.platform.win32.WinDef;
/**
* Elevates a Java process to administrator rights if requested.
*/
public class JavaElevator {
/** The program argument indicating the need of being elevated */
private static final String ELEVATE_ARG = "-elevate";
/**
* If requested, elevates the Java process started with the given arguments to administrator level.
*
* #param args The Java program arguments
* #return The cleaned program arguments
*/
public static String[] elevate(String[] args) {
String[] result = args;
// Check for elevation marker.
boolean elevate = false;
if (args.length > 0) {
elevate = args[args.length - 1].equals(ELEVATE_ARG);
}
if (elevate) {
// Get the command and remove the elevation marker.
String command = System.getProperty("sun.java.command");
command = command.replace(ELEVATE_ARG, "");
// Get class path and default java home.
String classPath = System.getProperty("java.class.path");
String javaHome = System.getProperty("java.home");
String vm = javaHome + "\\bin\\java.exe";
// Check for alternate VM for elevation. Full path to the VM may be passed with: -Delevation.vm=...
if (System.getProperties().contains("elevation.vm")) {
vm = System.getProperty("elevation.vm");
}
String parameters = "-cp " + classPath;
parameters += " " + command;
Shell32.INSTANCE.ShellExecute(null, "runas", vm, parameters, null, 0);
int lastError = Kernel32.INSTANCE.GetLastError();
if (lastError != 0) {
String errorMessage = Kernel32Util.formatMessageFromLastErrorCode(lastError);
errorMessage += "\n vm: " + vm;
errorMessage += "\n parameters: " + parameters;
throw new IllegalStateException("Error performing elevation: " + lastError + ": " + errorMessage);
}
System.exit(0);
}
return result;
}
}
Usage in the main method of the Java application:
public static void main(String[] args) {
String[] args1 = JavaElevator.elevate(args);
if (args1.length > 0) {
// Continue as intended.
...
I know, this is a very basic implementation - sufficient for one of my daily hiccups: Starting an elevated process from Eclipse. But maybe it points someone in some dicrection...
As has been pointed in comments, sadly the Java (or any other process) cannot be elevated while running. While in the case of JWM, it could be theoretically possible to move whole program context from normal user java.exe to elevated one, I don't think it's possible. I hope some day someone will come and tell me I'm wrong.
Surprisingly, even with restart in place, this was a tricky task that took me a while to figure out.
The non java part
First, how do we exactly run a program elevated from command line? There's an answer and you can see it's not simple. But we can break it to this VBS script:
Set UAC = CreateObject("Shell.Application")
UAC.ShellExecute "program name", "command line parameters", "working directory", "runas", 1
Soon, it also turns out that we won't have any success running java.exe from VBS script. In the end, I decided to run a helper batch file. Finally, here (answer to question in the last link) we have a complete set of two scripts which really run the given .jar file elevated. Here's improved version that allows quick testing by drag'n'dropping the Jar file on it:
' Require first command line parameter
if WScript.Arguments.Count = 0 then
MsgBox("Jar file name required.")
WScript.Quit 1
end if
' Get the script location, the directorry where it's running
Set objShell = CreateObject("Wscript.Shell")
strPath = Wscript.ScriptFullName
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.GetFile(strPath)
strFolder = objFSO.GetParentFolderName(objFile)
'MsgBox(strFolder)
' Create the object that serves as runnable something
Set UAC = CreateObject("Shell.Application")
' Args:
' path to executable to run
' command line parameters - first parameter of this file, which is the jar file name
' working directory (this doesn't work but I use it nevertheless)
' runas command which invokes elevation
' 0 means do not show the window. Normally, you show the window, but not this console window
' which just blinks and disappears anyway
UAC.ShellExecute "run-normally.bat", WScript.Arguments(0), strFolder, "runas", 0
WScript.Quit 0
The Java part
Java part is more straightforward. What we need to do is to open new process and execute the prepared scripts in it.
/**
* Start this very jar file elevated on Windows. It is strongly recommended to close any existing IO
* before calling this method and avoid writing anything more to files. The new instance of this same
* program will be started and simultaneous write/write or read/write would cause errors.
* #throws FileNotFoundException if the helper vbs script was not found
* #throws IOException if there was another failure inboking VBS script
*/
public void StartWithAdminRights() throws FileNotFoundException, IOException {
//The path to the helper script. This scripts takes 1 argument which is a Jar file full path
File runAsAdmin = new File("run-as-admin.vbs");;
//Our
String jarPath;
//System.out.println("Current relative path is: " + s);
try {
jarPath = "\""+new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getAbsolutePath()+"\"";
} catch (URISyntaxException ex) {
throw new FileNotFoundException("Could not fetch the path to the current jar file. Got this URISyntax exception:"+ex);
}
//If the jar path was created but doesn't contain .jar, we're (most likely) not running from jar
//typically this happens when running the program from IDE
//These 4 lines just serve as a fallback in testing, should be deleted in production
//code and replaced with another FileNotFoundException
if(!jarPath.contains(".jar")) {
Path currentRelativePath = Paths.get("");
jarPath = "\""+currentRelativePath.toAbsolutePath().toString()+"\\AutoClient.jar\"";
}
//Now we check if the path to vbs script exists, if it does we execute it
if(runAsAdmin.exists()) {
String command = "cscript \""+runAsAdmin.getAbsolutePath()+"\" "+jarPath;
System.out.println("Executing '"+command+"'");
//Note that .exec is asynchronous
//After it starts, you must terminate your program ASAP, or you'll have 2 instances running
Runtime.getRuntime().exec(command);
}
else
throw new FileNotFoundException("The VBSScript used for elevation not found at "+runAsAdmin.getAbsolutePath());
}
This is my version. It creates a VBScript script, then executes it. This only works if the program that is being run is in a jar file, so you will have to run your IDE as administrator to actually test your program.
public static void relaunchAsAdmin() throws IOException {
relaunchAsAdmin(ThisClass.class); //Change ThisClass to the class that this method is in
}
public static void relaunchAsAdmin(Class<?> clazz) throws IOException {
if(isCurrentProcessElevated()) {
return;
}
final String dir = System.getProperty("java.io.tmpdir");
final File script = new File(dir, "relaunchAsAdmin" + System.nanoTime() +
".vbs");
try {
script.createNewFile();
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(script));
osw.append("Set s=CreateObject(\"Shell.Application\")" + ln + "s.ShellExecute \"" +
System.getProperty("java.home") + "\\bin\\java.exe" + "\",\"-jar \"\"" +
new File(clazz.getProtectionDomain().getCodeSource(
).getLocation().toURI()).getAbsolutePath() + "\"\"\",,\"runas\",0" +
ln + "x=createObject(\"scripting.fileSystemObject\").deleteFile(" +
"WScript.scriptfullname)");
osw.close();
if(System.getenv("processor_architecture").equals("x86")) {
Runtime.getRuntime().exec("C:\\Windows\\System32\\wscript.exe \"" +
script.getAbsolutePath() + "\"");
} else {
Runtime.getRuntime().exec("C:\\Windows\\SysWoW64\\wscript.exe \"" +
script.getAbsolutePath() + "\"");
}
} catch(URISyntaxException e) {
e.printStackTrace();
}
Runtime.getRuntime().exit(0);
}
Note that it is a bit messy. I have been using this method before, so it has been line wrapped to 100 characters (except the comment I wrote for this answer). The
isCurrentProcessElevated()
method will have to be implemented in one way or another. You could try using JNI, or you could use a pure Java method, such as writing in the Program Files or System32 directory and seeing if it failed.
Obviously, this solution will only work on Windows. I never needed to elevate on Linux or Mac systems (mainly because I don't have any Mac systems, and I don't use Linux - I just play with it).

Run Beyond Compare4 Script from Java?

Beyond compare provides a script to get the Report in a html format.
I want to run the script through java to get the report in a particular folder
but i am getting error everytime.
MyScript
text-report layout:Interleived &
options:ignore-unimportant,display-context &
output-to:%3 output-options:html-color %1 %2
My java call to the script
String left = "D:\\DatabaseModifier\\VIS VS EBZVIS\\VIS_Lookup_left.xls";
String right = "D:\\DatabaseModifier\\VIS VS EBZVIS\\EBZVIS_Lookup_right.xls";
try{
Process process = new ProcessBuilder("C:\\Program Files\\Beyond Compare4\\BCompare", "#D:\\DatabaseModifier\\MyScript.txt",left,right,"D:\\DatabaseModifier\\check.html").start();
}catch(Exception e){
}
Earlier there was no check.html i created the file still getting the same error
After hours of searching and hit and trail this is the script
data-report layout:Interleaved options:ignore-unimportant output-to:"%3" output-options:html-color "%1" "%2"
and the java call
String left = "D:\\DatabaseModifier\\VIS VS EBZVIS\\VIS_Lookup_left.xls";
String right = "D:\\DatabaseModifier\\VIS VS EBZVIS\\EBZVIS_Lookup_right.xls";
try{
Process process = new ProcessBuilder("C:\\Program Files\\Beyond Compare 4\\BCompare", "#D:\\DatabaseModifier\\MyScript.txt",left,right,"D:\\DatabaseModifier\\check.html").start();
}catch(Exception e){
}

Java ProcessBuilder and VBS script issue

Am going round in circles here trying to execute a bit of VBScript from java using processbuilder. I'm running my java code as part of a tomcat servlet. If I run the same command from a command line - its running fine. Have tried opening permissions right up (everyone=full perms)
Java Code (excerpt):
ProcessBuilder pb = new ProcessBuilder("cscript", "C:\\Users\\Public\\Documents\\office2pdf.vbs", "C:\\Users\\Public\\Documents\\temp\\22.doc");
Process pr = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(pr.getErrorStream()));
String line = null;
StringBuilder sb = new StringBuilder("");
while ( (line = reader.readLine()) != null) {
sb.append(line);
}
int i = pr.waitFor() ;
getServletContext().log("pb response="+i+", er="+sb.toString());
VBScript (excerpt):
Sub SaveWordAsPDF(p_strFilePath)
'Save Word file as a PDF
'Initialise
Dim objWord, objDocument
Set objWord = CreateObject("Word.Application")
Wscript.Echo p_strFilePath
'Open the file
Set objDocument = objWord.Documents.Open(p_strFilePath)
'Save the PDF
objDocument.SaveAs PathOfPDF(p_strFilePath), WORD_PDF
'Close the file and exit the application
objDocument.Close FALSE
objWord.Quit
End Sub
I've traced it as far to see that the vbscript is infact loading; the line 'Wscript.Echo p_strFilePath' does in fact actually print the correct path to the word doc. According to the error stream returned from java the offending line from vbscript is: 'Set objDocument = objWord.Documents.Open(p_strFilePath)' for which its saying:
Microsoft VBScript runtime error: Object required: 'objWord.Documents.Open(...)'
not sure if this is only triggered from incomplete path; looked at lots of posts - haven't been able to find any that exactly match my situation. Any advice is greatly appreciated!

Call java command line program from .net and return output of STDOUT to variable

I have a java command line program I need execute from within a .net program(VB)
The java command line program spits out a number when it's done executing that I need to know.
Can I take the STDOUT from the java program and read it into the .net program?
EDIT: I've got the program executing, but how do I read the inputstream?
Here is the code for executing the java program
Dim processinfo As New ProcessStartInfo()
processinfo.WorkingDirectory = "C:\path\to\working\directory"
processinfo.FileName = "java.exe"
processinfo.Arguments = "-jar myprogram.jar argumentA argumentB"
Process.Start(processinfo)
This nets me a command box and I get to watch the program execute..
EDIT: full code sample that works
Dim processinfo As New ProcessStartInfo()
processinfo.WorkingDirectory = "C:\"
processinfo.FileName = "java.exe"
processinfo.Arguments = "-jar my.jar list of params"
Dim myProcess As New Process()
processinfo.UseShellExecute = False
processinfo.RedirectStandardOutput = True
myProcess.StartInfo = processinfo
myProcess.Start()
Dim myStreamReader As StreamReader = myProcess.StandardOutput
' Read the standard output of the spawned process.
Dim myString As String
Do
myString = myStreamReader.ReadLine()
TextBox1.Text += myString
Loop Until (myStreamReader.EndOfStream)
myProcess.WaitForExit()
myProcess.Close()
Yes, use Process.StandardOutput (e.g., StreamReader.ReadToEnd among other possibilities). Be sure to set ProcessStartInfo.UseShellExecute to false, and ProcessStartInfo.RedirectStandardOutput to true.

Categories