aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/CleanupService.java
blob: 29ed7564d382dd73e3b33d75e7bc92eb6cf2cbdd (plain)
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
/*
 * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0 which is available at
 * https://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
package org.eclipse.jgit.internal.util;

import org.eclipse.jgit.internal.JGitText;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A class that is registered as an OSGi service via the manifest. If JGit runs
 * in OSGi, OSGi will instantiate a singleton as soon as the bundle is activated
 * since this class is an immediate OSGi component with no dependencies. OSGi
 * will then call its {@link #start()} method. If JGit is not running in OSGi,
 * {@link #getInstance()} will lazily create an instance.
 * <p>
 * An OSGi-created {@link CleanupService} will run the registered cleanup when
 * the {@code org.eclipse.jgit} bundle is deactivated. A lazily created instance
 * will register the cleanup as a JVM shutdown hook.
 * </p>
 */
public final class CleanupService {

	private static final Logger LOG = LoggerFactory
			.getLogger(CleanupService.class);

	private static final Object LOCK = new Object();

	private static CleanupService INSTANCE;

	private final boolean isOsgi;

	private JGitText jgitText;

	private Runnable cleanup;

	/**
	 * Public component constructor for OSGi DS. Do <em>not</em> call this
	 * explicitly! (Unfortunately this constructor must be public because of
	 * OSGi requirements.)
	 */
	public CleanupService() {
		this.isOsgi = true;
		setInstance(this);
	}

	private CleanupService(boolean isOsgi) {
		this.isOsgi = isOsgi;
	}

	private static void setInstance(CleanupService service) {
		synchronized (LOCK) {
			INSTANCE = service;
		}
	}

	/**
	 * Obtains the singleton instance of the {@link CleanupService} that knows
	 * whether or not it is running on OSGi.
	 *
	 * @return the {@link CleanupService} singleton instance
	 */
	public static CleanupService getInstance() {
		synchronized (LOCK) {
			if (INSTANCE == null) {
				INSTANCE = new CleanupService(false);
			}
			return INSTANCE;
		}
	}

	void start() {
		// Nothing to do
	}

	void register(Runnable cleanUp) {
		if (isOsgi) {
			cleanup = cleanUp;
		} else {
			// Ensure the JGitText class is loaded. Depending on the framework
			// JGit runs in, it may not be possible anymore to load classes when
			// the hook runs. For instance when run in a maven plug-in: the
			// Plexus class world that loaded JGit may already have been
			// disposed by the time the JVM shutdown hook runs when the whole
			// maven build terminates.
			jgitText = JGitText.get();
			assert jgitText != null;
			try {
				Runtime.getRuntime().addShutdownHook(new Thread(() -> {
					try {
						cleanUp.run();
						// Don't catch exceptions; let the JVM do the problem
						// reporting.
					} finally {
						jgitText = null;
					}
				}));
			} catch (IllegalStateException e) {
				// Ignore -- the JVM is already shutting down.
			}
		}
	}

	void shutDown() {
		if (isOsgi && cleanup != null) {
			Runnable r = cleanup;
			cleanup = null;
			try {
				r.run();
			} catch (RuntimeException e) {
				LOG.error(JGitText.get().shutdownCleanupFailed, e);
			}
		}
	}
}