#1860 ✓resolved
R.Rettig

play start/stop NOT working reliable with windows

Reported by R.Rettig | August 12th, 2014 @ 10:27 PM | in 1.3.1 (closed)

Please include as much relevant information as possible including the exact framework version you're using and a code snippet that reproduces the problem. WARNING: Do not fill bugs related describing security vulnerabilities. Email directly guillaume dot bort at gmail dot com for that.

Framework version: 1.3.x
Platform you're using: windows

Reproduction steps:

  1. use sample app 'yabe' and run 'play start yabe' run 'play stop yabe'
C:\sample>"C:\sample\play\1.3.x\play.bat" stop yabe
~        _            _
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! 1.3.x, http://www.playframework.com
~
~ Cannot kill the process with pid 8788 (ERROR 6)
  1. use sample app 'yabe' and run 'play start yabe' kill the java process with task-manager ( server.pid should be left in application directory / or you can even create a server.pid ) run again 'play start yabe'
C:\sample>"C:\sample\play\1.3.x\play.bat" start yabe
~        _            _
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! 1.3.x, http://www.playframework.com
~
Traceback (most recent call last):
  File "C:\sample\play\1.3.x\play", line 156, in <module>
    status = cmdloader.commands[play_command].execute(command=play_command, app=play_app, args=remaining_args, env=play_env, cmdloader=cmdloader)
  File "C:\sample\play\1.3.x\framework\pym\play\commands\daemon.py", line 25, in execute
    start(app, args)
  File "C:\sample\play\1.3.x\framework\pym\play\commands\daemon.py", line 39, in start
    if process_running(pid):
  File "C:\sample\play\1.3.x\framework\pym\play\commands\daemon.py", line 174, in process_running
    return process_running_nt(pid)
  File "C:\sample\play\1.3.x\framework\pym\play\commands\daemon.py", line 207, in process_running_nt
    if process_list_nt().get(pid,"") != "":
  File "C:\sample\play\1.3.x\framework\pym\play\commands\daemon.py", line 185, in process_list_nt
    junk, instances = win32pdh.EnumObjectItems(None,None,'process', win32pdh.PERF_DETAIL_WIZARD)
pywintypes.error: (-1073738824, 'EnumObjectItems for buffer size', 'Das angegebene Objekt wurde nicht auf dem Computer gefunden.')

Details:

I fixed daemon.py as following:

import os, os.path
import subprocess
from play.utils import *
import time
if os.name == 'nt':
    import win32pdh, string, win32api, win32pdhutil

COMMANDS = ['start', 'stop', 'restart', 'pid', 'out']

HELP = {
    'start': 'Start the application in the background',
    'stop': 'Stop the running application',
    'restart': 'Restart the running application',
    'pid': 'Show the PID of the running application',
    'out': 'Follow logs/system.out file'
}

def execute(**kargs):
    command = kargs.get("command")
    app = kargs.get("app")
    args = kargs.get("args")
    play_env = kargs.get("env")

    if command == 'start':
        start(app, args)
    if command == 'stop':
        stop(app)
    if command == 'restart':
        restart(app, args)
    if command == 'pid':
        pid(app)
    if command == 'out':
        out(app)

def start(app, args):
    app.check()
    if os.path.exists(app.pid_path()):
        pid = open(app.pid_path()).readline().strip()
        if process_running(pid):
            print "~ Oops. %s is already started (pid:%s)! (or delete %s)" % (os.path.normpath(app.path), pid, os.path.normpath(app.pid_path()))
            print "~"
            sys.exit(1)
        else:
            print "~ removing pid file %s for not running pid %s" % (os.path.normpath(app.pid_path()), pid)
            os.remove(app.pid_path())

    sysout = app.readConf('application.log.system.out')
    sysout = sysout!='false' and sysout!='off'
    if not sysout:
        sout = None
    else:
        sout = open(os.path.join(app.log_path(), 'system.out'), 'w')
    try:
        pid = subprocess.Popen(app.java_cmd(args), stdout=sout, env=os.environ).pid
    except OSError:
        print "Could not execute the java executable, please make sure the JAVA_HOME environment variable is set properly (the java executable should reside at JAVA_HOME/bin/java). "
        sys.exit(-1)
    print "~ OK, %s is started" % os.path.normpath(app.path)
    if sysout:
      print "~ output is redirected to %s" % os.path.normpath(os.path.join(app.log_path(), 'system.out'))
    pid_file = open(app.pid_path(), 'w')
    pid_file.write(str(pid))
    print "~ pid is %s" % pid
    print "~"

def stop(app):
    app.check()
    if not os.path.exists(app.pid_path()):
        print "~ Oops! %s is not started (server.pid not found)" % os.path.normpath(app.path)
        print "~"
        sys.exit(-1)
    pid = open(app.pid_path()).readline().strip()
    kill(pid)
    os.remove(app.pid_path())
    print "~ OK, %s is stopped" % app.path
    print "~"


def restart(app, args):
    app.check()
    if not os.path.exists(app.pid_path()):
        print "~ Oops! %s is not started (server.pid not found)" % os.path.normpath(app.path)
        print "~"
    else:
        pid = open(app.pid_path()).readline().strip()
        os.remove(app.pid_path())
        kill(pid)

    sysout = app.readConf('application.log.system.out')
    sysout = sysout!='false' and sysout!='off'
    java_cmd = app.java_cmd(args)
    if not sysout:
      sout = None
    else:
      sout = open(os.path.join(app.log_path(), 'system.out'), 'w')
    try:
        pid = subprocess.Popen(java_cmd, stdout=sout, env=os.environ).pid
    except OSError:
        print "Could not execute the java executable, please make sure the JAVA_HOME environment variable is set properly (the java executable should reside at JAVA_HOME/bin/java). "
        sys.exit(-1)
    print "~ OK, %s is restarted" % os.path.normpath(app.path)
    if sysout:
      print "~ output is redirected to %s" % os.path.normpath(os.path.join(app.log_path(), 'system.out'))
    pid_file = open(app.pid_path(), 'w')
    pid_file.write(str(pid))
    print "~ New pid is %s" % pid
    print "~"
    sys.exit(0)


def pid(app):
    app.check()
    if not os.path.exists(app.pid_path()):
        print "~ Oops! %s is not started (server.pid not found)" % os.path.normpath(app.path)
        print "~"
        sys.exit(-1)
    pid = open(app.pid_path()).readline().strip()
    print "~ PID of the running applications is %s" % pid
    print "~ "

def out(app):
    app.check()
    if not os.path.exists(os.path.join(app.log_path(), 'system.out')):
        print "~ Oops! %s not found" % os.path.normpath(os.path.join(app.log_path(), 'system.out'))
        print "~"
        sys.exit(-1)
    sout = open(os.path.join(app.log_path(), 'system.out'), 'r')
    try:
        sout.seek(-5000, os.SEEK_END)
    except IOError:
        sout.seek(0)
    while True:
        where = sout.tell()
        line = sout.readline().strip()
        if not line:
            time.sleep(1)
            sout.seek(where)
        else:
            print line

def kill(pid):
    if os.name == 'nt':
        import ctypes, ctypes.wintypes
        Kernel32 = ctypes.WinDLL('kernel32.dll')
        OpenProcess = Kernel32.OpenProcess
        OpenProcess.restype = ctypes.wintypes.HANDLE
        TerminateProcess = Kernel32.TerminateProcess
        TerminateProcess.restype = ctypes.wintypes.BOOL
        CloseHandle = Kernel32.CloseHandle

        handle = OpenProcess(1, False, int(pid))
        if handle:
            if not TerminateProcess(handle, 0):
                CloseHandle(handle)
                print "~ Cannot kill the process with pid %s (ERROR %s)" % (pid, ctypes.windll.kernel32.GetLastError())
                print "~ "
                sys.exit(-1)
            else:
                print "~ Process with PID %s terminated" % pid
            CloseHandle(handle)
        else:
            print "~ Process with PID %s not found" % pid
            
    else:
        try:
            os.kill(int(pid), 15)
        except OSError:
            print "~ Play was not running (Process id %s not found)" % pid
            print "~"
            sys.exit(-1)

def process_running(pid):
    if os.name == 'nt':
                return process_running_nt(pid)   
    else:
        try:
            os.kill(int(pid), 0)
            return True
        except OSError:
            return False

# loosely based on http://code.activestate.com/recipes/303339/
def process_list_nt():
    #each instance is a process, you can have multiple processes w/same name
    object = win32pdhutil.find_pdh_counter_localized_name("Process")
    items, instances = win32pdh.EnumObjectItems(None,None,object, win32pdh.PERF_DETAIL_WIZARD)
    proc_ids={}
    proc_dict={}
    for instance in instances:
        if instance in proc_dict:
            proc_dict[instance] = proc_dict[instance] + 1
        else:
            proc_dict[instance]=0
    items = [win32pdhutil.find_pdh_counter_localized_name("ID Process")] + items[:5]
    for instance, max_instances in proc_dict.items():
        for inum in xrange(max_instances+1):
            hq = win32pdh.OpenQuery() # initializes the query handle 
            hcs = []
            for item in items:
                path = win32pdh.MakeCounterPath( (None,object,instance,
                                                  None, inum, item) )
                hcs.append(win32pdh.AddCounter(hq, path))
            win32pdh.CollectQueryData(hq)
            # as per http://support.microsoft.com/default.aspx?scid=kb;EN-US;q262938, some "%" based
            # counters need two collections
            time.sleep(0.01)
            win32pdh.CollectQueryData(hq)
            for hc in hcs:
                type, val = win32pdh.GetFormattedCounterValue(hc, win32pdh.PDH_FMT_LONG)
                proc_ids[str(val)]=instance;
                win32pdh.RemoveCounter(hc)
            win32pdh.CloseQuery(hq) 

    return proc_ids


def process_running_nt(pid):
    if process_list_nt().get(pid,"") != "":
        return True
    else:
        return False

Comments and changes to this ticket

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2>Play framework</h2>

Play makes it easier to build Web applications with Java. It is a clean alternative to bloated Enterprise Java stacks. It focuses on developer productivity and targets RESTful architectures. Learn more on the <a href="http://www.playframework.org">http://www.playframework.org</a> website.<br><br>

<h2>Source code is hosted on github</h2>Check out our repository at <a href="http://github.com/playframework/play">http://github.com/playframework/play</a><br><br>

<h2>Contributing, creating a patch</h2> Please read the <a href="http://play.lighthouseapp.com/projects/57987/contributor-guide">contributor guide</a><br><br>

<h2>Reporting Security Vulnerabilities</h2> Since all bug reports are public, please report any security vulnerability directly to <em>guillaume dot bort at gmail dot com</em>.<br><br>

<h2>Creating a bug report</h2> Bug reports are incredibly helpful, so take time to report bugs and request features in our ticket tracker. We’re always grateful for patches to Play’s code. Indeed, bug reports with attached patches will get fixed far quickly than those without any.<br><br>

Please include as much relevant information as possible including the exact framework version you're using and a code snippet that reproduces the problem.<br><br>

Don't have too much expectations. Unless the bug is really a serious "everything is broken" thing, you're creating a ticket to start a discussion. Having a patch (or a branch on Github we can pull from) is better, but then again we'll only pull high quality branches that make sense to be in the core of Play.

Tags

Referenced by

Pages