简体   繁体   中英

Java job not writing to file when running under cron

Apologies in advance, my Java is rubbish. I have cobbled together (from other people) a script which executes a script , spooling the output to a text file, which I then send to another system. Here is the crontab entry

jcb@LWS-DEV4B:~/bridge/heartbeat$ crontab -l
*/2 * * * * (cd /home/jcb/bridge/heartbeat/; ./hb_execute_bridge_util_mem_usage.sh)
*/5 * * * * (cd /home/jcb/bridge/heartbeat/; ./hb_bridge_status_cron.sh)

When I call the java program manually, it works.

jcb@LWS-DEV4B:~/bridge/heartbeat$ java -classpath /home/jcb/bridge/heartbeat/ojdbc6.jar:. hb_bridge_util_mem_usage
jcb@LWS-DEV4B:~/bridge/heartbeat$ ls -ltr hb_bridge_util_mem_usage.txt
-rwxr-xr-x 1 jcb jcb 14919 Oct 28 11:55 hb_bridge_util_mem_usage.txt

When I call the shell script which calls the java program, it works.

jcb@LWS-DEV4B:~/bridge/heartbeat$ ./hb_execute_bridge_util_mem_usage.sh
jcb@LWS-DEV4B:~/bridge/heartbeat$ ls -ltr hb_bridge_util_mem_usage.txt
-rwxr-xr-x 1 jcb jcb 14919 Oct 28 11:59 hb_bridge_util_mem_usage.txt

However the cronjob does not spool any output.

-rwxr-xr-x 1 jcb jcb       0 Oct 28 11:52 hb_bridge_util_mem_usage.txt

I have read a few things in forums about waiting for processes, which I've added to see whether it makes a difference. Here is my shell script:

#!/bin/bash
java -classpath /home/jcb/bridge/heartbeat/ojdbc6.jar:. hb_bridge_util_mem_usage

Here is my java program, again: the waiting, destroying, getting input are things that I added afterwards to see if it would make a difference:

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class hb_bridge_util_mem_usage {

  public static Connection getConnection() throws Exception {
    String driver = "oracle.jdbc.driver.OracleDriver";
    String url = "jdbc:oracle:thin:@???";
    String username = "???";
    String password = "???";
    Class.forName(driver);
    Connection conn = DriverManager.getConnection(url, username, password);
    return conn;
  }

  public static void main(String[] args)throws Exception {
    String id = "001";
    String fileName = "hb_bridge_util_mem_usage.txt";

    ProcessBuilder processBuilder = new ProcessBuilder();

    processBuilder.command("bash", "-c", "util_mem_usage | tee /home/jcb/bridge/heartbeat/hb_bridge_util_mem_usage.txt");

    Process process = processBuilder.start();
    InputStream in = process.getInputStream();
    InputStream error = process.getErrorStream();
    for (int i = 0; i < error.available(); i++) {
           System.out.println("" + error.read());
         }

    // wait for 10 seconds and then destroy the process
    Thread.sleep(10000);
    process.destroy();

    FileInputStream fis = null;
    PreparedStatement pstmt = null;
    Connection conn = null;
    try {
      conn = getConnection();
      conn.setAutoCommit(false);
      File file = new File(fileName);
      fis = new FileInputStream(file);
      pstmt = conn.prepareStatement("insert into hb_bridge_util_mem_usage(id, fileName, fileBody) values (?, ?, ?)");
      pstmt.setString(1, id);
      pstmt.setString(2, fileName);
      pstmt.setAsciiStream(3, fis, (int) file.length());
      pstmt.executeUpdate();
      conn.commit();
    } catch (Exception e) {
      System.err.println("Error: " + e.getMessage());
      e.printStackTrace();
    } finally {
      pstmt.close();
      fis.close();
      conn.close();
    }
  }

The problem is not related to your java code; java doesn't 'run differently' just because cron invoked it.

There are 2 likely explanations:

User of the process

Processes are owned by a user. Their rights, eg when accessing dirs and files, are controlled by what that user is allowed to do. For example, if you have your /home/barry folder set up as "readable/writable/seeable by the user, but only readable/seeable by anybody else", ie if you run ls -la in the /home dir and you see:

rwxr-xr-x barry

Then if you are logged in as barry and you start a java app that tries to execute:

Files.write("Hello!", Paths.get("/home/barry/test.txt"));

it'll work fine. But if you then run this as user jane instead, it will fail: the process has the same rights as user jane , and user jane doesn't have the right to write in that folder.

cron runs your application as some user. Depends on how you've configured cron. If said user doesn't have the rights to write to the file, you have your explanation. The easy fix is to either make where-ever your app is trying to write a writable place for whatever user cron is running it as, or , you configure cron to run this one under a different user, or , you run the command through su which lets you run it as different user. However, using su to run something as a different user only works if you invoke su itself as root .

SECURITY NOTE : Under many configs, there is a per-user cron table (and then those tasks are run as that user), as well as a global crontable and everything in that is run as root . Running apps as root is extremely dangerous; any problem can wipe out or take over the entire system. You should NOT run things as root. As a consequence, if this is in the global cron table and IS running as root, you should most absolutely do that su thing. We can more or less trust su not to mess up, and because it will then invoke your java app with the rights of eg barry , at least this way you no longer have to trust java . You shouldn'tr trust java for a simple reason: java is a complex executable that runs arbitrary code. It doesn't matter if NASA wrote it: You can't trust that kind of complexity nearly as much as the extremely simple su command.

Current working dir

I think the cd stuff means this can't be it, but I'm not 100% certain that the working dir for the java process ends up being the dir you cd to. You may want to check this. For example, by writing the result of System.getProperty("user.dir") (which is a rather weirdly named property that gives you the current working dir), or print new File(".").toAbsolutePath() to an absolute location, eg /home/foo/test.txt where /home/foo has rwxrwxrwx rights (set that up by running sudo chmod 777 /home/foo ), after making that foo dir for the sole purposes of this test.

Then let cron do its thing and you can use that file to track what your java process emitted as you debug the problem.

The problem was that Java could not execute the C program "util_mem_usage", despite me having "su" commands dotted around. My solution was to seperate this command into a shell scipt. The paths were what are required for the C program to work (and probably what Java was silently struggling with too).

#!/bin/bash
PATH=/home/jcb/bin:/usr/bin/
**LD_LIBRARY_PATH=/home/jcb/lib
PROJECT_ROOT=/home/jcb
export LD_LIBRARY_PATH
export PROJECT_ROOT**
util_mem_usage | tee /home/jcb/bridge/heartbeat/hb_bridge_util_mem_usage.txt

My java program then call this instead of the C utility

processBuilder.command("bash", "-c", "/home/jcb/bridge/heartbeat/hb_get_bridge_util_mem_usage.sh");

It does not seem like an elegant solution, but it works...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM