-
Notifications
You must be signed in to change notification settings - Fork 138
/
GenericSniffer.java
378 lines (349 loc) · 13.3 KB
/
GenericSniffer.java
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/*
* Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation.
* Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.internal.deployment;
import com.sun.enterprise.module.HK2Module;
import com.sun.enterprise.module.ModuleDefinition;
import com.sun.enterprise.module.ModulesRegistry;
import jakarta.inject.Inject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.StartDocument;
import javax.xml.stream.events.XMLEvent;
import org.glassfish.api.container.Sniffer;
import org.glassfish.api.deployment.DeploymentContext;
import org.glassfish.api.deployment.archive.ArchiveType;
import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.hk2.api.ServiceLocator;
/**
* Generic implementation of the Sniffer service that can be programmatically instantiated
*
* @author Jerome Dochez, Sanjeeb Sahoo
*/
public abstract class GenericSniffer implements Sniffer {
@Inject
protected ModulesRegistry modulesRegistry;
@Inject
protected ServiceLocator habitat;
final private String containerName;
final private String appStigma;
final private String urlPattern;
final private static XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
private HK2Module[] modules;
public GenericSniffer(String containerName, String appStigma, String urlPattern) {
this.containerName = containerName;
this.appStigma = appStigma;
this.urlPattern = urlPattern;
}
/**
* Returns true if the passed file or directory is recognized by this
* composite sniffer.
* @param context deployment context
* @return true if the location is recognized by this sniffer
*/
@Override
public boolean handles(DeploymentContext context) {
ArchiveType archiveType = habitat.getService(ArchiveType.class, context.getArchiveHandler().getArchiveType());
if (archiveType != null && !supportsArchiveType(archiveType)) {
return false;
}
return handles(context.getSource());
}
/**
* Returns the list of annotation names that this sniffer is interested in.
* If an application bundle contains at least one class annotated with
* one of the returned annotations, the deployment process will not
* call the handles method but will invoke the containers deployers as if
* the handles method had been called and returned true.
*
* @param context deployment context
* @return list of annotations this sniffer is interested in or an empty array
*/
@Override
public String[] getAnnotationNames(DeploymentContext context) {
List<String> annotationNames = new ArrayList<>();
for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {
annotationNames.add(annotationType.getName());
}
return annotationNames.toArray(new String[annotationNames.size()]);
}
/**
* Returns true if the passed file or directory is recognized by this
* instance.
*
* @param location the file or directory to explore
* @return true if this sniffer handles this application type
*/
@Override
public boolean handles(ReadableArchive location) {
if (appStigma != null) {
try {
if (location.exists(appStigma)) {
return true;
}
} catch (IOException e) {
// ignore
}
}
return false;
}
/**
* Returns the pattern to apply against the request URL
* If the pattern matches the URL, the service method of the associated
* container will be invoked
*
* @return pattern instance
*/
@Override
public String[] getURLPatterns() {
if (urlPattern!=null) {
return new String[] {urlPattern};
} else {
return null;
}
}
/**
* Returns the container name associated with this sniffer.
* <p>
* WARNING: This is different from {@link ArchiveType}!
*
* @return the container name
*/
@Override
public String getModuleType() {
return containerName;
}
/**
* Sets up the container libraries so that any imported bundle from the
* connector jar file will now be known to the module subsystem
*
* This method returns a {@link ModuleDefinition} for the module containing
* the core implementation of the container. That means that this module
* will be locked as long as there is at least one module loaded in the
* associated container.
*
* @param containerHome is where the container implementation resides (Not used anymore)
* @param logger the logger to use
* @return the module definition of the core container implementation.
*
* @throws java.io.IOException exception if something goes sour
*/
@Override
public synchronized HK2Module[] setup(String containerHome, Logger logger) throws IOException { // TODO(Sahoo): Change signature to not accept containerHome or logger
if (modules != null) {
if (logger.isLoggable(Level.FINE)) {
logger.logp(Level.FINE, "GenericSniffer", "setup", "{0} has already setup {1} container, so just returning.", new Object[]{this, containerName});
}
return modules;
}
List<HK2Module> tmp = new ArrayList<>();
for (String moduleName : getContainerModuleNames()) {
HK2Module m = modulesRegistry.makeModuleFor(moduleName, null);
if (m != null) {
tmp.add(m);
} else {
throw new RuntimeException("Unable to set up module " + moduleName);
}
}
modules = tmp.toArray(new HK2Module[tmp.size()]);
return modules;
}
protected String[] getContainerModuleNames() {
return new String[0];
}
/**
* Tears down a container, remove all imported libraries from the module
* subsystem.
*
*/
@Override
public void tearDown() {
// It is not safe to uninstall modules in a running server as there might be existing
// references to objects loaded from those modules, so we don't uninstall modules at this point of time.
}
/**
* Returns the list of annotations types that this sniffer is interested in.
* If an application bundle contains at least one class annotated with
* one of the returned annotations, the deployment process will not
* call the handles method but will invoke the containers deployers as if
* the handles method had been called and returned true.
*
* @return list of annotations this sniffer is interested in.
*/
@Override
public Class<? extends Annotation>[] getAnnotationTypes() {
return new Class[0];
}
/**
* @return false
*/
@Override
public boolean isUserVisible() {
return false;
}
/**
* @return false
*/
@Override
public boolean isJakartaEE() {
return false;
}
@Override
public boolean equals(Object other) {
if (other instanceof Sniffer) {
Sniffer otherSniffer = (Sniffer)other;
return getModuleType().equals(otherSniffer.getModuleType());
}
return false;
}
@Override
public int hashCode() {
int hash = 5;
hash = 71 * hash + (getModuleType() != null ? getModuleType().hashCode() : 0);
return hash;
}
/**
* Returns a map of deployment configurations composed by reading from a
* list of paths in the readable archive. (For Jakarta EE applications the
* deployment configurations correspond to the deployment descriptors.) The
* {@link #getDeploymentConfigurationPaths} method returns this list of paths
* which might exist in archives that this sniffer handles.
* <p>
* In each returned map entry the key is a path and the value is the
* contents of the archive entry at that path. This method creates a map
* entry only if the path exists in the readable archive.
* <p>
* Sniffers for applications that do not store their configurations as
* deployment descriptors at predictable paths within an archive are free
* to override this implementation to return whatever information is
* appropriate to that application type. A key usage of the returned
* Map is in the application type's GUI plug-in (if desired) to allow
* users to customize the deployment configuration after the application
* has been deployed. The concrete Sniffer implementation and the
* GUI plug-in must agree on the conventions for storing deployment
* configuration inforation in the Map.
*
* @param location the readable archive for the application of interest
* @return a map from path names to the contents of the archive entries
* at those paths
* @throws java.io.IOException in case of errors retrieving an entry or
* reading the archive contents at an entry
*/
@Override
public Map<String,String> getDeploymentConfigurations(final ReadableArchive location) throws IOException {
final Map<String,String> deploymentConfigs = new HashMap<>();
for (String path : getDeploymentConfigurationPaths()) {
InputStream is = null;
try {
is = location.getEntry(path);
if (is != null) {
String dc = readDeploymentConfig(is);
deploymentConfigs.put(path, dc);
}
} finally {
if (is != null) {
is.close();
}
}
}
return deploymentConfigs;
}
/**
* Returns a list of paths within an archive that represents deployment
* configuration files.
* <p>
* Sniffers that recognize Jakarta EE applications typically override this
* default implementation to return a list of the deployment descriptors
* that might appear in the type of Jakarta EE application which the sniffer
* recognizes. For example, the WebSniffer implementation of this method
* returns WEB-INF/web.xml, WEB-INF/glassfish-web.xml and
* WEB-INF/sun-web.xml.
*
* @return list of paths in the archive where deployment configuration
* archive entries might exist
*/
protected List<String> getDeploymentConfigurationPaths() {
return Collections.EMPTY_LIST;
}
/**
* @return the set of the sniffers that should not co-exist for the
* same module. For example, ejb and appclient sniffers should not
* be returned in the sniffer list for a certain module.
* This method will be used to validate and filter the retrieved sniffer
* lists for a certain module
*
*/
@Override
public String[] getIncompatibleSnifferTypes() {
return null;
}
private String readDeploymentConfig(final InputStream is) throws IOException {
String encoding = null;
XMLEventReader rdr = null;
try {
is.mark(Integer.MAX_VALUE);
rdr = xmlInputFactory.createXMLEventReader(
new InputStreamReader(is));
while (rdr.hasNext()) {
final XMLEvent ev = rdr.nextEvent();
if (ev.isStartDocument()) {
final StartDocument sd = (StartDocument) ev;
encoding = sd.getCharacterEncodingScheme();
break;
}
}
} catch (XMLStreamException e) {
if (rdr != null) {
try {
rdr.close();
} catch (XMLStreamException inner) {
throw new IOException(e);
}
}
throw new IOException(e);
}
if (encoding == null) {
encoding = "UTF-8";
}
is.reset();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bytesRead;
final byte [] buffer = new byte[1024];
while (( bytesRead = is.read(buffer)) != -1 ) {
baos.write(buffer, 0, bytesRead);
}
try {
rdr.close();
} catch (XMLStreamException ex) {
throw new IOException(ex);
}
is.close();
return new String(baos.toByteArray(), encoding);
}
}