Wednesday, April 20, 2016

Java Uygulamalarının Windows Servisi Olarak Yönetilmesi

Servis rolünde çalışacak Java uygulamalarını Windows işletim sisteminde Windows servisi olarak tanımlamak isteriz. Windows servisine eklenen uygulamaları, Windows servisi olarak yönetmek, otomatik olarak başlatmak, durdurmak, devre dışı bırakmak, yeniden başlatmak ve eğer varsa bağımlı olduğu diğer servisleri tanımlamak mümkündür. Windows servisi olarak çalıştıracağımız Java uygulaması Java SE'de yazılmış bir web servisi olacak:
package com.example.ws;

import java.util.Collection;
import java.util.Random;
import java.util.stream.Collectors;

import javax.jws.WebService;

@WebService
public class LotteryWS {
 public Collection<Integer> draw() {
  return new Random().ints(1, 50).distinct().limit(6).sorted().boxed()
    .collect(Collectors.toList());
 }
}
Bu web servisini Windows servisi olarak başlatmak ve durdurmak istiyoruz. Bu amaçla Apache Commons'da ye alan Daemon kütüphanesinden yararlanacağız: https://commons.apache.org/proper/commons-daemon. İlk olarak servisi başlatma ve durdurma metodlarını içerecek bir sınıf kodlayacağız:
package com.example.daemon;

import java.util.Scanner;

import javax.xml.ws.Endpoint;

import org.apache.commons.daemon.Daemon;
import org.apache.commons.daemon.DaemonContext;
import org.apache.commons.daemon.DaemonInitException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.example.ws.LotteryWS;

public class SimpleService implements Daemon {
 static final Logger LOG = LoggerFactory.getLogger(SimpleService.class);
 private static LotteryWS ws = new LotteryWS();
 private static Endpoint ep= null;
 private static boolean isRunning = false;

 public static void main(String[] args) {

  Scanner sc = new Scanner(System.in);
  // wait until receive stop command from keyboard
  System.out.printf("Enter 'stop' to halt: ");
  while (!sc.nextLine().toLowerCase().equals("stop"))
   ;

  if (isRunning) {
   terminate();
  }
  sc.close();
 }

 public static void windowsService(String args[]) {
  String cmd = "start";
  if (args.length > 0) {
   cmd = args[0];
  }

  if ("start".equals(cmd)) {
   windowsStart(args);
  } else {
   windowsStop(args);
  }
 }

 public static void windowsStart(String[] args) {
  LOG.info("windowsStart called");
  initialize();
  while (isRunning) {
   synchronized (SimpleService.class) {
    try {
     SimpleService.class.wait(60000);
    } catch (InterruptedException ie) {
    }
   }
  }
 }

 public static void windowsStop(String[] args) {
  LOG.info("windowsStop is called");
  terminate();
  synchronized (SimpleService.class) {
   SimpleService.class.notify();
  }
 }

 public static void initialize() {
  if (ws != null) {
   LOG.info("Starting the WS");
   ep = Endpoint.publish("http://localhost:7001/lottery", ws);
   isRunning=true;   
  }
 }

 public static void terminate() {
  if (ws != null) {
   LOG.info("Stopping the WS");
   ep.stop();
   LOG.info("Web service is stopped.");
   isRunning= false;
  }
 }

 @Override
 public void destroy() {
  LOG.info("Destroying the service...");
 }

 @Override
 public void init(DaemonContext context) throws DaemonInitException,
   Exception {
  LOG.info("Initializing the service...");
 }

 @Override
 public void start() throws Exception {
  LOG.info("Starting the service...");
 }

 @Override
 public void stop() throws Exception {
  LOG.info("Stoping the service...");
 }

}
Her ne kadar bu yazıda Windows işletim sistemini ele almış olsak da bu çözüm Daemon arayüzünü gerçeklediği için Unix işletim sisteminde de kullanılabilir. SimpleService sınıfı ve Java uygulamamız aslında doğrudan Windows servisi ile karşılaşmıyor. SimpleService sınıfını prunsrv.exe için tanımlıyoruz. Windows servisi prunsrv'yi görür. Servis ile ilgili başlat, durdur gibi tüm komutlar prunsrv'ye ulaşır. prunsrv ise uygulamayı SimpleService sınıfı yardımı ile yönetir. SimpleService sınıfında servisi başlatmak yapılacak işlemleri windowsStart(String[] args) metodunda ve servisi durdurmak için gerekli işlemleri ise windowsStop(String[] args) metodunda kodluyoruz. Bu metod isimlerinin prunsrv için özel bir anlamı bulunmuyor. Bu nedenle prunsrv'ye tanıtmamız gerekecek. prunsrv'nin Java prosesini yaratırken hangi Java Sanal Makinasının (JSM) kullanacağını, Heap ve diğer JSM parametrelerinin neler olacağını, CLASSPATH tanımı, servis sınıfı ve servisi başlatma ve durdurma metodlarının adlarının ne olduğunu özel çevre değişkenlerinde tanımlamamız gerekiyor. Yukarıdaki uygulamamızı servis olarak ekleyecek komutları bir betik haline getirebiliriz:
set SERVICE_NAME=LotteryService
set PR_INSTALL=C:\opt\prunsrv.exe
 
REM Service log configuration
set PR_LOGPREFIX=%SERVICE_NAME%
set PR_LOGPATH=c:\tmp
set PR_STDOUTPUT=c:\tmp\stdout.txt
set PR_STDERROR=c:\tmp\stderr.txt
set PR_LOGLEVEL=Info
 
REM Path to java installation
set PR_JVM=c:\opt64\java\jdk1.8.0_60\jre\bin\server\jvm.dll
set PR_CLASSPATH=c:\opt\lottery-service.jar;c:\opt\commons-daemon-1.0.15.jar;c:\opt\slf4j-simple-1.7.6.jar;c:\opt\slf4j-api-1.7.6.jar;c:\opt\logback-core-1.1.7.jar;c:\opt\logback-classic-1.1.7.jar
 
REM Startup configuration
set PR_STARTUP=auto
set PR_STARTMODE=jvm
set PR_STARTCLASS=com.example.daemon.SimpleService
set PR_STARTMETHOD=windowsStart
REM Shutdown configuration
set PR_STOPMODE=jvm
set PR_STOPCLASS=com.example.daemon.SimpleService
set PR_STOPMETHOD=windowsStop
 
REM JVM configuration
set PR_JVMMS=256
set PR_JVMMX=1024
set PR_JVMSS=4000
set PR_JVMOPTIONS=-Duser.language=en;-Duser.region=us
REM Install service 
prunsrv.exe //IS//%SERVICE_NAME%
Servisi kaldırmak için ise aşağıdaki komutu çalıştırıyoruz:
prunsrv.exe //DS/LotteryService
Servisi başlatmak ve durdurmayı test etmek için aşağıdaki komuttan yararlanabilirsiniz:
c:\opt>prunsrv.exe //TS/LotteryService
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/opt/slf4j-simple-1.7.6.jar!/org/slf4j/impl
/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/opt/logback-classic-1.1.7.jar!/org/slf4j/i
mpl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.

SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory]
[main] INFO com.example.daemon.SimpleService - windowsStart called
[main] INFO com.example.daemon.SimpleService - Starting the WS
[Thread-3] INFO com.example.daemon.SimpleService - windowsStop is called
[Thread-3] INFO com.example.daemon.SimpleService - Stopping the WS
[Thread-3] INFO com.example.daemon.SimpleService - Web service is stopped.
Windows servislerini services.msc komutu ile çalıştırdığımız pencere uygulamasından izleyebilir ve yönetebiliriz:
Windows servise eklediğimiz servisleri komut satırından da başlatıp durdurabiliriz:
c:\opt>net start LotteryService
The LotteryService service is starting.
The LotteryService service was started successfully.

c:\opt>net stop LotteryService
The LotteryService service is stopping.
The LotteryService service was stopped successfully.
Servisin durumunu ise aşağıda verildiği şekilde izleyebilirsiniz:
c:\opt>net start LotteryService
The LotteryService service is starting.
The LotteryService service was started successfully.

c:\opt>tasklist | find /I "prun"
prunsrv.exe                   7628 Services                   0     46,256 K

c:\opt>net start | find /I "lottery"
   LotteryService

No comments:

Post a Comment