I want to open a new document from a MS-Word template from my Java-App, but only manage to edit the template itself.
Here is my situation:
Inside my Jar file is a word template, that gets copied to a user-specified location, so he/she can edit it. Afterwards, the application can open this edited template, insert data into it and open it in word. This all works fine (using Apache-POI), but the last step is not entirely what I want.
Normally, when double-clicking a word-template, Word would open a NEW document (titled Document1) that is not saved anywhere yet. In my case, Word opens the word-template for editing (titled blablaMyTemplate), meaning the already saved template from which documents should be created. How do I manage to open a newly created document from the template using Java?
This is my code (try/catch and stream closing omitted):
File bbb = new File(new File(getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile().getParentFile().getAbsolutePath() + "/blablaMyTemplate.dotx");
if (!bbb.exists()) { //copy file to outside of jar for user editing
Files.copy(Buchungsbegleitblatt.class.getResourceAsStream("bbb.dotx"), bbb.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
File tmp = File.createTempFile("bbb", ".dotx"); //create tmp file to insert data
InputStream in = new FileInputStream(bbb);
OutputStream out = new FileOutputStream(tmp);
XWPFDocument document = new XWPFDocument(in);
//here, some data is filled into the document using Apache-POI (omitted, because it works fine)
document.write(out);
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().open(tmp); //this opens the template for editing, it does not create a new doc from template
}
The issue lies within the last line, but I have no idea what else I could call here.
To make it a little clearer, here is an image of the context menu I get on the template file and what is supposed to happen:
You have exactly described the problem already. Desktop.open will exactly do what it says. It will perform the open event for the called application which is assigned to the file type.
What you need is to perform the new event. This can be achieved in Word using startup command-line switches to start Word.
In the linked knowledge base entry you can find:
...
/ttemplate_name Starts Word with a new document based on a template
other than the Normal template.
...
To do so with Java either Runtime.getRuntime().exec or ProcessBuilder can be used. With both I would recommend first to start the command interpreter CMD as a shell and use the start command from this to start the application. So we avoid the need to know the exact path to the application.
Examples:
import java.io.*;
class RuntimeExec {
public static void main(String[] args) {
try {
// Execute a command as a single line
File f = new File("C:/Users/axel/Documents/The Template.dotx");
System.out.println(f.getAbsolutePath());
String cmd = "cmd /C start winword.exe /t\"" + f.getAbsolutePath() + "\"";
Process child = Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
}
}
}
class UseProcessBuilder {
public static void main(String[] args) {
try {
//use ProcessBuilder to have more control
File f = new File("C:/Users/axel/Documents/The Template.dotx");
System.out.println(f.getAbsolutePath());
String application = "winword.exe";
String switchNewFromTemplate = "/t";
String file = f.getAbsolutePath();
ProcessBuilder pb = new ProcessBuilder("cmd", "/C", "start", application, switchNewFromTemplate+file);
Process process = pb.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
There is one possibility to not explicitly start the winword application. The start command has the feature to perform the default action according to the file extension with the given file if we give a empty string "" as the application name:
start "" "The name of the file.ext"
Example:
start "" "The name of the file.dotx"
This will perform the default action new in winword application which is related to the dotx extension in registry database.
So:
class RuntimeExec {
public static void main(String[] args) {
try {
// Execute a command as a single line
File f = new File("C:/Users/Axel Richter/Documents/The Template.dotx");
System.out.println(f.getAbsolutePath());
String cmd = "cmd /C start \"\" \"" + f.getAbsolutePath() + "\"";
Process child = Runtime.getRuntime().exec(cmd);
InputStream in = child.getErrorStream();
int c;
while ((c = in.read()) != -1) {
System.out.print((char)c);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class UseProcessBuilder {
public static void main(String[] args) {
try {
//use ProcessBuilder to have more control
File f = new File("C:/Users/Axel Richter/Documents/The Template.dotx");
System.out.println(f.getAbsolutePath());
String file = f.getAbsolutePath();
ProcessBuilder pb = new ProcessBuilder("cmd", "/C", "start", "\"\"", file);
Process process = pb.start();
InputStream in = process.getErrorStream();
int c;
while ((c = in.read()) != -1) {
System.out.print((char)c);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
One way to do this is to start a process on the template so that Windows will handle the opening, and use the default intent. It's been a while since I've touched Java, but if it's like C# it will be something like new Process(tmp).Start().
Though, I'm not really sure if this is what you're looking for, exactly.
Related
I have a problem with executing command in Runtime.getRuntime().exec(command) on my linux DEV machine.
I'm trying to convert html content to mobi format using calibre but it doesn't work.
Even if I added log.warn to display what this process returns it shows nothing.
It works on my local machine where I do have Windows 10 and I read on the Internet that I should point out that command should be launched on linux so I added String[] cmd = {"bash", "-c", command}; but it still doesn't work.
My command looks like this:
/usr/src/calibre/ebook-convert /tmp/filesDirectory_mobi3970575619159760977/d7ed6792-b3cb-4761-bb6a-b9facf9e7a6c9250643820137116550.html /tmp/filesDirectory_mobi3970575619159760977/d7ed6792-b3cb-4761-bb6a-b9facf9e7a6c11909891397415910433.mobi
And here's my code:
#Override
public Document convert(Document document, DocumentFormat documentFormat) {
Document htmlDocument = htmlDocumentConverter.convert(document, documentFormat);
try {
log.info("Converting document from {} to {}", getSourceFormat().toString(), getTargetFormat().toString());
CalibreConfigData calibreData = calibreConfig.getConfigurationData(CalibreConversion.HTML_TO_MOBI);
Files.write(calibreData.getSourceFilePath(), htmlDocument.getContent());
String command = calibreData.getCalibreCommand();
String[] cmd = {"bash", "-c", command};
var r = Runtime.getRuntime().exec(cmd);
r.waitFor();
log.warn("Process: " + new String(r.getInputStream().readAllBytes(), StandardCharsets.UTF_8));
byte[] convertedFileAsBytes = Files.readAllBytes(calibreData.getConvertedFilePath());
// Files.deleteIfExists(calibreData.getSourceFilePath());
// Files.deleteIfExists(calibreData.getConvertedFilePath());
// Files.deleteIfExists(calibreData.getFilesDirectoryPath());
return new Document(convertedFileAsBytes);
} catch (InterruptedException | IOException e) {
log.error("Conversion failed due to problem: " + e);
throw new ConversionException("Conversion failed due to problem: " + e);
}
}
I checked created tempFiles and found out that file .html has content inside but file .mobi is empty even after executing above's command.
A main method of a Calibre conversion invoker could be as simple as the below:
public static void main(String[] args) {
try {
String[] command = { "/usr/src/calibre/ebook-convert", args[0], args[1] };
ProcessBuilder pb = new ProcessBuilder(command);
pb.inheritIO();
pb.start();
} catch (Throwable t) {
t.printStackTrace();
}
}
I have created a java class to execute a batch file that is in my desktop so that the commands in the batch file will be executed too. The problem is that, i keep getting the error:
The filename, directory name, or volume label syntax is incorrect.
The filename, directory name, or volume label syntax is incorrect.
I have checked the .bat name and the directory. It is correct. When i type cmd /c start C:/Users/attsuap1/Desktop, windows explorer opens the desktop tab. However when i type cmd /c start C:/Users/attsuap1/Desktop/DraftBatchFile.bat, it gives the error. My DraftBatchFile.bat is in my desktop.
Here are my java codes:
public class OpenDraftBatchFile{
public OpenDraftBatchFile() {
super();
}
/**Main Method
* #param args
*/
public static void main(String[] args) {
//Get Runtime object
Runtime runtime = Runtime.getRuntime();
try {
//Pass string in this format to open Batch file
runtime.exec("cmd /c start C:/Users/attsuap1/Desktop/DraftBatchFile.bat");
} catch (IOException e) {
System.out.println(e);
}
}
Why is it that the batch file cannot be executed even if the directory is correct? Someone please help me. Thank you so much.
These are the codes in DraftBatchFile.bat
#echo off
echo.>"Desktop:\testing\draft.txt"
#echo Writing text to draft.txt> Desktop:\testing\draft.txt
When i execute the DraftBatchFile.bat by running the java class, i want a draft.txt file to be created in a testing folder that i have created (in desktop).
there is no such thing as desktop:\
Instead try something like this.
#echo off
echo . %userprofile%\Desktop\testing\dblank.txt
#echo Writing text to draft.txt > %userprofile%\Desktop\testing\dblank.txt
You just need to change a directory and create new subdirectory if it not exist. My offer is change .bat file like this:
#echo off
if not exist "%userprofile%\Desktop\testing" mkdir "%userprofile%\Desktop\testing"
echo.>"%userprofile%\Desktop\draft.txt"
#echo Writing text to draft.txt>"%userprofile%\Desktop\draft.txt"
Also you can create .txt file and write in it text through Java code:
File directory = new File(System.getProperty("user.home")+"//Desktop//testing");
if (!directory.exists())
directory.mkdirs();
String content = "Writing text to draft.txt";
File file = new File(System.getProperty("user.home")+"//Desktop//testing//draft.txt");
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(file));
writer.write(content);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (writer != null)
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
I am trying to make it so that when a user clicks a button, a new screen appears and automatically runs a command line process, and they are able to see the outputs of this process.
I thought that I might be able to use a JTextArea to set text to.
Here's what I've got at the moment:
runButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent r)
{
JFrame runFrame = new JFrame("Running process...");
runFrame.setVisible(true);
runFrame.setSize(500, 400);
runFrame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
runFrame.setLayout(null);
JTextArea run = new JTextArea();
run.setBounds(100,50,300,200);
runFrame.add(run);
Runtime runtime = Runtime.getRuntime();
Process process = null;
try
{
process = runtime.exec("cat /cmd/h:/testfile");
}
catch (IOException e)
{
e.printStackTrace();
}
InputStream runStream = process.getInputStream();
InputStreamReader runStreamReader = new InputStreamReader(runStream);
BufferedReader br = new BufferedReader(runStreamReader);
String line;
StringBuilder sb = new StringBuilder();
try
{
while ((line = br.readLine()) != null)
{
sb.append(line);
}
}
catch (IOException e)
{
e.printStackTrace();
}
run.setText(sb.toString());
}
});
The error I'm getting with this is:
Cannot run program "cat": CreateProcess error=2, The system cannot find the file specified
I was trying to test opening a file to test this, which just contains lines of random letters.
EDIT:
I'm not sure I explained clearly what I need this to do.
What I want is for a command to be run in command line that opens a file. I then want the result of the command line to be output into the JTextArea.
EDIT 2:
I have tried to change my command to "ping riot.de -t". This will ping riots server every so often and it returns a response with the response time.
Upon running this, the new frame is just black, and java freezes up.
maybe the env of your java application execution has not the right path of cat cmd? you have try to specify absolute path for cat command?
I am using a really old Java version and i am missing a lot of classes. The system runs on an embedded platform. There is a class to execute system commands, but the output from the command is discarded. Is there anyway to cache or get this output another way?
There is another java application that is not coded by us that we interact with. This application starts a test and output the results in the shell. We are not able to edit the source code of that application.
Any suggestions?
You can check ProcessBuilder to run system commands.
ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2");
Map env = pb.environment();
env.put("VAR1", "myValue");
env.remove("OTHERVAR");
env.put("VAR2", env.get("VAR1") + "suffix");
pb.directory(new File("myDir"));
File log = new File("log");
pb.redirectErrorStream(true);
pb.redirectOutput(Redirect.appendTo(log));
Process p = pb.start();
If the external app will run inside the same process, you can try to redirect System.out (using System.setOut()) before the call to the external application.
Depending on how desperate you are, you can manipulate the standard output of the launched process by adding a "shim" class. Basically, create your own class with a main method which redirects stdout to your desired location (like some known file).
public class HackMain {
public static void main(String[] args) {
// ... use reflection to hack System.out ...
// invoke "real" main
RealMainClass.main(args);
}
}
Then add a jar with this class to the invoked process command line and call it instead like java -cp <other_jars>:<hack_jar> HackMain [<args> ...].
If you're using a shell wrapper and you have access to the file system you can try to redirect the output of your command to a file and read it from there:
Ish.execute(fancyCommand + " > myfile.tmp");
InputStream is = null;
StringBuilder sb = new StringBuilder();
try {
is = new BufferedInputStream(new FileInputStream("/path/to/myfile.tmp"));
int c;
while ((c = is.read()) != -1){
sb.append((char)c);
}
} catch (IOException ioex) {
ioex.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (Exception ignore) {
}
}
}
I am trying to execute this command sort --field-separator="," --key=2 /home/dummy/Desktop/sample.csv" -o /home/dummy/Desktop/sample_temp.csv using Java Runtime and ProcessBuilder.
Manually I am able to execute this command in linux, but using Runtime or ProcessBuilder, this command does not execute. It returns with an error code = 2.
Edit:
If I am trying to execute 'ls' command in linux through Java, I get the list of files in the current directory. But, If I try to execute the command 'ls | grep a', an IOException is thrown with error code=2.
Here is the snippet:
public static void main(String[] args) throws IOException {
InputStream is = null;
ByteArrayOutputStream baos = null;
ProcessBuilder pb = new ProcessBuilder("ls | grep a");
try {
Process prs = pb.start();
is = prs.getInputStream();
byte[] b = new byte[1024];
int size = 0;
baos = new ByteArrayOutputStream();
while((size = is.read(b)) != -1){
baos.write(b, 0, size);
}
System.out.println(new String(baos.toByteArray()));
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
try {
if(is != null) is.close();
if(baos != null) baos.close();
} catch (Exception ex){}
}
}
There could be a range of issue with your code. Hence you did not supply your code I can only guess.
The output file needs to be already created
The ',' field separator does not need the quotes around it (see code below)
So after these 2 issues (both making the program exit with '2'), this code actually works:
import java.io.IOException;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder(Arrays.asList("sort", "--field-separator=,", "--key=2", "/tmp/sample.csv", "-o",
"/tmp/sample_temp.csv"));
Process p = pb.start();
int returnCode = p.waitFor();
System.out.println(returnCode);
}
}
Will print '0' and will sort the file correctly.
For the 'ls | grep' issue, read this great article: http://www.javaworld.com/article/2071275/core-javahen-runtime-exec---won-t/core-java/when-runtime-exec---won-t.html
The article basically explains that the Runtime.exec (and the ProcessBuilder wrapper) is for running processes and not a Shell (the ls | grep you are trying are actually 2 processes in Linux communicating with each other thru stdout/in).
I am able to execute that manually. And error code 2 means Misuse of Shell BuiltIns
I see in your example you are only invoking "ls", not "/usr/bin/ls" (or something like that).
When you execute manually you have the luxury of PATH environment variable which is not availabled to the process you create.
Use "which ls" to discover the location of 'ls' on your target system. For your code to be portable you will have to make it a configurable option.
this is the way to execute any bash commands like sort, ls, cat (with sub-options). Please find the snippet:
private String executeCommand(String command) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec("script.sh");
p.waitFor();
BufferedReader reader =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
In the exec() method, I passed a shell script which contains the bash command within it. That linux command will be executed and you can carry on with the next task. Hope this was helpful.