-
Notifications
You must be signed in to change notification settings - Fork 136
/
Server.java
540 lines (473 loc) · 18.8 KB
/
Server.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
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation
* Copyright (c) 2009, 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.embedded;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.api.container.Sniffer;
import org.glassfish.embeddable.BootstrapProperties;
import org.glassfish.embeddable.GlassFish;
import org.glassfish.embeddable.GlassFishException;
import org.glassfish.embeddable.GlassFishProperties;
import org.glassfish.embeddable.GlassFishRuntime;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
import org.jvnet.hk2.annotations.Contract;
/**
* Instances of server are embedded application servers, capable of attaching various containers
* (entities running users applications).
*
* @author Jerome Dochez
*/
@Contract
public class Server {
/**
* Builder for creating embedded server instance. Builder can be used to configure
* the logger, the verbosity and the embedded file system which acts as a
* virtual file system to the embedded server instance.
*/
public static class Builder {
final String serverName;
EmbeddedFileSystem fileSystem;
/**
* Creates an unconfigured instance. The habitat will be obtained
* by scanning the inhabitants files using this class's classloader
*
* @param id the server name
*/
public Builder(String id) {
this.serverName = id;
}
/**
* Enables or disables the logger for this server
*
* @param enabled true to enable, false to disable
* @return this instance
*/
public Builder logger(boolean enabled) {
return this;
}
/**
* Sets the log file location
*
* @param f a valid file location
* @return this instance
*/
public Builder logFile(File f) {
return this;
}
/**
* Turns on of off the verbose flag.
*
* @param b true to turn on, false to turn off
* @return this instance
*/
public Builder verbose(boolean b) {
return this;
}
/**
* Set the jmx port number. Also enables the jmx connector. This applies
* only when the default configuration is being used.
*
* @param portNumber jmx port number.
* @return this instance
*/
public Builder jmxPort(int portNumber) {
return this;
}
/**
* Sets the embedded file system for the application server, used to locate
* important files or directories used through the server lifetime.
*
* @param fileSystem a virtual filesystem
* @return this instance
*/
public Builder embeddedFileSystem(EmbeddedFileSystem fileSystem) {
this.fileSystem = fileSystem;
return this;
}
/**
* Uses this builder's name to create or return an existing embedded
* server instance.
* The embedded server will be using the configured parameters
* of this builder. If no embedded file system is used, the embedded instance will use
* a temporary instance root with a default basic configuration. That temporary instance
* root will be deleted once the server is shutdown.
*
* @return the configured server instance
*/
public Server build() {
return build(null);
}
/**
* Uses this builder's name to create or return an existing embedded
* server instance.
* The embedded server will be using the configured parameters
* of this builder. If no embedded file system is used, the embedded instance will use
* a temporary instance root with a default basic configuration. That temporary instance
* root will be deleted once the server is shutdown.
*
* @param properties extra creation properties
*
* @return the configured server instance
*/
public Server build(Properties properties) {
synchronized(servers) {
if (!servers.containsKey(serverName)) {
Server s = new Server(this, properties);
servers.put(serverName, s);
return s;
}
throw new IllegalStateException("An embedded server of this name already exists");
}
}
}
private final static class Container {
private final EmbeddedContainer container;
boolean started;
private Container(EmbeddedContainer container) {
this.container = container;
}
}
private final static Map<String, Server> servers = new HashMap<>();
private final String serverName;
private final ServiceHandle<EmbeddedFileSystem> fileSystem;
private final ServiceLocator habitat;
private final List<Container> containers = new ArrayList<>();
private final GlassFish glassfish;
private static GlassFishRuntime glassfishRuntime;
private static final Logger logger = Logger.getAnonymousLogger();
private void setBootstrapProperties(BootstrapProperties props, EmbeddedFileSystem fs) {
props.setProperty("GlassFish_Platform", "Static");
if (fs != null) {
String instanceRoot = fs.instanceRoot != null ? fs.instanceRoot.getAbsolutePath() : null;
String installRoot = fs.installRoot != null ? fs.installRoot.getAbsolutePath() : instanceRoot;
if (installRoot != null) {
props.setInstallRoot(installRoot);
}
}
}
private void setGlassFishProperties(GlassFishProperties props, EmbeddedFileSystem fs) {
props.setProperty("-type", "EMBEDDED");
props.setProperty("org.glassfish.persistence.embedded.weaving.enabled", "false");
if (fs != null) {
String instanceRoot = fs.instanceRoot != null ? fs.instanceRoot.getAbsolutePath() : null;
if (instanceRoot != null) {
props.setInstanceRoot(fs.instanceRoot.getAbsolutePath());
}
if (fs.configFile != null) {
props.setConfigFileURI(fs.configFile.toURI().toString());
}
if (fs.autoDelete) {
props.setProperty("org.glassfish.embeddable.autoDelete", "true");
}
}
// TODO :: Support modification of jmxPort
}
private Server(Builder builder, Properties properties) {
serverName = builder.serverName;
try {
if(properties == null) {
properties = new Properties();
}
EmbeddedFileSystem fs = builder.fileSystem;
BootstrapProperties bootstrapProps = new BootstrapProperties(properties);
setBootstrapProperties(bootstrapProps, fs);
glassfishRuntime = GlassFishRuntime.bootstrap(bootstrapProps, getClass().getClassLoader());
GlassFishProperties gfProps = new GlassFishProperties(properties);
setGlassFishProperties(gfProps, fs);
glassfish = glassfishRuntime.newGlassFish(gfProps);
glassfish.start();
if(fs == null) {
EmbeddedFileSystem.Builder fsBuilder = new EmbeddedFileSystem.Builder();
fsBuilder.autoDelete(true);
fs = fsBuilder.build();
}
// Add the neccessary inhabitants.
habitat = glassfish.getService(ServiceLocator.class);
ServiceLocatorUtilities.addOneConstant(habitat, this);
fileSystem = habitat.getServiceHandle(
ServiceLocatorUtilities.<EmbeddedFileSystem>addOneConstant(habitat, fs, null, EmbeddedFileSystem.class));
logger.logp(Level.FINER, "Server", "<init>", "Created GlassFish = {0}, " +
"GlassFish Status = {1}", new Object[]{glassfish, glassfish.getStatus()});
} catch (Throwable ex) {
throw new RuntimeException(ex);
}
}
/**
* Returns the list of existing embedded instances
*
* @return list of the instanciated embedded instances.
*/
public static List<String> getServerNames() {
List<String> names = new ArrayList<>();
names.addAll(servers.keySet());
return names;
}
/**
* Returns the embedded server instance of a particular name
*
* @param name requested server name
* @return a server instance if it exists, null otherwise
*/
public static Server getServer(String name) {
return servers.get(name);
}
// todo : have the same name, and make it clear we use the type string().
/**
* Get the embedded container configuration for a container type.
* @param type the container type (e.g. Type.ejb)
* @return the embedded configuration for this container
*/
public ContainerBuilder<EmbeddedContainer> createConfig(ContainerBuilder.Type type) {
return createConfig(type.toString());
}
/**
* Get the embedded container builder for a container type identified by its
* name.
* @param name the container name, which is the name used on the @Service annotation
* @return the embedded builder for this container
*/
@SuppressWarnings("unchecked")
public ContainerBuilder<EmbeddedContainer> createConfig(String name) {
return habitat.getService(ContainerBuilder.class, name);
}
/**
* Get an embedded container configuration. The type of the expected
* configuration is passed to the method and is not necessarily known to
* the glassfish embedded API. This type of configuration is used for
* extensions which are not defined by the core glassfish project.
*
* The API stability of the interfaces returned by this method is outside the
* scope of the glassfish-api stability contract, it's a private contract
* between the provider of that configuration and the user.
*
* @param configType the type of the embedded container configuration
* @param <T> type of the embedded container
* @return the configuration to configure a container of type <T>
*/
public <T extends ContainerBuilder<?>> T createConfig(Class<T> configType) {
return habitat.getService(configType);
}
/**
* Adds a container of a particular type using the default operating
* configuration for the container.
*
* @param type type of the container to be added (like web, ejb).
* @throws IllegalStateException if the container is already started.
*/
public synchronized void addContainer(final ContainerBuilder.Type type) {
containers.add(new Container(new EmbeddedContainer() {
final List<Container> delegates = new ArrayList<>();
final ArrayList<Sniffer> sniffers = new ArrayList<>();
@Override
public List<Sniffer> getSniffers() {
synchronized(sniffers) {
if (sniffers.isEmpty()) {
if (type == ContainerBuilder.Type.all) {
for (final ContainerBuilder.Type t : ContainerBuilder.Type.values()) {
if (t!=ContainerBuilder.Type.all) {
delegates.add(getContainerFor(t));
}
}
} else {
delegates.add(getContainerFor(type));
}
}
for (Container c : delegates) {
sniffers.addAll(c.container.getSniffers());
}
}
return sniffers;
}
@Override
public void bind(Port port, String protocol) {
for (Container delegate : delegates) {
delegate.container.bind(port, protocol);
}
}
private Container getContainerFor(final ContainerBuilder.Type type) {
ContainerBuilder b = createConfig(type);
if (b!=null) {
return new Container(b.create(Server.this));
} else {
return new Container(new EmbeddedContainer() {
@Override
public List<Sniffer> getSniffers() {
List<Sniffer> sniffers = new ArrayList<>();
Sniffer s = habitat.getService(Sniffer.class, type.toString());
if (s!=null) {
sniffers.add(s);
}
return sniffers;
}
@Override
public void bind(Port port, String protocol) {
}
@Override
public void start() throws LifecycleException {
}
@Override
public void stop() throws LifecycleException {
}
});
}
}
@Override
public void start() throws LifecycleException {
for (Container c : delegates) {
if (!c.started) {
c.container.start();
c.started=true;
}
}
}
@Override
public void stop() throws LifecycleException {
for (Container c : delegates) {
if (c.started) {
c.container.stop();
c.started=false;
}
}
}
}));
}
// todo : clarify that adding containers after the server is created is illegal
// todo : makes the return of those APIs return void.
/**
* Adds a container to this server.
*
* Using the configuration instance for the container of type <T>,
* creating the container from that configuration and finally adding the
* container instance to the list of managed containers
*
* @param info the configuration for the container. Must not be null.
* @param <T> type of the container
* @return instance of the container <T>
* @throws IllegalStateException if the container is already started.
*/
public synchronized <T extends EmbeddedContainer> T addContainer(ContainerBuilder<T> info) {
T container = info.create(this);
if (container != null && containers.add(new Container(container))) {
return container;
}
return null;
}
/**
* Returns a list of the currently managed containers
*
* @return the containers list
*/
public Collection<EmbeddedContainer> getContainers() {
ArrayList<EmbeddedContainer> copy = new ArrayList<>();
for (Container c : containers) {
copy.add(c.container);
}
return copy;
}
/**
* Creates a port to attach to embedded containers. Ports can be attached to many
* embedded containers and some containers may accept more than one port.
*
* @param portNumber port number for this port
* @return a new port abstraction.
* @throws IOException if the port cannot be opened.
*/
public Port createPort(int portNumber) throws IOException {
Ports ports = habitat.getService(Ports.class);
return ports.createPort(portNumber);
}
/**
* Returns the server name, as specified in {@link Server.Builder#Builder(String)}
*
* @return container name
*/
public String getName(){
return serverName;
}
/**
* Returns the embedded file system used to run this embedded instance.
*
* @return embedded file system used by this instance
*/
public EmbeddedFileSystem getFileSystem() {
return fileSystem.getService();
}
/**
* Starts the embedded server, opening ports, and running the startup
* services.
*
* @throws LifecycleException if the server cannot be started propertly
*/
public synchronized void start() throws LifecycleException {
if(glassfish != null) {
try {
if (glassfish.getStatus() != GlassFish.Status.STARTED) {
glassfish.start();
}
} catch (GlassFishException e) {
throw new LifecycleException(e); // TODO(Sahoo): Proper Exception Handling
}
logger.finer("GlassFish has been started");
}
}
/**
* stops the embedded server instance, any deployed application will be stopped
* ports will be closed and shutdown services will be ran.
* EmbeddedFileSystem will be released, meaning that any managed directory will
* be deleted rendering the EmbeddedFileSystem unusable.
*
* @throws LifecycleException if the server cannot shuts down properly
*/
public synchronized void stop() throws LifecycleException {
try {
if (glassfish != null) {
glassfish.stop();
logger.finer("GlassFish has been stopped");
}
if (glassfishRuntime != null) {
glassfishRuntime.shutdown();
logger.finer("GlassFishruntime has been shutdown");
}
} catch (Exception ex) {
logger.log(Level.WARNING, ex.getMessage(), ex);
} finally {
synchronized(servers) {
servers.remove(serverName);
}
fileSystem.getService().preDestroy();
}
}
/**
* Returns the embedded deployer implementation, can be used to
* generically deploy applications to the embedded server.
*
* @return embedded deployer
*/
public EmbeddedDeployer getDeployer() {
return habitat.getService(EmbeddedDeployer.class);
}
}