fr.quatrevieux.singleinstance.InstanceManager   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 95
Duplicated Lines 0 %

Test Coverage

Coverage 81.82%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 10
eloc 26
c 1
b 0
f 0
dl 0
loc 95
ccs 18
cts 22
cp 0.8182
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getCurrentPid() 0 2 1
A server() 0 13 2
A InstanceManager() 0 2 1
A release() 0 2 1
A InstanceManager(LockFile) 0 2 1
A find() 0 2 2
A acquire() 0 7 2
1
/*
2
 * This file is part of SingleInstance.
3
 *
4
 * SingleInstance is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * SingleInstance is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with SingleInstance.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2020 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.singleinstance;
21
22
import fr.quatrevieux.singleinstance.ipc.InstanceServer;
23
24
import java.io.IOException;
25
import java.lang.management.ManagementFactory;
26
import java.util.Optional;
27
28
/**
29
 * Handle single instance system
30
 *
31
 * Usage :
32
 * <pre>{@code
33
 *     public static void main(String[] args) {
34
 *         // Check single instance
35
 *         InstanceManager manager = new InstanceManager();
36
 *
37
 *         manager.find().ifPresent(distant -> {
38
 *             // A running instance is present : send a message and close the application
39
 *             distant.send("My message", args[0].getBytes());
40
 *             System.exit(0);
41
 *         });
42
 *
43
 *         // Initialize application
44
 *         MyApp app = xxx;
45
 *
46
 *         // Start the IPC server
47
 *         manager.server().ifPresent(server -> {
48
 *             server.consume(message -> app.handleMessage(message));
49
 *         });
50
 *     }
51
 * }</pre>
52
 */
53
final public class InstanceManager {
54
    final private LockFile lockFile;
55
56
    public InstanceManager() {
57
        this(new LockFile());
58
    }
59
60 1
    public InstanceManager(LockFile lockFile) {
61 1
        this.lockFile = lockFile;
62 1
    }
63
64
    /**
65
     * Try to acquire the single instance (i.e. the lock file)
66
     * If the lock is acquired, the PID will be written on
67
     *
68
     * @return true if the current process as acquire the lock
69
     *
70
     * @throws IOException When cannot access to lock file
71
     */
72
    public boolean acquire() throws IOException {
73 1
        if (lockFile.acquire()) {
74 1
            lockFile.write(output -> output.writeInt(getCurrentPid()));
75 1
            return true;
76
        }
77
78
        return false;
79
    }
80
81
    /**
82
     * Try to get the distant instance (the first run process)
83
     *
84
     * Usage:
85
     * <pre>{@code
86
     *     InstanceManager im = new InstanceManager();
87
     *     im.find().ifPresent(distant -> {
88
     *         System.out.println("Process already running");
89
     *         // Send a message to the first process
90
     *         distant.send("Open", args[0].getBytes());
91
     *         System.exit(0);
92
     *     });
93
     * }</pre>
94
     *
95
     * @return An empty optional if the current process is the first running, or the distant instance if exists
96
     *
97
     * @throws IOException When cannot access to lock file
98
     */
99
    public Optional<DistantInstance> find() throws IOException {
100 1
        return acquire() ? Optional.empty() : Optional.of(new DistantInstance(lockFile));
101
    }
102
103
    /**
104
     * Try to open the IPC server
105
     * If the server is successfully opened, the port number will be written on the lock file
106
     *
107
     * Usage:
108
     * <pre>{@code
109
     *     InstanceManager im = new InstanceManager();
110
     *     im.server().ifPresent(server -> {
111
     *         server.consume(message -> {
112
     *             // Process received messages
113
     *             if (message.name().equals(xxx)) {
114
     *                 // ...
115
     *             }
116
     *         });
117
     *     });
118
     * }</pre>
119
     *
120
     * @return An empty optional if the current process is not the first running instance
121
     *
122
     * @throws IOException When cannot open the server
123
     */
124
    public Optional<InstanceServer> server() throws IOException {
125 1
        if (!acquire()) {
126
            return Optional.empty();
127
        }
128
129 1
        InstanceServer server = new InstanceServer();
130 1
        server.open();
131 1
        lockFile.write(output -> {
132 1
            output.writeInt(getCurrentPid());
133 1
            output.writeInt(server.port());
134 1
        });
135
136 1
        return Optional.of(server);
137
    }
138
139
    /**
140
     * Release the lock file
141
     */
142
    public void release() {
143 1
        lockFile.release();
144 1
    }
145
146
    private int getCurrentPid() {
147 1
        return Integer.parseInt(ManagementFactory.getRuntimeMXBean().getName().split("@", 2)[0]);
148
    }
149
}
150