summaryrefslogtreecommitdiffstats
path: root/jdisc_core/src/main/java/com/yahoo/jdisc/core/BootstrapDaemon.java
blob: 33d03d29f7d850da1e30566f04a8f8b88d6f8cc5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.jdisc.core;

import com.yahoo.protect.Process;
import org.apache.commons.daemon.Daemon;
import org.apache.commons.daemon.DaemonContext;

import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
 */
public class BootstrapDaemon implements Daemon {

    private static final Logger log = Logger.getLogger(BootstrapDaemon.class.getName());
    private final BootstrapLoader loader;
    private final boolean privileged;
    private String bundleLocation;

    static {
        // force load slf4j to avoid other logging frameworks from initializing before it
        org.slf4j.LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
    }

    public BootstrapDaemon() {
        this(new ApplicationLoader(Main.newOsgiFramework(), Main.newConfigModule()),
             Boolean.valueOf(System.getProperty("jdisc.privileged")));
    }

    BootstrapDaemon(BootstrapLoader loader, boolean privileged) {
        this.loader = loader;
        this.privileged = privileged;
    }

    BootstrapLoader loader() {
        return loader;
    }

    private static class WatchDog implements Runnable {
        final String name;
        final CountDownLatch complete;
        final long timeout;
        final TimeUnit timeUnit;
        WatchDog(String name, CountDownLatch complete, long timeout, TimeUnit timeUnit) {
            this.name = name;
            this.complete = complete;
            this.timeout = timeout;
            this.timeUnit = timeUnit;
        }
        @Override
        public void run() {
            boolean dumpStack;
            try {
                dumpStack = !complete.await(timeout, timeUnit);
            } catch (InterruptedException e) {
                return;
            }
            if (dumpStack) {
                log.warning("The watchdog for BootstrapDaemon." + name + " detected that it had not completed in "
                        + timeUnit.toMillis(timeout) + "ms. Dumping stack.");
                Process.dumpThreads();
            }
        }
    }
    private interface MyRunnable {
        void run() throws Exception;
    }
    private void startWithWatchDog(String name, long timeout, TimeUnit timeUnit, MyRunnable task) throws Exception {
        CountDownLatch complete = new CountDownLatch(1);
        Thread thread = new Thread(new WatchDog(name, complete, timeout, timeUnit), name);
        thread.setDaemon(true);
        thread.start();
        try {
            task.run();
        } catch (Exception e) {
            log.log(Level.WARNING, "Exception caught during BootstrapDaemon." + name, e);
            throw e;
        } catch (Error e) {
            log.log(Level.WARNING, "Error caught during BootstrapDaemon." + name, e);
            throw e;
        } catch (Throwable thrown) {
            log.log(Level.WARNING, "Throwable caught during BootstrapDaemon." + name, thrown);
        } finally {
            complete.countDown();
            thread.join();
        }
    }

    @Override
    public void init(DaemonContext context) throws Exception {
        String[] args = context.getArguments();
        if (args == null || args.length != 1 || args[0] == null) {
            throw new IllegalArgumentException("Expected 1 argument, got " + Arrays.toString(args) + ".");
        }
        bundleLocation = args[0];
        if (privileged) {
            log.finer("Initializing application with privileges.");
            startWithWatchDog("init", 60, TimeUnit.SECONDS, () -> loader.init(bundleLocation, true));
        }
    }

    @Override
    public void start() throws Exception {
        try {
            if (!privileged) {
                log.finer("Initializing application without privileges.");
                startWithWatchDog("init", 60, TimeUnit.SECONDS, () -> loader.init(bundleLocation, false));
            }
            startWithWatchDog("start", 60, TimeUnit.SECONDS, () -> loader.start());
        } catch (Exception e) {
            try {
                log.log(Level.SEVERE, "Failed starting container", e);
            }
            finally {
                Runtime.getRuntime().halt(1);
            }
        }
    }

    @Override
    public void stop() throws Exception {
        startWithWatchDog("stop", 60, TimeUnit.SECONDS, () -> loader.stop());
    }

    @Override
    public void destroy() {
        try {
            startWithWatchDog("destroy", 60, TimeUnit.SECONDS, () -> loader.destroy());
        } catch (Exception e) {
        }
    }

}