Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: c668225a65f13010e816aece1f1e875dc0997bf8 (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/*******************************************************************************
 * Copyright (c) 2005, 2015 Cognos Incorporated, IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Cognos Incorporated - initial API and implementation
 *     IBM Corporation - bug fixes and enhancements
 *     Raymond Augé <raymond.auge@liferay.com> - Bug 436698
 *******************************************************************************/
package org.eclipse.equinox.http.servlet.internal.context;

import java.io.File;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletContext;
import org.eclipse.equinox.http.servlet.internal.util.Const;

/**
 * The ProxyContext provides something similar to a ServletContext for all servlets and resources under a particular ProxyServlet.
 * In particular it holds and represent the concept of "context path" through the Proxy Servlets servlet path.
 * The Http Service also requires a ServletContext namespaced by each individual HttpContext. The ProxyContext provides support for the
 * attribute map of a ServletContext again namespaced by HttpContext as specified in the Http Service specification. The ContextAttributes
 * are reference counted so that when the HttpContext is no longer referenced the associated context attributes can be
 * garbage collected and the context temp dir deleteted.
 */
public class ProxyContext {
	private static final String JAVAX_SERVLET_CONTEXT_TEMPDIR = "javax.servlet.context.tempdir"; //$NON-NLS-1$

	private final ConcurrentMap<ContextController, ContextAttributes> attributesMap =
		new ConcurrentHashMap<ContextController, ContextAttributes>();
	File proxyContextTempDir;
	private ServletContext servletContext;

	public ProxyContext(String contextName, ServletContext servletContext) {
		this.servletContext = servletContext;
		File tempDir = (File) servletContext.getAttribute(JAVAX_SERVLET_CONTEXT_TEMPDIR);
		if (tempDir != null) {
			tempDir = new File(tempDir, "proxytemp"); //$NON-NLS-1$
			proxyContextTempDir = new File(tempDir, contextName);
			deleteDirectory(proxyContextTempDir);
			proxyContextTempDir.mkdirs();
		}
	}

	public void destroy() {
		if (proxyContextTempDir != null)
			deleteDirectory(proxyContextTempDir);
	}

	public String getServletPath() {
		return Const.BLANK;
	}

	public void createContextAttributes(
		ContextController controller) {

		synchronized (attributesMap) {
			ContextAttributes contextAttributes = attributesMap.get(controller);

			if (contextAttributes == null) {
				contextAttributes = new ContextAttributes(controller);

				attributesMap.put(controller, contextAttributes);
			}

			contextAttributes.addReference();
		}
	}

	public void destroyContextAttributes(
		ContextController controller) {

		synchronized (attributesMap) {
			ContextAttributes contextAttributes = attributesMap.get(controller);

			if (contextAttributes == null) {
				throw new IllegalStateException("too many calls"); //$NON-NLS-1$
			}

			if (contextAttributes.removeReference() == 0) {
				attributesMap.remove(controller);
				contextAttributes.destroy();
			}
		}
	}

	public Dictionary<String, Object> getContextAttributes(
		ContextController controller) {

		return attributesMap.get(controller);
	}

	public ServletContext getServletContext() {
		return servletContext;
	}

	/**
	 * deleteDirectory is a convenience method to recursively delete a directory
	 * @param directory - the directory to delete.
	 * @return was the delete succesful
	 */
	protected static boolean deleteDirectory(File directory) {
		if (directory.exists() && directory.isDirectory()) {
			File[] files = directory.listFiles();
			for (File file : files) {
				if (file.isDirectory()) {
					deleteDirectory(file);
				} else {
					file.delete();
				}
			}
		}
		return directory.delete();
	}

	public class ContextAttributes
		extends Dictionary<String, Object> implements Serializable {

		private static final long serialVersionUID = 1916670423277243587L;
		private final AtomicInteger referenceCount = new AtomicInteger();

		public ContextAttributes(ContextController controller) {
			if (proxyContextTempDir != null) {
				File contextTempDir = new File(
					proxyContextTempDir,
					"hc_" + controller.hashCode()); //$NON-NLS-1$

				contextTempDir.mkdirs();

				put(JAVAX_SERVLET_CONTEXT_TEMPDIR, contextTempDir);
			}
		}

		public void destroy() {
			File contextTempDir = (File) get(JAVAX_SERVLET_CONTEXT_TEMPDIR);
			if (contextTempDir != null)
				deleteDirectory(contextTempDir);
		}

		public int addReference() {
			return referenceCount.incrementAndGet();
		}

		public int removeReference() {
			return referenceCount.decrementAndGet();
		}

		public int referenceCount() {
			return referenceCount.get();
		}

		@Override
		public Enumeration<Object> elements() {
			return Collections.enumeration(_map.values());
		}

		@Override
		public Object get(Object key) {
			return _map.get(key);
		}

		@Override
		public boolean isEmpty() {
			return _map.isEmpty();
		}

		@Override
		public Enumeration<String> keys() {
			return Collections.enumeration(_map.keySet());
		}

		@Override
		public Object put(String key, Object value) {
			return _map.put(key, value);
		}

		@Override
		public Object remove(Object key) {
			return _map.remove(key);
		}

		@Override
		public int size() {
			return _map.size();
		}

		private final Map<String, Object> _map =
			new ConcurrentHashMap<String, Object>();
	}

}

Back to the top