无论什么操作系统,在Java中执行计划任务的最佳解决方案是什么? [英] What is the best solution to perform scheduled tasks in Java, whatever the OS?

查看:273
本文介绍了无论什么操作系统,在Java中执行计划任务的最佳解决方案是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在我的Java桌面应用程序上生成警报:

I'd like to generate alarms on my Java desktop application :


  • 设置了特定日期/时间的警报, 5分钟或5个月

  • 我需要能够在触发闹钟时创建SWT应用程序

  • 我需要此功能才能工作在任何操作系统。软件用户可能会有Windows(其中90%)和其余Mac OS(包括我)

  • 软件许可证必须允许我在商业程序中使用它,

  • 我不能要求用户安装Cygwin,所以实现需要是Windows和Unix的原生

  • alarms set with a specific date/time which can be in 5 minutes or 5 months
  • I need to be able to create a SWT application when the alarm is triggered
  • I need this to be able to work on any OS. The software users will likely have Windows (90% of them), and the rest Mac OS (including me)
  • the software license must allow me to use it in a commercial program, without requiring to open source it (hence, no GPL)
  • I cannot require the users to install Cygwin, so the implementation needs to be native to Windows and Unix

我使用Java,Eclipse,SWT开发,我的应用程序是使用Java Web Start从我的服务器部署的。我使用Mac OS X.6进行开发。

I am developing using Java, Eclipse, SWT and my application is deployed from my server using Java Web Start. I'm using Mac OS X.6 for developing.

我想我有几个选项:


  1. 在启动时运行我的应用程序,并自己处理一切;

  2. 使用系统服务。

  3. 使用Unix上的cron表和Windows上的计划任务






< h1>在启动时运行

我真的不喜欢这个解决方案,我希望有更优雅的东西。

参考:我希望在Mac OS / Windows上的System Startup上运行我的Java程序。我如何做到这一点?

如果我作为系统服务运行,我可以从中受益,因为操作系统将确保我的软件:

If I run it as a system service, I can benefit from this, because the OS will ensure that my software:


  • 始终运行

  • 没有/需要GUI

  • 失败后重新启动

研究了一些我可以使用的资源:

I've researched some resources that I can use:

  • run4j — CPL — runs on Windows only, seems like a valid candidate
  • jsvc — Apache 2.0 — Unix only, seems like a valid candidate
  • Java Service Wrapper — Various — I cannot afford paid licenses, and the free one is a GPL. Hence, I don't want to/can't use this

我在系统服务选项中的问题是:

My questions in the system service options are:

我的计划实施是否正确:

  1. Are there other options?
  2. Is my planned implementation correct:


  • 在应用程序启动时检查服务是否存在

  • 如果未安装:

    • 升级用户以安装服务(在Unix上为root,在Windows上为UAC)

    • 如果主机操作系统是Windows,请使用run4j注册服务

    • 如果主机操作系统是Unix,请使用jsvc注册服务

    • at the application startup, check for existence of the service
    • if it is not installed:
      • escalate the user to install the service (root on Unix, UAC on Windows)
      • if the host OS is Windows, use run4j to register the service
      • if the host OS is Unix, use jsvc to register the service


      $ b

      因此,在第一次运行时,应用程序将安装服务并启动它。当应用程序关闭时,服务仍在运行,并且不再需要该应用程序,除非它是未注册的。

      然而,我想我仍然错过启动时运行功能。

      Thus, at the first run, the application will install the service and start it. When the application closes the service is still running and won't need the application ever again, except if it is unregistered.
      However, I think I still miss the "run on startup" feature.

      我是对的吗?

      在Unix上,我可以轻松地使用cron表需要应用程序将用户升级到root。我不需要处理重新启动,系统日期更改等。似乎不错。

      On Unix, I can easily use the cron table without needing the application to escalate the user to root. I don't need to handle restarts, system date changes, etc. Seems nice.

      在Windows上,我可以使用任务计划程序,即使在命令行中使用 At SchTasks 。这看起来不错,但我需要这是从XP兼容到7,我不能轻易地测试这个。

      On Windows, I can use the Task Scheduler, even in command-line using At or SchTasks. This seems nice, but I need this to be compatible from XP up to 7, and I can't easily test this.

      那么你会做什么?我错过了什么吗?

      So what would you do? Did I miss something? Do you have any advice that could help me pick the best and most elegant solution?

      推荐答案

      这里是我最后实现的: / p>

      Here's what I ended up implementing:

      public class AlarmManager {
          public static final String ALARM_CLI_FORMAT = "startalarm:";
          public static SupportedOS currentOS = SupportedOS.UNSUPPORTED_OS;
      
          public enum SupportedOS {
              UNSUPPORTED_OS,
              MAC_OS,
              WINDOWS,
          }
      
          public AlarmManager() {
              final String osName = System.getProperty("os.name");
              if (osName == null) {
                  L.e("Unable to retrieve OS!");
              } else if ("Mac OS X".equals(osName)) {
                  currentOS = SupportedOS.MAC_OS;
              } else if (osName.contains("Windows")) {
                  currentOS = SupportedOS.WINDOWS;
              } else {
                  L.e("Unsupported OS: "+osName);
              }
          }
      
          /**
           * Windows only: name of the scheduled task
           */
          private String getAlarmName(final long alarmId) {
              return new StringBuilder("My_Alarm_").append(alarmId).toString();
          }
      
          /**
           * Gets the command line to trigger an alarm
           * @param alarmId
           * @return
           */
          private String getAlarmCommandLine(final long alarmId) {
              return new StringBuilder("javaws -open ").append(ALARM_CLI_FORMAT).append(alarmId).append(" ").append(G.JNLP_URL).toString();
          }
      
          /**
           * Adds an alarm to the system list of scheduled tasks
           * @param when
           */
          public void createAlarm(final Calendar when) {
              // Create alarm
              // ... stuff here
              final long alarmId = 42;
      
              // Schedule alarm
              String[] commandLine;
              Process child;
              final String alarmCL = getAlarmCommandLine(alarmId);
              try {
                  switch (currentOS) {
                  case MAC_OS:
                      final String cron = new SimpleDateFormat("mm HH d M '*' ").format(when.getTime()) + alarmCL;
      
                      commandLine = new String[] {
                              "/bin/sh", "-c",
                              "crontab -l | (cat; echo \"" + cron + "\") | crontab"
                      };
                      child = Runtime.getRuntime().exec(commandLine);
                      break;
      
                  case WINDOWS:
                      commandLine = new String[] {
                              "schtasks",
                              "/Create",
                              "/ST "+when.get(Calendar.HOUR_OF_DAY) + ":" + when.get(Calendar.MINUTE),
                              "/SC ONCE",
                              "/SD "+new SimpleDateFormat("dd/MM/yyyy").format(when.getTime()), // careful with locale here! dd/MM/yyyy or MM/dd/yyyy? I'm French! :)
                              "/TR \""+alarmCL+"\"",
                              "/TN \""+getAlarmName(alarmId)+"\"",
                              "/F",
                      };
                      L.d("create command: "+Util.join(commandLine, " "));
                      child = Runtime.getRuntime().exec(commandLine);
                      break;
                  }
              } catch (final IOException e) {
                  L.e("Unable to schedule alarm #"+alarmId, e);
                  return;
              }
      
              L.i("Created alarm #"+alarmId);
          }
      
          /**
           * Removes an alarm from the system list of scheduled tasks
           * @param alarmId
           */
          public void removeAlarm(final long alarmId) {
              L.i("Removing alarm #"+alarmId);
              String[] commandLine;
              Process child;
              try {
                  switch (currentOS) {
                  case MAC_OS:
                      commandLine = new String[] {
                              "/bin/sh", "-c",
                              "crontab -l | (grep -v \""+ALARM_CLI_FORMAT+"\") | crontab"
                      };
                      child = Runtime.getRuntime().exec(commandLine);
                      break;
      
                  case WINDOWS:
                      commandLine = new String[] {
                              "schtasks",
                              "/Delete",
                              "/TN \""+getAlarmName(alarmId)+"\"",
                              "/F",
                      };
                      child = Runtime.getRuntime().exec(commandLine);
                      break;
                  }
              } catch (final IOException e) {
                  L.e("Unable to remove alarm #"+alarmId, e);
              }
          }
      
          public void triggerAlarm(final long alarmId) {
              // Do stuff
              //...
              L.i("Hi! I'm alarm #"+alarmId);
      
              // Remove alarm
              removeAlarm(alarmId);
          }
      }
      

      用法很简单。使用以下命令安排新的报警:

      Usage is simple. Schedule a new alarm using:

      final AlarmManager m = new AlarmManager();
      final Calendar cal = new GregorianCalendar();
      cal.add(Calendar.MINUTE, 1);
      m.createAlarm(cal);
      

      触发此类警报:

      public static void main(final String[] args) {
          if (args.length >= 2 && args[1] != null && args[1].contains(AlarmManager.ALARM_CLI_FORMAT)) {
              try {
                  final long alarmId = Long.parseLong(args[1].replace(AlarmManager.ALARM_CLI_FORMAT, ""));
                  final AlarmManager m = new AlarmManager();
                  m.triggerAlarm(alarmId);
              } catch (final NumberFormatException e) {
                  L.e("Unable to parse alarm !", e);
              }
          }
      }
      

      在Mac OS X上测试。 6和Windows Vista。类 L System.out.println G 保存我的全局常量(这里,我的服务器上的JNLP文件用于启动我的应用程序)。

      Tested on Mac OS X.6 and Windows Vista. The class L is an helper to System.out.println and G holds my global constants (here, my JNLP file on my server used to launch my application).

      这篇关于无论什么操作系统,在Java中执行计划任务的最佳解决方案是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆