将Java程序制作成windows系统服务相关


  用EXE4J将Java程序制作成windows系统服务。具体方法略。

  通常情况下,这样做是没有问题的。

  但是如果java程序中有调用DLL文件的内容时,EXE4J生成的系统服务就无法正常运行。跟踪的结果是程序卡在调用DLL方法的代码上(用JNative调用DLL)。

  同样的情况也出现在使用jeasyopc时,怀疑也是同样的原因,因为使用jeasyopc.jar时,会在当前文件夹下生成一个JCustomOpc.dll的文件。

  出现问题的当时,没有找到解决方法,只能放弃将Java程序做成系统服务,而是将该程序做成tomcat自启动项目,用tomcat来代替想要做成的系统服务。

  这样处置会有很多隐患。

  现在,又找到了另一个替代方案。

  用处理OPC Client的Java程序来举例:

    首先假设OPC Client的程序已经开发完成,简称为OPC程序。

    做一个定时任务,检查系统中OPC程序是否运行,如果没有运行,就启动OPC程序。

    将定时任务做成系统服务。这样可以避免在在服务中直接调用DLL。简称为定时任务服务。

  完成这个思路,有2个问题需要处理:

    首先,启动OPC程序:

    这个问题很简单,代码如下

    

            new Thread() {
                        public void run() {
                            try {
                                Runtime.getRuntime().exec(cmd);
                            } catch (final SecurityException ex) {
                                ex.printStackTrace();
                            } catch (final IOException ex) {
                                ex.printStackTrace();
                            }
                        }
                    }.start();

  这段代码中cmd变量就是OPC程序的启动命令,大概就是Java -cp “.\lib\log4j-1.2.15.jar;” opc.OPC,需要注意的就是路径问题。

    其次就是检查系统中OPC程序是否运行:

    这个问题有2种处理方式:

    一是OPC程序开一个任意监听端口,而定时任务服务将试图连接这个端口与OPC程序通讯,只要定时任务服务能顺利与OPC程序通讯,就说明OPC程序已经在运行了。具体实现略。

没有采用这样方法的原因是因为OPC程序和定时任务程序紧耦合了。

    二是使用JDK中提供tools.jar中的工具。

import sun.jvmstat.monitor.*;
import sun.management.ConnectorAddressLink;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;


public class LocalVirtualMachine {
    private String address;
    private String commandLine;
    private String displayName;
    private int vmid;
    private boolean isAttachSupported;
    private String jvmArgs;

    public LocalVirtualMachine(int vmid, String commandLine, String jvmArgs, boolean canAttach, String connectorAddress) {
        this.vmid = vmid;
        this.commandLine = commandLine;
        this.jvmArgs = jvmArgs;
        this.address = connectorAddress;
        this.isAttachSupported = canAttach;
        this.displayName = getDisplayName(commandLine);
    }

    public String getAddress() {
        return address;
    }

    public String getCommandLine() {
        return commandLine;
    }

    public String getDisplayName() {
        return displayName;
    }

    public boolean isAttachSupported() {
        return isAttachSupported;
    }

    public String getJvmArgs() {
        return jvmArgs;
    }

    public int getVmid() {
        return vmid;
    }

    private static String getDisplayName(String commandLine) {
        // trim the pathname of jar file if it's a jar
        String[] res = commandLine.split(" ", 2);
        if (res[0].endsWith(".jar")) {
            File jarfile = new File(res[0]);
            String displayName = jarfile.getName();
            if (res.length == 2) {
                displayName += " " + res[1];
            }
            return displayName;
        }
        return commandLine;
    }

    public static Map<Integer, LocalVirtualMachine> getMonitoredVMs() {
        Map<Integer, LocalVirtualMachine> map = new HashMap<Integer, LocalVirtualMachine>();
        MonitoredHost host;
        Set vms;
        try {
            host = MonitoredHost.getMonitoredHost(new HostIdentifier((String) null));
            vms = host.activeVms();
        } catch (java.net.URISyntaxException sx) {
            throw new InternalError(sx.getMessage());
        } catch (MonitorException mx) {
            throw new InternalError(mx.getMessage());
        }
//        LocalMonitoredVm
        for (Object vmid : vms) {

            if (vmid instanceof Integer) {
                int pid = (Integer) vmid;
                String name = vmid.toString(); // default to pid if name not available

                boolean attachable = false;
                String address = null;
                String jvmArgs = null;
                try {
                    MonitoredVm mvm = host.getMonitoredVm(new VmIdentifier(name));
                    // use the command line as the display name

                    name = MonitoredVmUtil.commandLine(mvm);
                    jvmArgs = MonitoredVmUtil.jvmArgs(mvm);

                    attachable = MonitoredVmUtil.isAttachable(mvm);

                    address = ConnectorAddressLink.importFrom(pid);

                    mvm.detach();
                } catch (Exception x) {
                    // ignore
                }

                map.put((Integer) vmid,
                        new LocalVirtualMachine(pid, name, jvmArgs, attachable, address));

            }
        }
        return map;
    }

   
}

  通过遍历getMonitoredVMs()方法的Map中的localVirtualMachine.getDisplayName(),就可以得到当前操作系统中由java启动的所有程序的名称。

  好吧,我承认这段代码是抄袭Jconsole的源代码。

  综合以上:

final String[] aArray = cmd.split(" ");

        if (aArray.length > 0) {
            Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(new Runnable() {
                public void run() {

                    boolean isRunning = false;
                    Map<Integer, LocalVirtualMachine> map = LocalVirtualMachine.getMonitoredVMs();
                    for (LocalVirtualMachine localVirtualMachine : map.values()) {
                        if (localVirtualMachine.getDisplayName().equals(aArray[aArray.length-1])) {
                            isRunning = true;
                            break;
                        }
                    }
                    map.clear();

                    if (!isRunning) {
                        new Thread() {
                            public void run() {
                                try {
                                    Runtime.getRuntime().exec(cmd);
                                } catch (final SecurityException ex) {
                                    ex.printStackTrace();
                                } catch (final IOException ex) {
                                    ex.printStackTrace();
                                }
                            }
                        }.start();


                    }
                }
            }, 0, 5, TimeUnit.SECONDS);
        }
    }

  

 

  

  这样做的好处是解除了OPC程序和定时任务服务直接的耦合。

不可否认的是,以上方法依然是治标不治本的方法。到现在为止,还是没有解决EXE4J生成的系统服务中无法调用DLL的问题。甚至问题的根源是不是因为调用了DLL,都不是很有把握。希望达人指点,谢谢!

  

posted @ 2012-08-15 17:53  崔宏  阅读(740)  评论(0编辑  收藏  举报