StandardServer.java
1 |
/*
|
---|---|
2 |
** Module : StandardServer.java
|
3 |
** Abstract : standard server application
|
4 |
**
|
5 |
** Copyright (c) 2005-2023, Golden Code Development Corporation.
|
6 |
**
|
7 |
** -#- -I- --Date-- --JPRM-- ---------------------------------Description-------------------------------------
|
8 |
** 001 NVS 20050831 @22451 Created the initial version.
|
9 |
** 002 NVS 20050915 @22735 Alternative path /server/default/exports is
|
10 |
** tried if no /server/serverID/exports exists.
|
11 |
** Standard entry point processing added, based
|
12 |
** on directory entries under /server/serverID/
|
13 |
** entry/accountID.
|
14 |
** 003 NVS 20051221 @23733 Saves the reference to the queue in the session
|
15 |
** context so it can be accessed at any time.
|
16 |
** 004 GES 20060110 @23850 Implemented proper transaction manager
|
17 |
** infrastructure and init such that converted
|
18 |
** P2J applications can be run with this
|
19 |
** class as their launch point.
|
20 |
** 005 ECF 20060114 @23904 Moved instantiation of target class to the
|
21 |
** invoke() method. This is necessary to include
|
22 |
** constructors/initializers within the first
|
23 |
** TransactionManager scope created. Modified
|
24 |
** various catch statements to not discard stack
|
25 |
** traces of caught exceptions.
|
26 |
** 006 GES 20060130 @24143 Added quit state flag processing in invoke.
|
27 |
** 007 GES 20060202 @24229 Added new flag in pushScope() signature.
|
28 |
** 008 GES 20060203 @24244 Changed TransactionManager method name
|
29 |
** makeBackup() to blockSetup().
|
30 |
** 009 GES 20060210 @24528 Shifted to a simpler approach for server
|
31 |
** exports. First pass (more is coming later).
|
32 |
** 010 NVS 20060226 @24750 Made portions of invoke() reusable in Utils.
|
33 |
** Calling reusable invoke() method from here.
|
34 |
** 011 GES 20060305 @24891 Cleaned up logging.
|
35 |
** 012 NVS 20060306 @24901 Added exports registration for ErrorManager.
|
36 |
** 013 GES 20060314 @25062 Fixed exception processing to properly retry on STOP.
|
37 |
** 014 NVS 20060417 @25578 Added support for the password aging. Before
|
38 |
** the configured application entry point is
|
39 |
** taken, the user account is checked for the
|
40 |
** aged password, and, if so, the password
|
41 |
** change method is called..
|
42 |
** 015 GES 20060608 @27054 Added logging of any abnormal end before it
|
43 |
** is re-thrown. ConditionExceptions and
|
44 |
** RetryUnwindExceptions are not logged as these
|
45 |
** are "normal" transaction manager flow of
|
46 |
** control interactions.
|
47 |
** 016 NVS 20060614 @27196 Fixed a problem with NET package exports of
|
48 |
** SecurityManager class.
|
49 |
** Added a call to RemoteObject to do deferred
|
50 |
** registrations.
|
51 |
** 017 GES 20060804 @28432 Honor new iterate() processing which only
|
52 |
** happens at the top of the block body.
|
53 |
** 018 NVS 20060617 @30461 Implemented stop_disposition configuration
|
54 |
** option reading and honoring when the business
|
55 |
** logic encounters STOP condition.
|
56 |
** 019 GES 20061212 @31666 Force "quit" processing in any fatal abend
|
57 |
** (something other than one of the "expected"
|
58 |
** 4GL conditions). This avoids a call to
|
59 |
** LogicalTerminal.pauseBeforeEnd() which will
|
60 |
** fail in any case where we are exiting because
|
61 |
** the client was forcefully disconnected.
|
62 |
** 020 GES 20061219 @31718 Removed dead code.
|
63 |
** 021 GES 20070111 @31782 Reworked notifiable interface as needed.
|
64 |
** 022 NVS 20070126 @32004 Application entry point is now the subject to
|
65 |
** the standardized search in the directory.
|
66 |
** The node is named "p2j-entry" to separate it
|
67 |
** from the server level entry point.
|
68 |
** 023 NVS 20070221 @32381 Administration Server exports are registered.
|
69 |
** 024 EVL 20070609 @34005 Adding explicit import of the class
|
70 |
** com.goldencode.p2j.net.Queue to eliminate
|
71 |
** conflict with the same class from java.util
|
72 |
** package to be able to compile for Java 6
|
73 |
** 025 GES 20071011 @35424 Eliminated compile warning.
|
74 |
** 026 ECF 20071112 @35892 Refactored to leverage net package changes.
|
75 |
** Replaced initialize() with bootstrap(). All
|
76 |
** non-network function that was previously in
|
77 |
** ServerBootstrap is now in this class, while
|
78 |
** the network-specific function is now handled
|
79 |
** by the net package's SessionManager.
|
80 |
** 027 GES 20071214 @36410 Added code to translate unexpected exceptions
|
81 |
** (but not errors) into a stop condition. This
|
82 |
** will avoid the silent drop-out to the command
|
83 |
** line unless the stop disposition is also
|
84 |
** configured to silently exit (disposition 2).
|
85 |
** 028 GES 20080409 @37912 Export remote directory access when the network protocol is ready.
|
86 |
** 029 NVS 20090312 @41522 Added initialization of the AdminServerImpl class.
|
87 |
** 030 SIY 20090507 @42111 Added support for context hooks.
|
88 |
** 031 SIY 20090511 @42142 Added support for server hooks.
|
89 |
** 032 SIY 20090512 @42144 Reworked server hooks to support more than one server hook.
|
90 |
** 033 SIY 20090515 @42188 All services now are started as server hooks and hardcoded.
|
91 |
** 034 NVS 20090528 @42482 Custom account extension plugin is loaded at startup, if defined
|
92 |
** in the directory
|
93 |
** 035 GES 20090722 @43331 Handle chained errors as errors not exceptions.
|
94 |
** 036 GES 20090723 @43359 Avoid logging STOP conditions.
|
95 |
** 037 NVS 20090817 @43684 Server hook load failures are logged but are not critical for the
|
96 |
** server startup.
|
97 |
** 038 NVS 20090818 @43690 Excluded the exception stack trace from the error message in #037.
|
98 |
** 039 NVS 20090821 @43711 Admin Enabled flag is now searched correctly scoped to the per
|
99 |
** server scope.
|
100 |
** 040 NVS 20090826 @43775 Passing server name to the LogHelper.initialize().
|
101 |
** 041 ECF 20090827 @43787 Fixed logging to file. SecureFileHandler is now used when
|
102 |
** initializing the LogHelper, and is closed when the server is
|
103 |
** finished shutting down.
|
104 |
** 042 CA 20090901 @43809 On bootstrap, SessionManager will be set a field which contains the
|
105 |
** InterruptHandler implementation.
|
106 |
** 043 CA 20090917 @43927 Fix abnormal connection end. In invoke(), any SilentUnwindExceptions
|
107 |
** will stop the Conversation thread.
|
108 |
** 044 NVS 20090918 @43950 Reworked invoke() method to take an instance of Isolatable interface
|
109 |
** to allow the reuse of invoke for admin server purposes where some
|
110 |
** methods have to run in a transaction manager environment to be able
|
111 |
** to access the database properly, including modifications. Reworked
|
112 |
** standardEntry() method to comply with the new requirements; added
|
113 |
** the inner class MainInvoker that encapsulates the client entry
|
114 |
** specific logic.
|
115 |
** 045 NVS 20090928 @44048 Server startup is protected against non-existent directory.
|
116 |
** 046 LMR 20101210 Edited bootstrap() and registerDefaultServices() to initialize the
|
117 |
** AdminGate service only if net/connection/secure is set to true in
|
118 |
** the bootstrap config (if not found it defaults to false).
|
119 |
** 047 GES 20111004 Reworked the web server startup to more generically support multiple
|
120 |
** applets (not just admin alone).
|
121 |
** 048 GES 20111025 Signature changes in calling RemoteObject to export methods. Added
|
122 |
** waitUntilReady().
|
123 |
** 049 CA 20111130 Use the system classloader to load hooks, as Class.forName caches
|
124 |
** the loaded class in some native code from which it can never be
|
125 |
** removed. Before initializing hooks and exports, create class loaders
|
126 |
** for all the jars defined in the directory.
|
127 |
** 050 CA 20130529 Added appserver support.
|
128 |
** 051 CA 20130705 Register the appserver launcher.
|
129 |
** 052 SVL 20130624 Set runtime Configuration to be used.
|
130 |
** 053 HC 20130902 For #2164 added warning log when server persistence is inactive.
|
131 |
** 054 CA 20131004 Expose client-side parameter to server-side application code.
|
132 |
** 055 CA 20131023 At runtime, the in-memory registry plugin must be used.
|
133 |
** 056 MAG 20131101 Upgrade to jetty 9.1 embedded server.
|
134 |
** 057 MAG 20131114 Add Handler for Chui Web client.
|
135 |
** 058 OM 20131210 Made DatabaseTriggerManager Scopable context-local instantiation.
|
136 |
** 059 EVL 20131115 Adding UnstoppableExitException handling for unrecoverable errors in
|
137 |
** batch mode. The serve side of the ErrorManages is also initialized
|
138 |
** here.
|
139 |
** 060 MAG 20131223 Get Chui Web client target root from directory. Initialize temporary
|
140 |
** accounts pool.
|
141 |
** 061 MAG 20140128 Change the main return type from boolean to Result. For web
|
142 |
** clients provide temporary account credentials.
|
143 |
** 062 MAG 20140131 Reverse changes at 061 (2014-01-28).
|
144 |
** 063 CA 20140206 Added scheduler support.
|
145 |
** 064 MAG 20140217 Added terminate method to web server hooker.
|
146 |
** 065 EVL 20140327 Adding the support to get the server properties to client.
|
147 |
** 066 ECF 20140404 Initialize conversion pool during bootstrap.
|
148 |
** 067 ECF 20140430 Removed stack trace from conversion pool initialization warning.
|
149 |
** Enhanced warning message and added debug-level logging to provide
|
150 |
** more information and stack trace.
|
151 |
** 068 MAG 20140707 Implements remote launcher (broker). Exports broker API.
|
152 |
** 069 GES 20141229 Added support for user-specified entry points.
|
153 |
** 070 GES 20150310 Made web client support more generic (it isn't chui-specific now).
|
154 |
** 071 CA 20150622 Context-local client parameters (localParams field) must not be reset.
|
155 |
** Added database statistics collection (ECF).
|
156 |
** 072 CA 20160205 Added ServerKeyStore.initialize() at the server startup hooks.
|
157 |
** 073 IAS 20160329 Provide client-side parameters in a separate call
|
158 |
** 074 GES 20160121 Added WebClientLauncher service registration.
|
159 |
** 075 OM 20160422 Added project token implementation for multiple project support.
|
160 |
** 076 OM 20160527 SourceNameMapper.convertName() changed to convertNameToClass().
|
161 |
** 077 OM 20160927 Activated BatchMode when expressly requested by client.
|
162 |
** 078 HC 20170118 FWD version is printed to the log output on server start.
|
163 |
** 079 CA 20170228 Added PUBLISH/SUBSCRIBE/UNSUBSCRIBE extensions for global support and
|
164 |
** for external applications.
|
165 |
** 080 SBI 20170628 Added sessions listener to manage web client sessions.
|
166 |
** 081 HC 20170612 Changes related to implementation of new GWT-based Admin client.
|
167 |
** 082 ECF 20180615 Read list of jars containing application resources from the
|
168 |
** directory during bootstrap, so resource searches can be limited to
|
169 |
** these jars.
|
170 |
** 083 CA 20180724 Allow the embedded web app server to run from within FWD.
|
171 |
** 084 CA 20190611 Added support for legacy services (REST).
|
172 |
** 085 CA 20190703 Added support for WebHandler services.
|
173 |
** 086 ECF 20190521 Register a controller at server startup for method execution tracing.
|
174 |
** 087 CA 20190710 The legacy services need to be registered after their handlers have
|
175 |
** been initialized.
|
176 |
** 088 GES 20200213 Bypass termination of embedded mode, REST services and the web
|
177 |
** handler when if they were never initialized.
|
178 |
** 089 CA 20200514 Added support for SOAP web services.
|
179 |
** CA 20200527 Allow the project token to be overridden via a client:project:token setting.
|
180 |
** 090 CA 20200915 Upgraded to Jetty 9.4.22.
|
181 |
** 091 CA 20200930 Force ANTLR to use Class.forName instead of loadClass.
|
182 |
** 092 SBI 20210413 Added web content handlers to public static content.
|
183 |
** CA 20210511 The client's entry point (be it from directory's 'p2j-entry' or the client's
|
184 |
** startup-procedure configuration) will be executed as a "RUN external-program"
|
185 |
** statement, if the target is an external program which can be resolved.
|
186 |
** CA 20210512 Refactored CA/20210511 so that the legacy RUN emulation and the old 'execute'
|
187 |
** method are separated.
|
188 |
** IAS 20210520 Added support for session-based database auto-connect
|
189 |
** HC 20210725 Added support for v6colon.
|
190 |
** GES 20210827 Added driver-level diagnostics information logged at server startup.
|
191 |
** SBI 20210923 Added FontTable initialization.
|
192 |
** OM 20190620 Added -profile command line option for specifying the configuration profile.
|
193 |
** CA 20210928 Set the temp directory for the FWD admin web app.
|
194 |
** CA 20211114 The InMemoryRegistryPlugin instance at the AstManager must be context-local, for
|
195 |
** runtime conversion to work in concurrent mode.
|
196 |
** CA 20220405 Added authentication and authorization for web requests. When this is enabled,
|
197 |
** the target API call will be executed under the authenticated FWD context, and not
|
198 |
** the agent's context.
|
199 |
** CA 20220901 Refactored scope notification support: ScopeableFactory was removed, and the
|
200 |
** registration is now specific to each type of scopeable. For each case, the block
|
201 |
** will be registered for scope support (for that particular scopeable) only when
|
202 |
** the scopeable is 'active' (i.e. unnamed streams or accumulators are used). This
|
203 |
** allows a lazy registration of scopeables, to avoid the unnecessary overhead of
|
204 |
** processing all the scopeables for each and every block.
|
205 |
** CA 20220906 Moved the interactive client cleaner to LogicalTerminal.registerCleaner().
|
206 |
** TJD 20220504 Migration to Java 11 minor changes
|
207 |
** SBI 20221215 Changed log message for loadStartup to specify the failure cause.
|
208 |
** CA 20220501 Each profile has the same structure as the main 'global' config. Allow multiple
|
209 |
** profiles to be ran at once, with the conversion switching the state between
|
210 |
** profiles, when a resource (like a file or namespace) is being processed. Only
|
211 |
** front phase is supported at this time.
|
212 |
** CA 20220520 Initialize SourceNameMapper at server startup.
|
213 |
** 093 VVT 20230318 UnitTestEngine static network server registration added. See #6237.
|
214 |
** 094 GBB 20230412 Initialize LegacyLogManager.
|
215 |
** 095 GBB 20230608 Added a flag to LegacyLogManagerConfigs for server-side filesystem in use.
|
216 |
*/
|
217 |
|
218 |
/*
|
219 |
** This program is free software: you can redistribute it and/or modify
|
220 |
** it under the terms of the GNU Affero General Public License as
|
221 |
** published by the Free Software Foundation, either version 3 of the
|
222 |
** License, or (at your option) any later version.
|
223 |
**
|
224 |
** This program is distributed in the hope that it will be useful,
|
225 |
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
226 |
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
227 |
** GNU Affero General Public License for more details.
|
228 |
**
|
229 |
** You may find a copy of the GNU Affero GPL version 3 at the following
|
230 |
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
|
231 |
**
|
232 |
** Additional terms under GNU Affero GPL version 3 section 7:
|
233 |
**
|
234 |
** Under Section 7 of the GNU Affero GPL version 3, the following additional
|
235 |
** terms apply to the works covered under the License. These additional terms
|
236 |
** are non-permissive additional terms allowed under Section 7 of the GNU
|
237 |
** Affero GPL version 3 and may not be removed by you.
|
238 |
**
|
239 |
** 0. Attribution Requirement.
|
240 |
**
|
241 |
** You must preserve all legal notices or author attributions in the covered
|
242 |
** work or Appropriate Legal Notices displayed by works containing the covered
|
243 |
** work. You may not remove from the covered work any author or developer
|
244 |
** credit already included within the covered work.
|
245 |
**
|
246 |
** 1. No License To Use Trademarks.
|
247 |
**
|
248 |
** This license does not grant any license or rights to use the trademarks
|
249 |
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
|
250 |
** of Golden Code Development Corporation. You are not authorized to use the
|
251 |
** name Golden Code, FWD, or the names of any author or contributor, for
|
252 |
** publicity purposes without written authorization.
|
253 |
**
|
254 |
** 2. No Misrepresentation of Affiliation.
|
255 |
**
|
256 |
** You may not represent yourself as Golden Code Development Corporation or FWD.
|
257 |
**
|
258 |
** You may not represent yourself for publicity purposes as associated with
|
259 |
** Golden Code Development Corporation, FWD, or any author or contributor to
|
260 |
** the covered work, without written authorization.
|
261 |
**
|
262 |
** 3. No Misrepresentation of Source or Origin.
|
263 |
**
|
264 |
** You may not represent the covered work as solely your work. All modified
|
265 |
** versions of the covered work must be marked in a reasonable way to make it
|
266 |
** clear that the modified work is not originating from Golden Code Development
|
267 |
** Corporation or FWD. All modified versions must contain the notices of
|
268 |
** attribution required in this license.
|
269 |
*/
|
270 |
|
271 |
package com.goldencode.p2j.main; |
272 |
|
273 |
import java.io.*; |
274 |
import java.lang.reflect.*; |
275 |
import java.text.*; |
276 |
import java.util.*; |
277 |
import java.util.concurrent.*; |
278 |
import java.util.function.*; |
279 |
import java.util.logging.*; |
280 |
import java.util.logging.Formatter; |
281 |
|
282 |
import javax.servlet.*; |
283 |
|
284 |
import org.eclipse.jetty.server.handler.*; |
285 |
import org.eclipse.jetty.util.resource.Resource; |
286 |
import org.eclipse.jetty.webapp.*; |
287 |
|
288 |
import com.goldencode.ast.*; |
289 |
import com.goldencode.p2j.*; |
290 |
import com.goldencode.p2j.admin.*; |
291 |
import com.goldencode.p2j.admin.server.*; |
292 |
import com.goldencode.p2j.aspects.ltw.*; |
293 |
import com.goldencode.p2j.cfg.*; |
294 |
import com.goldencode.p2j.cfg.Configuration; |
295 |
import com.goldencode.p2j.classloader.*; |
296 |
import com.goldencode.p2j.directory.*; |
297 |
import com.goldencode.p2j.net.*; |
298 |
import com.goldencode.p2j.persist.*; |
299 |
import com.goldencode.p2j.persist.trigger.*; |
300 |
import com.goldencode.p2j.rest.*; |
301 |
import com.goldencode.p2j.scheduler.*; |
302 |
import com.goldencode.p2j.security.*; |
303 |
import com.goldencode.p2j.security.SecurityManager; |
304 |
import com.goldencode.p2j.soap.*; |
305 |
import com.goldencode.p2j.testengine.*; |
306 |
import com.goldencode.p2j.ui.*; |
307 |
import com.goldencode.p2j.util.*; |
308 |
import com.goldencode.p2j.util.logging.*; |
309 |
import com.goldencode.p2j.util.ErrorManager; |
310 |
|
311 |
/**
|
312 |
* Standard server application which is driven by the P2J directory contents.
|
313 |
* <p>
|
314 |
* In particular, exported entry points are read from directory objects
|
315 |
* /server/serverID/exports/groupID/methodID where methodID is a named string
|
316 |
* object. The name of this object is the well known exported method name.
|
317 |
* The "value" attribute is a string encoding fully qualified class name,
|
318 |
* method name and, possibly, the signature as in class.method(signature)
|
319 |
* <p>
|
320 |
* The directory also encodes the standard entry point of the P2J converted
|
321 |
* application which is to be invoked. This class provides the mechanism
|
322 |
* to look up this entry point and invoke it (see {@link #standardEntry} and
|
323 |
* {@link #invoke}).
|
324 |
*/
|
325 |
public class StandardServer |
326 |
{ |
327 |
/** Logger (this is JVM-wide rather than being context-local). */
|
328 |
private static final CentralLogger LOG = |
329 |
CentralLogger.get(StandardServer.class.getName()); |
330 |
|
331 |
/** File name extension for jar files */
|
332 |
private static final String JAR_EXT = ".jar"; |
333 |
|
334 |
/** Name of P2J jar */
|
335 |
private static final String P2J_JAR = "p2j.jar"; |
336 |
|
337 |
/** Context-local client-side parameters exposed to the server. */
|
338 |
private static final ContextLocal<ClientParameters> localParams = |
339 |
new ContextLocal<ClientParameters>()
|
340 |
{ |
341 |
protected boolean isResetAllowed() |
342 |
{ |
343 |
return false; |
344 |
} |
345 |
}; |
346 |
|
347 |
/** Array of paths to jars in the classpath which hold application resources */
|
348 |
private static List<String> resourceJarPaths; |
349 |
|
350 |
/** Lock object for shutdown synchronization. */
|
351 |
private static final Object shutdownLock = new Object(); |
352 |
|
353 |
/** Flag which is set to <code>true</code> when shutdown is done. */
|
354 |
private static boolean shutdownDone = false; |
355 |
|
356 |
/** Server hooks. */
|
357 |
private static final ArrayList<InitTermListener> serverHooks = new ArrayList<>(); |
358 |
|
359 |
/** Map that keeps instantiated classes by name. */
|
360 |
private final Map<String, Object> inst = new HashMap<>(); |
361 |
|
362 |
/** Signals when the non-network portions of the server are ready. */
|
363 |
private final CountDownLatch ready = new CountDownLatch(1); |
364 |
|
365 |
/** Admin extension. */
|
366 |
private AdminServerExtension adminServerExtension;
|
367 |
|
368 |
/**
|
369 |
* Set the client parameters.
|
370 |
*
|
371 |
* @param params
|
372 |
* A container with any client-related parameters.
|
373 |
*
|
374 |
*/
|
375 |
public static void setClientParams(ClientParameters params) |
376 |
{ |
377 |
localParams.set(params); |
378 |
} |
379 |
|
380 |
/**
|
381 |
* The method loads client startup parameters from the directory.
|
382 |
*
|
383 |
* @return Object holding the client startup parameters.
|
384 |
*/
|
385 |
public static StartupParameters loadStartupParameters() |
386 |
{ |
387 |
StartupParametersLoader loader = new StartupParametersLoader();
|
388 |
return loader.load(DirectoryService.getInstance());
|
389 |
} |
390 |
|
391 |
/**
|
392 |
* Set the project token to the server-side.
|
393 |
*
|
394 |
* @param token
|
395 |
* the token that identifies the project.
|
396 |
*
|
397 |
* @see Utils#setProjectToken(String)
|
398 |
*/
|
399 |
public static void setProjectToken(String token) |
400 |
{ |
401 |
// access the directory service
|
402 |
DirectoryService ds = DirectoryService.getInstance(); |
403 |
|
404 |
if (!ds.bind())
|
405 |
{ |
406 |
throw new RuntimeException("directory bind failed"); |
407 |
} |
408 |
|
409 |
// set the token before we do directory lookup
|
410 |
if (token == null || token.isEmpty()) |
411 |
{ |
412 |
// client's client:project:token overrides any directory setting.
|
413 |
token = Utils.getDirectoryNodeString(ds, "project_token", null); |
414 |
} |
415 |
|
416 |
if (token != null && !token.isEmpty()) |
417 |
{ |
418 |
Utils.setProjectToken(token); |
419 |
// some settings could have been already set at a previous moment when project_token was
|
420 |
// not available yet
|
421 |
EnvironmentOps.setSearchPath(Utils.getDirectoryNodeString(ds, "searchpath", "."), false); |
422 |
// TODO: update other settings
|
423 |
} |
424 |
|
425 |
ds.unbind(); |
426 |
} |
427 |
|
428 |
/**
|
429 |
* Serves as a standard transaction entry point to be called from the client which reroutes control to a
|
430 |
* specific entry point which is defined in the directory.
|
431 |
* <p>
|
432 |
* The entry point definition is the subject to a standardized directory lookup, which allows server or
|
433 |
* account specific definitions as well as server-wide or system-wide defaults.
|
434 |
* <p>
|
435 |
* p2j-entry can refer to a public method of the class. It can be either a static or an instance method
|
436 |
* taking no arguments. The class is instantiated before calling the named method. The returned object, if
|
437 |
* any, is discarded.
|
438 |
* <p>
|
439 |
* If the resolved class name is mapped to a legacy external program, and the method's name is
|
440 |
* <code>execute</code>, the {@link LegacyInvoker} will be used to execute the program. Otherwise, the
|
441 |
* {@link MainInvoker} will invoke the method, which will bypass the normal state processing for a legacy
|
442 |
* external program.
|
443 |
* <p>
|
444 |
* The {@link #invoke} method is the worker that actually handles the method's invocation using reflection.
|
445 |
* <p>
|
446 |
* If directory contains definition for context hook, then it is instantiated. Hook definition directory
|
447 |
* entry is searched using the same rules as application entry point (refer to
|
448 |
* {@link Utils#findDirectoryNodePath(DirectoryService, String, String, boolean)} for more details),
|
449 |
* appropriate variable has name <b>context-hook</b>.
|
450 |
* Context hook class must implement {@link InitTermListener} interface.
|
451 |
*
|
452 |
* @return <code>true</code> if re-logon should be performed.
|
453 |
*/
|
454 |
public static boolean standardEntry() |
455 |
{ |
456 |
SecurityManager sm = SecurityManager.getInstance(); |
457 |
String userId = sm.getUserId();
|
458 |
|
459 |
ClientParameters params = localParams.get(); |
460 |
boolean isServerSideFileSystem = OSResourceManager.getInstance().isServerSideFileSystem();
|
461 |
LegacyLogManagerConfigs legacyLogManagerConfigs = new LegacyLogManagerConfigs(params.clientLog,
|
462 |
params.loggingLevel, |
463 |
params.logEntryTypes, |
464 |
params.logThreshold, |
465 |
params.numLogFiles, |
466 |
params.pid, |
467 |
params.inp, |
468 |
params.osUserName, |
469 |
userId, |
470 |
isServerSideFileSystem); |
471 |
|
472 |
// this is an app server and needs its own management
|
473 |
if (AppServerManager.startAppServer(legacyLogManagerConfigs))
|
474 |
{ |
475 |
return false; |
476 |
} |
477 |
|
478 |
LegacyLogManager logManager = LegacyLogOps.logMgr(); |
479 |
logManager.initialize(legacyLogManagerConfigs); |
480 |
|
481 |
if (params.batchMode > 0) |
482 |
{ |
483 |
// run this connection in batch mode (as requested from client command-line)
|
484 |
EnvironmentOps.setBatchMode(true);
|
485 |
// LogicalTerminal.activateBatchMode(true); -- should already be set ?
|
486 |
} |
487 |
|
488 |
// access the directory service
|
489 |
DirectoryService ds = DirectoryService.getInstance(); |
490 |
|
491 |
if (!ds.bind())
|
492 |
{ |
493 |
throw new RuntimeException("directory bind failed"); |
494 |
} |
495 |
|
496 |
// get the entry point method definition and any configured session hooks; these values
|
497 |
// need no security check since the directory is an inherently trusted source; anyone that
|
498 |
// can control the directory can control everything about the server so there is no
|
499 |
// security check that can make this any better here
|
500 |
String entryPath = Utils.findDirectoryNodePath(ds, "p2j-entry", "string", true); |
501 |
String method = Utils.getDirectoryNodeString(ds, "p2j-entry", ""); |
502 |
String hookPath = Utils.findDirectoryNodePath(ds, "context-hook", "string", true); |
503 |
String hook = Utils.getDirectoryNodeString(ds, "context-hook", ""); |
504 |
|
505 |
// obtain the STOP disposition parameter:
|
506 |
// 0 - retry without logging off
|
507 |
// 1 - force logoff and then re-logon
|
508 |
// 2 - force logoff
|
509 |
int stopDisp = Utils.getDirectoryNodeInt(ds, "stop_disposition", 1); |
510 |
|
511 |
if (stopDisp < 0 || stopDisp > 2) |
512 |
{ |
513 |
LOG.warning("stop_disposition configuration item is rejected: " + stopDisp);
|
514 |
stopDisp = 1;
|
515 |
} |
516 |
|
517 |
// last use of the directory, end our session with it
|
518 |
ds.unbind(); |
519 |
|
520 |
DatabaseManager.autoConnect(); |
521 |
|
522 |
// these are used only for a 'com.foo.Bar.execute' p2j-entry case
|
523 |
String className = null; |
524 |
String methodName = null; |
525 |
|
526 |
Isolatable invoker = null;
|
527 |
if (params.startupProc != null && params.startupProc.length() > 0) |
528 |
{ |
529 |
// this is emulated via a RUN always, and that will take care to resolve any errors if the program is
|
530 |
// not found. just do the ACL check here.
|
531 |
EntryPointResource epr = (EntryPointResource) sm.getPluginInstance("entrypoint");
|
532 |
|
533 |
// security check is needed here because this data is user-supplied (which is an untrusted source)
|
534 |
if (epr == null || !epr.isAllowed(params.startupProc)) |
535 |
{ |
536 |
if (LOG.isLoggable(Level.WARNING)) |
537 |
{ |
538 |
if (epr == null) |
539 |
{ |
540 |
String spec1 = "User-specified startup program '%s' cannot be accessed " + |
541 |
"because the EntryPointResource is NOT enabled.";
|
542 |
|
543 |
LOG.logp(Level.WARNING,
|
544 |
"StandardServer.standardEntry",
|
545 |
"",
|
546 |
CentralLogger.generate(spec1, params.startupProc)); |
547 |
} |
548 |
else
|
549 |
{ |
550 |
String spec2 = "Access to user-specified startup program '%s' not " + |
551 |
"allowed for user %s. Falling back to default.";
|
552 |
|
553 |
LOG.logp(Level.WARNING,
|
554 |
"StandardServer.standardEntry",
|
555 |
"",
|
556 |
CentralLogger.generate(spec2, params.startupProc, sm.getUserId())); |
557 |
} |
558 |
} |
559 |
|
560 |
// disallow access
|
561 |
return false; |
562 |
} |
563 |
else
|
564 |
{ |
565 |
invoker = new LegacyInvoker(params.startupProc);
|
566 |
} |
567 |
} |
568 |
else
|
569 |
{ |
570 |
// check if there is an entry point to execute
|
571 |
if (method.length() == 0) |
572 |
{ |
573 |
throw new RuntimeException("no entry point definition found"); |
574 |
} |
575 |
|
576 |
// resolve the p2j-entry - either a 'com.foo.Bar.execute' Java method or a legacy program name.
|
577 |
|
578 |
// try to resolve the legacy program name first
|
579 |
String javaClass = SourceNameMapper.convertNameToClass(method);
|
580 |
if (javaClass != null) |
581 |
{ |
582 |
// the program name exists, use an emulated RUN statement
|
583 |
invoker = new LegacyInvoker(method);
|
584 |
|
585 |
// TODO: always use a LegacyInvoker
|
586 |
} |
587 |
else
|
588 |
{ |
589 |
// TODO: this code will become obsolete and needs to be removed.
|
590 |
|
591 |
// fallback to a 'com.foo.Bar.Execute' Java method
|
592 |
|
593 |
// parse the definition into class name and method name
|
594 |
int l = method.lastIndexOf("."); |
595 |
|
596 |
if (l == -1) |
597 |
{ |
598 |
throw new RuntimeException("incorrect syntax for method definition " + method + " for " + |
599 |
entryPath); |
600 |
} |
601 |
else
|
602 |
{ |
603 |
className = method.substring(0, l);
|
604 |
methodName = method.substring(l + 1);
|
605 |
} |
606 |
|
607 |
// inspect the class
|
608 |
Class<?> cl = null; |
609 |
try
|
610 |
{ |
611 |
cl = Class.forName(className);
|
612 |
} |
613 |
catch (Exception e) |
614 |
{ |
615 |
// something's wrong with the class
|
616 |
throw new RuntimeException("can't load " + className + " for " + entryPath, e); |
617 |
} |
618 |
|
619 |
// inspect the method
|
620 |
Method entry = null; |
621 |
try
|
622 |
{ |
623 |
entry = cl.getMethod(methodName, (Class[]) null); |
624 |
} |
625 |
catch (Exception ex) |
626 |
{ |
627 |
throw new RuntimeException( |
628 |
"no method " + methodName + " defined in " + className + " for " + entryPath, |
629 |
ex); |
630 |
} |
631 |
|
632 |
invoker = new MainInvoker(cl, entry, stopDisp);
|
633 |
} |
634 |
} |
635 |
|
636 |
// load hook, if any
|
637 |
InitTermListener listener = loadHook(hook, hookPath); |
638 |
|
639 |
// instantiate the class (if necessary) and invoke the entry point method
|
640 |
boolean mainResult = false; |
641 |
|
642 |
Throwable t = null; |
643 |
|
644 |
try
|
645 |
{ |
646 |
if (listener != null) |
647 |
{ |
648 |
listener.initialize(); |
649 |
} |
650 |
|
651 |
invoke(stopDisp, invoker); |
652 |
} |
653 |
catch (InstantiationException ex) |
654 |
{ |
655 |
t = ex; |
656 |
throw new RuntimeException("unable to instantiate " + className, ex); |
657 |
} |
658 |
catch (IllegalAccessException ex) |
659 |
{ |
660 |
t = ex; |
661 |
throw new RuntimeException("access problem in " + methodName, ex); |
662 |
} |
663 |
catch (IllegalArgumentException ex) |
664 |
{ |
665 |
t = ex; |
666 |
throw new RuntimeException("invalid argument to " + methodName, ex); |
667 |
} |
668 |
catch (ReflectiveOperationException ex)
|
669 |
{ |
670 |
t = ex; |
671 |
throw new RuntimeException("exception in " + methodName, ex); |
672 |
} |
673 |
catch (StopConditionException sce)
|
674 |
{ |
675 |
t = sce; |
676 |
|
677 |
if (stopDisp == 1) |
678 |
{ |
679 |
// this value forces logoff and then re-logon (continued operation)
|
680 |
mainResult = true;
|
681 |
} |
682 |
else
|
683 |
{ |
684 |
// this value forces logoff (exit)
|
685 |
mainResult = false;
|
686 |
} |
687 |
} |
688 |
catch (UnstoppableExitException uee)
|
689 |
{ |
690 |
// this value forces logoff (exit)
|
691 |
mainResult = false;
|
692 |
} |
693 |
catch (RuntimeException re) |
694 |
{ |
695 |
t = re; |
696 |
throw re;
|
697 |
} |
698 |
|
699 |
finally
|
700 |
{ |
701 |
if (listener != null) |
702 |
{ |
703 |
listener.terminate(t); |
704 |
} |
705 |
|
706 |
} |
707 |
|
708 |
return mainResult;
|
709 |
} |
710 |
|
711 |
/**
|
712 |
* Get the registered hook which is defined in the given jar, if any.
|
713 |
*
|
714 |
* @param jarName
|
715 |
* The jar file name.
|
716 |
*
|
717 |
* @return See above.
|
718 |
*/
|
719 |
public static String getHookForJar(String jarName) |
720 |
{ |
721 |
synchronized (shutdownLock)
|
722 |
{ |
723 |
for (InitTermListener listener : serverHooks)
|
724 |
{ |
725 |
if (MultiClassLoader.handledByJar(listener.getClass(), jarName))
|
726 |
{ |
727 |
return listener.getClass().getName();
|
728 |
} |
729 |
} |
730 |
} |
731 |
|
732 |
return null; |
733 |
} |
734 |
|
735 |
/**
|
736 |
* Terminate and remove the given server hook.
|
737 |
*
|
738 |
* @param hookClass
|
739 |
* The hook class name.
|
740 |
*/
|
741 |
public static void removeHook(String hookClass) |
742 |
{ |
743 |
synchronized (shutdownLock)
|
744 |
{ |
745 |
Iterator<InitTermListener> iter = serverHooks.iterator();
|
746 |
while (iter.hasNext())
|
747 |
{ |
748 |
InitTermListener listener = iter.next(); |
749 |
|
750 |
if (listener.getClass().getName().equals(hookClass))
|
751 |
{ |
752 |
listener.terminate(null);
|
753 |
iter.remove(); |
754 |
|
755 |
break;
|
756 |
} |
757 |
} |
758 |
} |
759 |
} |
760 |
|
761 |
/**
|
762 |
* Add server hook which will be notified about server startup and shutdown.
|
763 |
*
|
764 |
* @param hook
|
765 |
* Server hook to add.
|
766 |
*/
|
767 |
public static void register(InitTermListener hook) |
768 |
{ |
769 |
synchronized (shutdownLock)
|
770 |
{ |
771 |
serverHooks.add(hook); |
772 |
} |
773 |
} |
774 |
|
775 |
/**
|
776 |
* Load {@link InitTermListener} hook using specified class and path.
|
777 |
*
|
778 |
* @param hook
|
779 |
* Full class name.
|
780 |
* @param hookPath
|
781 |
* Hook path in directory.
|
782 |
*
|
783 |
* @return Instance of loaded hook if any.
|
784 |
*/
|
785 |
public static InitTermListener loadHook(String hook, String hookPath) |
786 |
{ |
787 |
InitTermListener listener = null;
|
788 |
|
789 |
if (hook != null && hook.length() > 0) |
790 |
{ |
791 |
Class<?> hookCl = null; |
792 |
|
793 |
try
|
794 |
{ |
795 |
// can not use Class.forName, as it caches the loaded class in
|
796 |
// some native code from which it can never be removed
|
797 |
hookCl = ClassLoader.getSystemClassLoader().loadClass(hook);
|
798 |
} |
799 |
catch (Exception e) |
800 |
{ |
801 |
// something's wrong with the hook class
|
802 |
throw new RuntimeException("can't load " + hook + " for " |
803 |
+ hookPath, |
804 |
e); |
805 |
} |
806 |
|
807 |
if (!InitTermListener.class.isAssignableFrom(hookCl))
|
808 |
// something's wrong with the hook class
|
809 |
throw new RuntimeException("class " + hook + " must implement " |
810 |
+ InitTermListener.class.getSimpleName() |
811 |
+ " interface.");
|
812 |
|
813 |
try
|
814 |
{ |
815 |
listener = (InitTermListener) hookCl.getDeclaredConstructor().newInstance(); |
816 |
} |
817 |
catch (InstantiationException e) |
818 |
{ |
819 |
throw new RuntimeException("unable to instantiate " + hook, e); |
820 |
} |
821 |
catch (InvocationTargetException e) |
822 |
{ |
823 |
throw new RuntimeException("wrapped exception in " + hook + " constructor", e.getTargetException()); |
824 |
} |
825 |
catch (NoSuchMethodException e) |
826 |
{ |
827 |
throw new RuntimeException("access problem in " + hook + " constructor", e); |
828 |
} |
829 |
catch (SecurityException e) |
830 |
{ |
831 |
throw new RuntimeException("security problem in " + hook + " constructor", e); |
832 |
} |
833 |
catch (IllegalAccessException e) |
834 |
{ |
835 |
throw new RuntimeException("access problem in " + hook + " constructor", e); |
836 |
} |
837 |
catch (IllegalArgumentException e) |
838 |
{ |
839 |
throw new RuntimeException("argument problem in " + hook + " constructor", e); |
840 |
} |
841 |
} |
842 |
|
843 |
return listener;
|
844 |
} |
845 |
|
846 |
/**
|
847 |
* Get the parameters exposed by the client-side exposed, for this context.
|
848 |
*
|
849 |
* @return A {@link ClientParameters} instance, with the context's parameters.
|
850 |
*/
|
851 |
public static ClientParameters getClientParameters() |
852 |
{ |
853 |
return localParams.get();
|
854 |
} |
855 |
|
856 |
/**
|
857 |
* Return the paths, relative to the server starting directory, of jars from which resources
|
858 |
* are to be loaded. This will be a subset of the server process' classpath.
|
859 |
*
|
860 |
* @return List of resource jar paths.
|
861 |
*/
|
862 |
public static List<String> getResourceJarPaths() |
863 |
{ |
864 |
return resourceJarPaths;
|
865 |
} |
866 |
|
867 |
/**
|
868 |
* Block the calling thread until the non-network portions of the server
|
869 |
* are fully initialized. At that point the server is ready to execute
|
870 |
* real logic.
|
871 |
*/
|
872 |
public void waitUntilReady() |
873 |
throws InterruptedException |
874 |
{ |
875 |
ready.await(); |
876 |
} |
877 |
|
878 |
/**
|
879 |
* Bootstrap the server environment by initializing network services,
|
880 |
* exporting all standard entry points for this server, loading all
|
881 |
* application specific startup classes, and starting the server socket
|
882 |
* listening loop.
|
883 |
* <p>
|
884 |
* The implementation looks up the directory under the path
|
885 |
* <code>/server/serverID/exports</code> to figure out the subtree of
|
886 |
* registrations.
|
887 |
* <p>
|
888 |
* If no such subtree exists, <code>/server/default/exports</code> is
|
889 |
* checked next.
|
890 |
* <p>
|
891 |
* Entries have the format <code>export-group-ID/export-method-ID</code>
|
892 |
* where the <code>export-group-ID</code> is a container naming a group and
|
893 |
* <code>export-method-ID</code> is an object naming the export and
|
894 |
* providing its implementation location through its string type "value"
|
895 |
* attribute as <code>class-name.method-name(signature)</code>.
|
896 |
* <p>
|
897 |
* <code>method-name</code> should refer to a public method of the class.
|
898 |
* It can be either a static or instance method. The class is instantiated
|
899 |
* once for the first instance method reference.
|
900 |
* <p>
|
901 |
* Specifying a method without signature is allowed if the method is not
|
902 |
* overloaded.
|
903 |
* <p>
|
904 |
* Once all the generic directory-driven exports are processed, then all
|
905 |
* the following are registered using the simpler <code>RemoteObject</code>
|
906 |
* mechanism:
|
907 |
* <p>
|
908 |
*
|
909 |
* <pre>
|
910 |
* Interface Type Target
|
911 |
* ----------------- ----------- ----------------------------------
|
912 |
* ServerExports static LogicalTerminal
|
913 |
* RemoteErrorData static ErrorManager
|
914 |
* MainEntry static StandardServer
|
915 |
* </pre>
|
916 |
* <p>
|
917 |
* When basic initialization is finished, this method registers default
|
918 |
* services and then loads user defined services if they are defined in
|
919 |
* <b>startups</b> directory entry under either
|
920 |
* <code>/server/serverID</code> or <code>/server/default</code> entry.
|
921 |
* <p>
|
922 |
* User defined services must implement {@link InitTermListener} interface
|
923 |
* and its {@link InitTermListener#initialize()} will be invoked right
|
924 |
* before server will start listen for incoming connections while its
|
925 |
* {@link InitTermListener#terminate(Throwable)} method will be invoked
|
926 |
* when server stopped accepting incoming connections and all active
|
927 |
* sessions are terminated. If server is stopped in graceful way (via
|
928 |
* remote request) then <code>null</code> is passed as a parameter into
|
929 |
* {@link InitTermListener#terminate(Throwable)} method. If server is
|
930 |
* stopped in abnormal way, for example, because user typed Ctrl-C, then
|
931 |
* some non-null value is passed as a parameter. Passed value should not be
|
932 |
* relied on to determine real cause of server stop.
|
933 |
*
|
934 |
* @param config
|
935 |
* Bootstrap configuration information for this server.
|
936 |
* @param diagnostics
|
937 |
* Details about the driver state that can help diagnose server startup issues.
|
938 |
* @param cfgProfile
|
939 |
* The optional configuration profile.
|
940 |
*
|
941 |
* @throws Exception
|
942 |
* Various exceptions generated by invalid configurations.
|
943 |
*/
|
944 |
public void bootstrap(BootstrapConfig config, Supplier<String> diagnostics, String cfgProfile) |
945 |
throws Exception |
946 |
{ |
947 |
// set a global property to for Antlr to use Class.forName
|
948 |
System.setProperty("ANTLR_USE_DIRECT_CLASS_LOADING", "true"); |
949 |
|
950 |
LOG.info(Version.getFWDVersion() + " Server starting initialization." );
|
951 |
LOG.info(diagnostics.get()); |
952 |
LOG.info("BootstrapConfig " + config.toString());
|
953 |
|
954 |
Configuration.setRuntimeConfig(true); |
955 |
|
956 |
// instantiate the directory service with must_exist option
|
957 |
config.setConfigItem("directory", "xml", "must_exist", "true"); |
958 |
DirectoryService ds = DirectoryService.createInstance(config); |
959 |
|
960 |
// bind to the directory
|
961 |
ds.bind(); |
962 |
|
963 |
// instantiate security manager
|
964 |
final SecurityManager sm = SecurityManager.createInstance(config); |
965 |
final SessionManager sessMgr = SessionManagerFactory.createRouterNode(config);
|
966 |
|
967 |
// initialize the class loaders for the jars set in the directory
|
968 |
initClassLoaders(ds); |
969 |
|
970 |
// enumerate exported groups
|
971 |
String path = Utils.findDirectoryNodePath(ds, "exports", "container", false); |
972 |
String[] groups = ds.enumerateNodes(path); |
973 |
|
974 |
if (groups == null || groups.length == 0) |
975 |
LOG.info("No exported entry points defined in the P2J directory");
|
976 |
|
977 |
// put instance references into the map for these special cases
|
978 |
Class<?> cl = ds.getClass();
|
979 |
inst.put(cl.getName(), ds); // reference to DirectoryService
|
980 |
cl = sm.getClass(); |
981 |
inst.put(cl.getName(), sm); // reference to SecurityManager
|
982 |
cl = this.getClass();
|
983 |
inst.put(cl.getName(), this); // reference to StandardServer |
984 |
|
985 |
if (groups != null) |
986 |
{ |
987 |
// process groups
|
988 |
for (String group : groups) |
989 |
{ |
990 |
// enumerate exports for this group
|
991 |
String methodPath = path + "/" + group; |
992 |
String[] methods = ds.enumerateNodes(methodPath); |
993 |
|
994 |
if (methods == null) |
995 |
continue;
|
996 |
|
997 |
// process exports
|
998 |
for (String s : methods) |
999 |
{ |
1000 |
// read the method specification
|
1001 |
String valuePath = methodPath + "/" + s; |
1002 |
String method = ds.getNodeString(valuePath, "value"); |
1003 |
|
1004 |
// register method
|
1005 |
if (exportMethod(group, s, method))
|
1006 |
LOG.finer(" exported " + group + ":" + s); |
1007 |
else
|
1008 |
LOG.finer("export failed " + group + ":" + s); |
1009 |
} |
1010 |
} |
1011 |
} |
1012 |
|
1013 |
// simple form (not directory based)
|
1014 |
Class<?>[] ifaces = new Class[] { ServerExports.class }; |
1015 |
RemoteObject.registerStaticNetworkServer(ifaces, LogicalTerminal.class);
|
1016 |
|
1017 |
ifaces = new Class[] { RemoteErrorData.class }; |
1018 |
RemoteObject.registerStaticNetworkServer(ifaces, ErrorManager.class); |
1019 |
|
1020 |
ifaces = new Class[] { MainEntry.class }; |
1021 |
RemoteObject.registerStaticNetworkServer(ifaces, StandardServer.class);
|
1022 |
|
1023 |
ifaces = new Class[] { AppServerEntry.class }; |
1024 |
RemoteObject.registerStaticNetworkServer(ifaces, AppServerManager.class);
|
1025 |
|
1026 |
ifaces = new Class[] { AppServer.class }; |
1027 |
RemoteObject.registerStaticNetworkServer(ifaces, AppServerLauncher.class);
|
1028 |
|
1029 |
ifaces = new Class[] { BatchProcess.class }; |
1030 |
RemoteObject.registerStaticNetworkServer(ifaces, ProcessClientSpawner.class);
|
1031 |
|
1032 |
ifaces = new Class[] { RemoteSpawner.class }; |
1033 |
RemoteObject.registerStaticNetworkServer(ifaces, ClientBuilder.class);
|
1034 |
|
1035 |
ifaces = new Class[] { BrokerServerServices.class }; |
1036 |
RemoteObject.registerStaticNetworkServer(ifaces, BrokerManager.class);
|
1037 |
|
1038 |
ifaces = new Class[] { ServerPropertiesInspector.class }; |
1039 |
RemoteObject.registerStaticNetworkServer(ifaces, ServerPropertiesDaemon.class);
|
1040 |
|
1041 |
ifaces = new Class[] { CentralLogService.class }; |
1042 |
RemoteObject.registerStaticNetworkServer(ifaces, CentralLogStaticService.class);
|
1043 |
|
1044 |
RemoteObject.registerStaticNetworkServer(new Class[] { UnitTestEngine.class }, UnitTestServer.class); |
1045 |
|
1046 |
AdminServerImpl.initialize(); |
1047 |
|
1048 |
DirectoryManager.initServer();
|
1049 |
|
1050 |
// set the optional configuration profile must be called after initialization of Configuration
|
1051 |
// and DirectoryManager
|
1052 |
if (cfgProfile != null) |
1053 |
{ |
1054 |
Configuration.setDefaultProfile(cfgProfile);
|
1055 |
} |
1056 |
|
1057 |
registerDefaultServices(sm, config); |
1058 |
|
1059 |
AstManager.initialize(() -> new InMemoryRegistryPlugin());
|
1060 |
|
1061 |
Throwable poolExc = null; |
1062 |
try
|
1063 |
{ |
1064 |
ConversionPool.initialize(); |
1065 |
} |
1066 |
catch (RuntimeException exc) |
1067 |
{ |
1068 |
poolExc = exc; |
1069 |
} |
1070 |
catch (ConfigurationException exc) |
1071 |
{ |
1072 |
poolExc = exc.getCause(); |
1073 |
} |
1074 |
finally
|
1075 |
{ |
1076 |
if (poolExc != null) |
1077 |
{ |
1078 |
// not all apps use runtime conversion services, so this is not a fatal error, but
|
1079 |
// log a warning
|
1080 |
if (LOG.isLoggable(Level.WARNING)) |
1081 |
{ |
1082 |
LOG.log(Level.WARNING,
|
1083 |
"Unable to initialize runtime conversion pool; if dynamic database " +
|
1084 |
"features are not in use, this warning can be ignored safely (otherwise " +
|
1085 |
"restart server with FINE logging for additional information)");
|
1086 |
if (LOG.isLoggable(Level.FINE)) |
1087 |
{ |
1088 |
LOG.log(Level.FINE,
|
1089 |
"Ensure configuration items for runtime conversion are stored in the " +
|
1090 |
"application jar file",
|
1091 |
poolExc); |
1092 |
} |
1093 |
} |
1094 |
} |
1095 |
} |
1096 |
|
1097 |
// set the interrupt handler in the session manager.
|
1098 |
sessMgr.setInterruptHandler(new InterruptedExceptionHandler());
|
1099 |
|
1100 |
// load the standard startup classes (must be after the dispatcher,
|
1101 |
// directory, security manager are all initialized)
|
1102 |
loadStartup(ds); |
1103 |
|
1104 |
// initialize the list of jar files from which to load resources
|
1105 |
initResourceJarPaths(ds); |
1106 |
|
1107 |
hookInitialize(); |
1108 |
|
1109 |
Runtime.getRuntime().addShutdownHook(new Thread(() -> { |
1110 |
Throwable t = new Throwable(); |
1111 |
|
1112 |
try
|
1113 |
{ |
1114 |
sm.setInitialSecurityContext(); |
1115 |
sessMgr.waitForShutdown(); |
1116 |
} |
1117 |
catch (Throwable e) |
1118 |
{ |
1119 |
t = e; |
1120 |
} |
1121 |
|
1122 |
hookTerminate(t); |
1123 |
})); |
1124 |
|
1125 |
long n1 = System.nanoTime(); |
1126 |
for (int i = 0; i < 1_000_000; i++) |
1127 |
{ |
1128 |
LOG.log(Level.WARNING, "something"); |
1129 |
// LOG2.log(Level.WARNING, "something");
|
1130 |
} |
1131 |
long n2 = System.nanoTime(); |
1132 |
System.out.println("centralLogger " + (n2 - n1) / 1_000_000L); |
1133 |
|
1134 |
Logger rootLogger = Logger.getLogger(""); |
1135 |
ConsoleHandler consoleHandler = new ConsoleHandler(); |
1136 |
consoleHandler.setFormatter(new CleanFormatter());
|
1137 |
rootLogger.addHandler(consoleHandler); |
1138 |
|
1139 |
Logger anotherLogger = Logger.getLogger("random"); |
1140 |
|
1141 |
n1 = System.nanoTime();
|
1142 |
for (int i = 0; i < 1_000_000; i++) |
1143 |
{ |
1144 |
anotherLogger.log(Level.WARNING, "something"); |
1145 |
} |
1146 |
n2 = System.nanoTime();
|
1147 |
System.out.println("java console logger " + (n2 - n1) / 1_000_000L); |
1148 |
|
1149 |
try
|
1150 |
{ |
1151 |
// wake up any waiting threads, the non-network portions of the
|
1152 |
// server are fully initialized now
|
1153 |
ready.countDown(); |
1154 |
|
1155 |
SessionListener webClientsManager; |
1156 |
if (Utils.getDirectoryNodeBoolean(ds, "webClient/enabled", false, false)) |
1157 |
{ |
1158 |
webClientsManager = WebClientsManager.getInstance(); |
1159 |
} |
1160 |
else
|
1161 |
{ |
1162 |
webClientsManager = null;
|
1163 |
} |
1164 |
|
1165 |
// main listening loop; service incoming socket connections
|
1166 |
sessMgr.listen(webClientsManager); |
1167 |
} |
1168 |
finally
|
1169 |
{ |
1170 |
sessMgr.waitForShutdown(); |
1171 |
hookTerminate(null);
|
1172 |
} |
1173 |
} |
1174 |
|
1175 |
|
1176 |
/**
|
1177 |
* Simple formatter to emit a very clean, condensed output.
|
1178 |
*/
|
1179 |
public static class CleanFormatter |
1180 |
extends Formatter |
1181 |
{ |
1182 |
/** Date format string. */
|
1183 |
private static final String FMT = "MM/dd/yyyy HH:mm:ss z"; |
1184 |
|
1185 |
/** Date and time formatter. */
|
1186 |
private static final ThreadLocal<SimpleDateFormat> sdf = new ThreadLocal<SimpleDateFormat>() |
1187 |
{ |
1188 |
@Override
|
1189 |
protected SimpleDateFormat initialValue() |
1190 |
{ |
1191 |
return new SimpleDateFormat(FMT); |
1192 |
} |
1193 |
}; |
1194 |
|
1195 |
/** Platform-specific line separator. */
|
1196 |
private static final String SEP = System.getProperty("line.separator"); |
1197 |
|
1198 |
/**
|
1199 |
* Format the given log record and return the formatted string.
|
1200 |
*
|
1201 |
* @param record
|
1202 |
* The log record to be formatted.
|
1203 |
*
|
1204 |
* @return The formatted log record.
|
1205 |
*/
|
1206 |
public String format(LogRecord record) |
1207 |
{ |
1208 |
String spec = "[%s] (%s:%s) %s%s"; |
1209 |
|
1210 |
Date dat = new Date(record.getMillis()); |
1211 |
|
1212 |
Object[] parms = new Object[] |
1213 |
{ |
1214 |
sdf.get().format(dat), |
1215 |
record.getSourceClassName(), |
1216 |
record.getLevel().toString(), |
1217 |
record.getMessage(), |
1218 |
SEP |
1219 |
}; |
1220 |
|
1221 |
String out = String.format(spec, parms); |
1222 |
|
1223 |
Throwable t = record.getThrown();
|
1224 |
|
1225 |
if (t != null) |
1226 |
{ |
1227 |
StringBuilder sb = new StringBuilder(out); |
1228 |
dumpThrowable(t, sb); |
1229 |
out = sb.toString(); |
1230 |
} |
1231 |
|
1232 |
return out;
|
1233 |
} |
1234 |
|
1235 |
/**
|
1236 |
* Dump all details of the throwable into the given string buffer.
|
1237 |
* Includes exception type, message, complete stack trace, and chained
|
1238 |
* throwables, if any.
|
1239 |
*
|
1240 |
* @param t
|
1241 |
* The throwable to dump.
|
1242 |
* @param buf
|
1243 |
* String buffer into which details are written.
|
1244 |
*/
|
1245 |
private void dumpThrowable(Throwable t, StringBuilder buf) |
1246 |
{ |
1247 |
Throwable cause = t;
|
1248 |
do
|
1249 |
{ |
1250 |
if (cause != t)
|
1251 |
{ |
1252 |
buf.append("Caused by: ");
|
1253 |
} |
1254 |
|
1255 |
buf.append(cause.getClass().getName()); |
1256 |
String msg = cause.getMessage();
|
1257 |
if (msg != null && msg.trim().length() > 0) |
1258 |
{ |
1259 |
buf.append(": ");
|
1260 |
buf.append(msg); |
1261 |
} |
1262 |
buf.append(SEP); |
1263 |
|
1264 |
StackTraceElement[] trace = cause.getStackTrace(); |
1265 |
|
1266 |
for (int i = 0; i < trace.length; i++) |
1267 |
{ |
1268 |
buf.append(" at ");
|
1269 |
buf.append(trace[i]); |
1270 |
buf.append(SEP); |
1271 |
} |
1272 |
|
1273 |
cause = cause.getCause(); |
1274 |
} |
1275 |
while (cause != null); |
1276 |
} |
1277 |
} |
1278 |
|
1279 |
/**
|
1280 |
* Initialize the class loaders for all the jars defined in the directory.
|
1281 |
* This can be done only if the <code>java.system.class.loader</code>
|
1282 |
* property is set to the {@link MultiClassLoader custom class loader}.
|
1283 |
*
|
1284 |
* @param ds
|
1285 |
* The directory service.
|
1286 |
*/
|
1287 |
private static void initClassLoaders(DirectoryService ds) |
1288 |
{ |
1289 |
// initialize the class loaders for the jars defined in the directory
|
1290 |
String path = Utils.findDirectoryNodePath(ds,
|
1291 |
"jars",
|
1292 |
"strings",
|
1293 |
false);
|
1294 |
if (path == null) |
1295 |
{ |
1296 |
return;
|
1297 |
} |
1298 |
|
1299 |
String[] jars = ds.getNodeStrings(path, "values"); |
1300 |
if (jars != null && jars.length > 0) |
1301 |
{ |
1302 |
ClassLoader scl = ClassLoader.getSystemClassLoader(); |
1303 |
if (scl instanceof MultiClassLoader) |
1304 |
{ |
1305 |
try
|
1306 |
{ |
1307 |
for (String jar : jars) |
1308 |
{ |
1309 |
if (MultiClassLoader.addJarLoader(jar) != ErrorCode.SUCCESS)
|
1310 |
{ |
1311 |
// we need to stop the server, as it could not initialize
|
1312 |
// properly
|
1313 |
throw new RuntimeException( |
1314 |
"Could not initialize class loader for jar " +
|
1315 |
jar + " !");
|
1316 |
} |
1317 |
} |
1318 |
} |
1319 |
catch (RestrictedUseException e)
|
1320 |
{ |
1321 |
throw new IllegalStateException("Call of " + |
1322 |
"MultiClassLoader.addJarLoader from this function should" +
|
1323 |
"be granted.");
|
1324 |
} |
1325 |
} |
1326 |
else
|
1327 |
{ |
1328 |
if (LOG.isLoggable(Level.SEVERE)) |
1329 |
{ |
1330 |
String msg = "Jars are defined in the directory, but the " + |
1331 |
"java.system.class.loader is not an instance of " +
|
1332 |
"com.goldencode.p2j.classloader.MultiClassLoader !";
|
1333 |
|
1334 |
LOG.logp(Level.SEVERE,
|
1335 |
"StandardServer.initClassLoaders",
|
1336 |
"",
|
1337 |
msg); |
1338 |
} |
1339 |
} |
1340 |
} |
1341 |
} |
1342 |
|
1343 |
/**
|
1344 |
* Register default services:
|
1345 |
* <ol>
|
1346 |
* <li> {@link Persistence} </li>
|
1347 |
* <li> {@link WebServer} </li>
|
1348 |
* <li> custom account extension plugin </li>
|
1349 |
* </ol>
|
1350 |
*
|
1351 |
* @param sm
|
1352 |
* The initialized instance of <code>SecurityManager</code>.
|
1353 |
* @param config
|
1354 |
* The bootstrap configuration information for this server.
|
1355 |
*/
|
1356 |
private void registerDefaultServices(final SecurityManager sm, |
1357 |
final BootstrapConfig config)
|
1358 |
{ |
1359 |
serverHooks.add(new AbstractInitTermListener()
|
1360 |
{ |
1361 |
@Override
|
1362 |
public void initialize() |
1363 |
{ |
1364 |
// initialize broker manager.
|
1365 |
// read broker configurations from directory.
|
1366 |
BrokerManager.initialize(); |
1367 |
} |
1368 |
|
1369 |
@Override
|
1370 |
public void terminate(Throwable t) |
1371 |
{ |
1372 |
// close brokers sessions
|
1373 |
BrokerManager.cleanup(); |
1374 |
} |
1375 |
}); |
1376 |
|
1377 |
serverHooks.add(new AbstractInitTermListener()
|
1378 |
{ |
1379 |
@Override
|
1380 |
public void initialize() |
1381 |
{ |
1382 |
// schedule the jobs to be launched; this will wait for the server to open its
|
1383 |
// secure and insecure sockets (depending on configuration)
|
1384 |
Scheduler.scheduleJobs(); |
1385 |
} |
1386 |
}); |
1387 |
|
1388 |
serverHooks.add(new AbstractInitTermListener()
|
1389 |
{ |
1390 |
@Override
|
1391 |
public void initialize() |
1392 |
{ |
1393 |
// after scheduler
|
1394 |
ProcedureManager.initialize(); |
1395 |
} |
1396 |
}); |
1397 |
|
1398 |
serverHooks.add(new AbstractInitTermListener()
|
1399 |
{ |
1400 |
@Override
|
1401 |
public void initialize() |
1402 |
{ |
1403 |
// client options must be loaded on a thread with the server context
|
1404 |
WebClientBuilderOptions.initialize(sm, config); |
1405 |
} |
1406 |
}); |
1407 |
|
1408 |
serverHooks.add(new AbstractInitTermListener()
|
1409 |
{ |
1410 |
@Override
|
1411 |
public void initialize() |
1412 |
{ |
1413 |
ServerKeyStore.initialize(); |
1414 |
} |
1415 |
}); |
1416 |
|
1417 |
// On this step we can initialize the server side of the ErrorWriter inside the
|
1418 |
// ErrorManager. It is important to do this exactly after the LogicalTerminal setup is
|
1419 |
// complete. Do not move this cal agead.
|
1420 |
ErrorManager.initErrorWriter(new ErrorWriterServer()); |
1421 |
|
1422 |
serverHooks.add(new AbstractInitTermListener()
|
1423 |
{ |
1424 |
@Override
|
1425 |
public void initialize() |
1426 |
{ |
1427 |
if (!Utils.getDirectoryNodeBoolean(null, "persistence/active", true, false)) |
1428 |
{ |
1429 |
LOG.warning("Server persistence is inactive. Running applications with " +
|
1430 |
"persistence dependency may fail.");
|
1431 |
return;
|
1432 |
} |
1433 |
|
1434 |
try
|
1435 |
{ |
1436 |
// initialize database statistics; must be called before persistence is
|
1437 |
// initialized, because DatabaseManager will register with DatabaseStatistics
|
1438 |
// when registering auto-connect databases
|
1439 |
DatabaseStatistics.get(); |
1440 |
|
1441 |
Persistence.initialize(); |
1442 |
} |
1443 |
catch (PersistenceException e)
|
1444 |
{ |
1445 |
throw new RuntimeException(e); |
1446 |
} |
1447 |
} |
1448 |
|
1449 |
@Override
|
1450 |
public void terminate(Throwable t) |
1451 |
{ |
1452 |
if (DatabaseStatistics.isEnabled())
|
1453 |
{ |
1454 |
DatabaseStatistics.get().collectStatistics(); |
1455 |
} |
1456 |
} |
1457 |
}); |
1458 |
|
1459 |
serverHooks.add(new AbstractInitTermListener()
|
1460 |
{ |
1461 |
@Override
|
1462 |
public void initialize() |
1463 |
{ |
1464 |
SourceNameMapper.initialize(); |
1465 |
} |
1466 |
}); |
1467 |
|
1468 |
// register controller for method execution tracing
|
1469 |
serverHooks.add(new AbstractInitTermListener()
|
1470 |
{ |
1471 |
@Override
|
1472 |
public void initialize() |
1473 |
{ |
1474 |
Class<?>[] ifaces = new Class[] { MethodTraceController.class }; |
1475 |
RemoteObject.registerStaticNetworkServer(ifaces, MethodTraceController.Impl.class);
|
1476 |
} |
1477 |
}); |
1478 |
|
1479 |
// don't start the web service unless we're in a secure environment
|
1480 |
boolean isSecure = config.getBoolean("net", "connection", "secure", false); |
1481 |
|
1482 |
if (isSecure)
|
1483 |
{ |
1484 |
serverHooks.add(new AbstractInitTermListener()
|
1485 |
{ |
1486 |
@Override
|
1487 |
public void initialize() |
1488 |
{ |
1489 |
// Initialize pool
|
1490 |
TemporaryAccountPool.getInstance(); |
1491 |
} |
1492 |
|
1493 |
@Override
|
1494 |
public void terminate(Throwable t) |
1495 |
{ |
1496 |
// Kill worker thread
|
1497 |
TemporaryAccountPool.shutDown(); |
1498 |
} |
1499 |
}); |
1500 |
|
1501 |
// FontTable initialization
|
1502 |
serverHooks.add(new AbstractInitTermListener()
|
1503 |
{ |
1504 |
@Override
|
1505 |
public void initialize() |
1506 |
{ |
1507 |
FontTable.initialize(); |
1508 |
} |
1509 |
}); |
1510 |
|
1511 |
serverHooks.add(new AbstractInitTermListener()
|
1512 |
{ |
1513 |
/** Flag to determine if the embedded mode app is active. */
|
1514 |
boolean embedded = false; |
1515 |
|
1516 |
/** Flag to determine if the REST services are active. */
|
1517 |
boolean rest = false; |
1518 |
|
1519 |
/** Flag to determine if the web handler is active. */
|
1520 |
boolean webHandler = false; |
1521 |
|
1522 |
boolean soap = false; |
1523 |
|
1524 |
@Override
|
1525 |
public void initialize() |
1526 |
{ |
1527 |
int num = 0; |
1528 |
boolean adm = false; |
1529 |
boolean webui = false; |
1530 |
|
1531 |
if (Utils.getDirectoryNodeBoolean(null, "adminEnabled", false, false)) |
1532 |
{ |
1533 |
// admin
|
1534 |
num++; |
1535 |
adm = true;
|
1536 |
} |
1537 |
|
1538 |
if (Utils.getDirectoryNodeBoolean(null, "webClient/enabled", false, false)) |
1539 |
{ |
1540 |
num++; |
1541 |
webui = true;
|
1542 |
} |
1543 |
|
1544 |
if (Utils.getDirectoryNodeBoolean(null, "embeddedWebApp/enabled", false, false)) |
1545 |
{ |
1546 |
num++; |
1547 |
embedded = true;
|
1548 |
} |
1549 |
|
1550 |
if (Utils.getDirectoryNodeBoolean(null, "rest/enabled", false, false)) |
1551 |
{ |
1552 |
num++; |
1553 |
rest = true;
|
1554 |
} |
1555 |
|
1556 |
if (Utils.getDirectoryNodeBoolean(null, "soap/enabled", false, false)) |
1557 |
{ |
1558 |
num++; |
1559 |
soap = true;
|
1560 |
} |
1561 |
|
1562 |
if (Utils.getDirectoryNodeBoolean(null, "webHandler/enabled", false, false)) |
1563 |
{ |
1564 |
num++; |
1565 |
webHandler = true;
|
1566 |
} |
1567 |
|
1568 |
ContextHandler[] webContentHandlers = null; |
1569 |
try
|
1570 |
{ |
1571 |
webContentHandlers = WebServer.getWebContentHandlers(); |
1572 |
num += webContentHandlers.length; |
1573 |
} |
1574 |
catch(Throwable ex) |
1575 |
{ |
1576 |
LOG.severe("", ex);
|
1577 |
} |
1578 |
|
1579 |
if (num == 0) |
1580 |
return;
|
1581 |
|
1582 |
final org.eclipse.jetty.server.Handler[] handlers = |
1583 |
new org.eclipse.jetty.server.Handler[num];
|
1584 |
|
1585 |
// admin
|
1586 |
if (adm)
|
1587 |
{ |
1588 |
Resource resourceBase = Resource.newClassPathResource("com/goldencode/p2j/admin");
|
1589 |
|
1590 |
String path = "/adminapp"; |
1591 |
Class servletExtClass = null; |
1592 |
String[] welcomeFile = new String[] {"AdminApp.html"}; |
1593 |
|
1594 |
if (adminServerExtension != null) |
1595 |
{ |
1596 |
path = adminServerExtension.modulePath; |
1597 |
if (adminServerExtension.adminClientResourcePath != null) |
1598 |
{ |
1599 |
Resource res = Resource.newClassPathResource(adminServerExtension.adminClientResourcePath); |
1600 |
resourceBase = new TwoURLsResource(res, resourceBase);
|
1601 |
} |
1602 |
|
1603 |
servletExtClass = adminServerExtension.adminExtensionServlet; |
1604 |
welcomeFile[0] = adminServerExtension.welcomeFile;
|
1605 |
} |
1606 |
|
1607 |
WebAppContext webApp = new WebAppContext("adminapp", "/admin"); |
1608 |
webApp.getSessionHandler().setMaxInactiveInterval(20);
|
1609 |
webApp.setBaseResource(resourceBase); |
1610 |
webApp.addServlet(AdminServiceImpl.class, path + "/AdminService");
|
1611 |
webApp.addServlet(AuthServiceImpl.class, path + "/AuthService");
|
1612 |
webApp.addServlet(ReportServlet.class, path + "/ReportService");
|
1613 |
|
1614 |
// prevent directory listing
|
1615 |
webApp.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed",
|
1616 |
"false");
|
1617 |
|
1618 |
// admin server extension
|
1619 |
if (servletExtClass != null) |
1620 |
{ |
1621 |
String pathSpec = path + adminServerExtension.servletPath;
|
1622 |
webApp.addServlet(servletExtClass, pathSpec); |
1623 |
|
1624 |
webApp.addFilter(SynchronizeFilter.class, pathSpec, |
1625 |
EnumSet.allOf(DispatcherType.class));
|
1626 |
webApp.addFilter(AuthFilter.class, pathSpec, |
1627 |
EnumSet.allOf(DispatcherType.class));
|
1628 |
} |
1629 |
|
1630 |
webApp.addFilter(SynchronizeFilter.class, |
1631 |
path + "/AdminService",
|
1632 |
EnumSet.allOf(DispatcherType.class));
|
1633 |
webApp.addFilter(SynchronizeFilter.class, |
1634 |
path + "/ReportService",
|
1635 |
EnumSet.allOf(DispatcherType.class));
|
1636 |
webApp.addFilter(AuthFilter.class, |
1637 |
path + "/AdminService",
|
1638 |
EnumSet.allOf(DispatcherType.class));
|
1639 |
webApp.addFilter(AuthFilter.class, |
1640 |
path + "/ReportService",
|
1641 |
EnumSet.allOf(DispatcherType.class));
|
1642 |
|
1643 |
webApp.setWelcomeFiles(welcomeFile); |
1644 |
webApp.setParentLoaderPriority(true);
|
1645 |
String adminTmp = Utils.getOrCreateTemporaryDirectory("fwdadmin" + new Random().nextLong()); |
1646 |
webApp.setTempDirectory(new File(adminTmp)); |
1647 |
|
1648 |
HandlerList list = new HandlerList();
|
1649 |
list.setHandlers(new org.eclipse.jetty.server.Handler[] { |
1650 |
webApp, |
1651 |
new DefaultHandler()
|
1652 |
}); |
1653 |
|
1654 |
handlers[--num] = list; |
1655 |
} |
1656 |
|
1657 |
if (webui)
|
1658 |
{ |
1659 |
handlers[--num] = new WebHandler();
|
1660 |
|
1661 |
// export the API for web client launching
|
1662 |
Class<?>[] ifaces = new Class[] { WebClientLauncher.class }; |
1663 |
RemoteObject.registerStaticNetworkServer(ifaces, WebHandler.class);
|
1664 |
} |
1665 |
|
1666 |
if (embedded)
|
1667 |
{ |
1668 |
handlers[--num] = EmbeddedWebAppHandler.initialize(); |
1669 |
} |
1670 |
|
1671 |
if (rest)
|
1672 |
{ |
1673 |
handlers[--num] = RestHandler.initialize(); |
1674 |
} |
1675 |
|
1676 |
if (soap)
|
1677 |
{ |
1678 |
handlers[--num] = SoapHandler.initialize(); |
1679 |
} |
1680 |
|
1681 |
if (webHandler)
|
1682 |
{ |
1683 |
handlers[--num] = WebServiceHandler.initialize(); |
1684 |
} |
1685 |
|
1686 |
if (rest || webHandler || soap)
|
1687 |
{ |
1688 |
// force loading of the services...
|
1689 |
SourceNameMapper.registerServices(); |
1690 |
|
1691 |
if (RestHandler.useWebServiceAuthentication() ||
|
1692 |
SoapHandler.useWebServiceAuthentication() || |
1693 |
WebServiceHandler.useWebServiceAuthentication()) |
1694 |
{ |
1695 |
sm.startWebServiceContextThreads(); |
1696 |
} |
1697 |
} |
1698 |
|
1699 |
if (webContentHandlers != null) |
1700 |
{ |
1701 |
System.arraycopy(webContentHandlers, 0, handlers, 0, webContentHandlers.length); |
1702 |
} |
1703 |
|
1704 |
WebServer.initialize(handlers); |
1705 |
|
1706 |
if (soap)
|
1707 |
{ |
1708 |
SoapHandler.initializePost(); |
1709 |
} |
1710 |
} |
1711 |
|
1712 |
@Override
|
1713 |
public void terminate(Throwable t) |
1714 |
{ |
1715 |
WebServer.terminate(); |
1716 |
|
1717 |
if (embedded)
|
1718 |
{ |
1719 |
EmbeddedWebAppHandler.terminate(); |
1720 |
} |
1721 |
|
1722 |
if (rest)
|
1723 |
{ |
1724 |
RestHandler.terminate(); |
1725 |
} |
1726 |
|
1727 |
if (soap)
|
1728 |
{ |
1729 |
SoapHandler.terminate(); |
1730 |
} |
1731 |
|
1732 |
if (webHandler)
|
1733 |
{ |
1734 |
WebServiceHandler.terminate(); |
1735 |
} |
1736 |
} |
1737 |
}); |
1738 |
} |
1739 |
|
1740 |
// load custom account extension server-side plugin
|
1741 |
String customExt = sm.getCustomServerExt();
|
1742 |
|
1743 |
if (customExt == null) |
1744 |
{ |
1745 |
return;
|
1746 |
} |
1747 |
|
1748 |
Class<?> cext = null; |
1749 |
|
1750 |
try
|
1751 |
{ |
1752 |
cext = Class.forName(customExt);
|
1753 |
} |
1754 |
|
1755 |
catch (ClassNotFoundException cnfe) |
1756 |
{ |
1757 |
LOG.severe("server extension class not found", cnfe);
|
1758 |
return;
|
1759 |
} |
1760 |
|
1761 |
final Method minit; |
1762 |
|
1763 |
try
|
1764 |
{ |
1765 |
minit = cext.getMethod("initialize", (Class[])null); |
1766 |
} |
1767 |
|
1768 |
catch (Exception e) |
1769 |
{ |
1770 |
LOG.severe("server extension class has no initialize() method", e);
|
1771 |
return;
|
1772 |
} |
1773 |
|
1774 |
serverHooks.add(new AbstractInitTermListener()
|
1775 |
{ |
1776 |
@Override
|
1777 |
public void initialize() |
1778 |
{ |
1779 |
try
|
1780 |
{ |
1781 |
minit.invoke(null, (Object[])null); |
1782 |
LOG.fine("server extension plugin initialized");
|
1783 |
} |
1784 |
catch (Exception e) |
1785 |
{ |
1786 |
LOG.severe("server extension class failed to initialize", e);
|
1787 |
return;
|
1788 |
} |
1789 |
} |
1790 |
}); |
1791 |
|
1792 |
try
|
1793 |
{ |
1794 |
Method m = cext.getMethod("getAdminExtension", (Class[])null); |
1795 |
adminServerExtension = (AdminServerExtension) m.invoke(null, (Object[]) null); |
1796 |
} |
1797 |
catch (NoSuchMethodException e) |
1798 |
{ |
1799 |
// the method is optional, so ignore
|
1800 |
} |
1801 |
catch (Exception e) |
1802 |
{ |
1803 |
LOG.severe("getServletClass call failed on server extension class", e);
|
1804 |
} |
1805 |
} |
1806 |
|
1807 |
/**
|
1808 |
* Initializes the <code>TransactionManager</code> environment and other
|
1809 |
* user-specific runtime initialization and then executes the given entry
|
1810 |
* point.
|
1811 |
*
|
1812 |
* @param stopDisp
|
1813 |
* STOP condition disposition.
|
1814 |
* <ul>
|
1815 |
* <li>0 - retry without logging off
|
1816 |
* <li>1 - force logoff and then re-logon
|
1817 |
* <li>2 - force logoff
|
1818 |
* </ul>
|
1819 |
*
|
1820 |
* @param entry
|
1821 |
* instance of the Isolatable interface that provides the entry
|
1822 |
* point to be executed
|
1823 |
*
|
1824 |
* @return The result of the invocation. If the signature of the method
|
1825 |
* specifies a <code>void</code> return then this will return
|
1826 |
* <code>null</code>. Note that it is a limitation of the
|
1827 |
* design of Java method signatures such that we do not
|
1828 |
* distinguish between a method with a <code>void</code> return
|
1829 |
* and a method that has a real return value which is set to
|
1830 |
* <code>null</code>.
|
1831 |
*
|
1832 |
* @throws NullPointerException
|
1833 |
* @throws IllegalAccessException
|
1834 |
* @throws IllegalArgumentException
|
1835 |
* @throws InvocationTargetException
|
1836 |
* @throws InstantiationException
|
1837 |
*/
|
1838 |
public static Object invoke(int stopDisp, Isolatable entry) |
1839 |
throws NullPointerException, |
1840 |
IllegalAccessException,
|
1841 |
InvocationTargetException,
|
1842 |
InstantiationException
|
1843 |
{ |
1844 |
// simple test for a quick out
|
1845 |
if (entry == null) |
1846 |
{ |
1847 |
throw new NullPointerException("Specified entry point is null!"); |
1848 |
} |
1849 |
|
1850 |
// force DatabaseTriggerManager context-local instantiation before the new scope is pushed
|
1851 |
DatabaseTriggerManager.get(); |
1852 |
|
1853 |
// add the topmost scope to the TransactionManager
|
1854 |
TransactionManager.pushScope("startup",
|
1855 |
TransactionManager.NO_TRANSACTION, |
1856 |
true,
|
1857 |
true,
|
1858 |
false,
|
1859 |
false);
|
1860 |
|
1861 |
// make sure that the global block gets a special notification - but only for non-appserver sessions.
|
1862 |
LogicalTerminal.registerCleaner(); |
1863 |
|
1864 |
Object result = null; |
1865 |
try
|
1866 |
{ |
1867 |
startup: |
1868 |
{ |
1869 |
TransactionManager.blockSetup(); |
1870 |
|
1871 |
do
|
1872 |
{ |
1873 |
try
|
1874 |
{ |
1875 |
// calling the entry point
|
1876 |
result = entry.execute(); |
1877 |
} |
1878 |
|
1879 |
catch (StopConditionException sce)
|
1880 |
{ |
1881 |
if (stopDisp != 0) |
1882 |
{ |
1883 |
if (stopDisp == 1) |
1884 |
LogicalTerminal.setRelogin(true);
|
1885 |
|
1886 |
throw sce;
|
1887 |
} |
1888 |
|
1889 |
TransactionManager.disableLoopProtection(null);
|
1890 |
TransactionManager.triggerRetry(null);
|
1891 |
} |
1892 |
|
1893 |
catch (ConditionException ce)
|
1894 |
{ |
1895 |
if (ce instanceof QuitConditionException) |
1896 |
{ |
1897 |
TransactionManager.setProcessingQuit(true);
|
1898 |
} |
1899 |
break startup;
|
1900 |
} |
1901 |
|
1902 |
catch (RetryUnwindException ru)
|
1903 |
{ |
1904 |
TransactionManager.honorRetry(ru); |
1905 |
} |
1906 |
|
1907 |
catch (Throwable th) |
1908 |
{ |
1909 |
Throwable chained = th;
|
1910 |
|
1911 |
boolean isError = false; |
1912 |
boolean isStop = false; |
1913 |
|
1914 |
// look through all causes to determine if there was an
|
1915 |
// underlying error
|
1916 |
while (chained != null) |
1917 |
{ |
1918 |
if (chained instanceof Error) |
1919 |
{ |
1920 |
isError = true;
|
1921 |
} |
1922 |
|
1923 |
if (chained instanceof StopConditionException) |
1924 |
{ |
1925 |
isStop = true;
|
1926 |
} |
1927 |
|
1928 |
if (chained instanceof SilentUnwindException) |
1929 |
{ |
1930 |
isStop = true;
|
1931 |
} |
1932 |
|
1933 |
if (chained instanceof UnstoppableExitException) |
1934 |
{ |
1935 |
throw new UnstoppableExitException(chained); |
1936 |
} |
1937 |
|
1938 |
chained = chained.getCause(); |
1939 |
} |
1940 |
|
1941 |
// stop conditions can be generated via many (sometimes
|
1942 |
// unexpected) paths and may be wrapped in other exception
|
1943 |
// types, in such a case we don't need to log it
|
1944 |
if (!isStop)
|
1945 |
{ |
1946 |
// log the fact that we have an abnormal end occurring
|
1947 |
LOG.logp(Level.SEVERE,
|
1948 |
"StandardServer",
|
1949 |
"invoke",
|
1950 |
"Abnormal end!",
|
1951 |
th); |
1952 |
} |
1953 |
|
1954 |
// treat Exception (but not Error) types the same as if
|
1955 |
// we got a STOP
|
1956 |
if (!isError)
|
1957 |
{ |
1958 |
if (stopDisp != 0) |
1959 |
{ |
1960 |
if (stopDisp == 1) |
1961 |
LogicalTerminal.setRelogin(true);
|
1962 |
|
1963 |
// in case of a SilentUnwindException, although we
|
1964 |
// throw a STOP here, the TM is still aware of the
|
1965 |
// abnormal connection end and will overwrite the STOP
|
1966 |
// with another SilentUnwindException - which is OK
|
1967 |
throw new StopConditionException(th); |
1968 |
} |
1969 |
|
1970 |
TransactionManager.disableLoopProtection(null);
|
1971 |
TransactionManager.triggerRetry(null);
|
1972 |
} |
1973 |
else
|
1974 |
{ |
1975 |
// the abend was an instance of Error - this is so
|
1976 |
// severe that we can't let the user continue
|
1977 |
|
1978 |
// set the flag to allow direct exit since we have such
|
1979 |
// a severe problem that we are going to exit to the
|
1980 |
// client BUT we MUST do this without trying to access
|
1981 |
// the client any further (via LogicalTerminal's
|
1982 |
// pauseBeforeEnd())
|
1983 |
TransactionManager.setProcessingQuit(true);
|
1984 |
|
1985 |
// rethrow to ensure that we exit
|
1986 |
throw new RuntimeException(th); |
1987 |
} |
1988 |
} |
1989 |
|
1990 |
} |
1991 |
while (TransactionManager.needsRetry());
|
1992 |
} |
1993 |
} |
1994 |
|
1995 |
// let any other exception pass through to the caller!
|
1996 |
|
1997 |
finally
|
1998 |
{ |
1999 |
// remove the topmost scope from the TransactionManager
|
2000 |
TransactionManager.popScope(); |
2001 |
} |
2002 |
|
2003 |
return result;
|
2004 |
} |
2005 |
|
2006 |
/**
|
2007 |
* Reads the portion of the directory where all preloaded application
|
2008 |
* classes are listed and instantiates them.
|
2009 |
*
|
2010 |
* @param ds
|
2011 |
* instance of <code>DirectoryService</code> to read from
|
2012 |
*
|
2013 |
* @throws ConfigurationException
|
2014 |
* when a class listed in the directory cannot be loaded
|
2015 |
*/
|
2016 |
private static void loadStartup(DirectoryService ds) |
2017 |
throws ConfigurationException |
2018 |
{ |
2019 |
String nodeId = Utils.findDirectoryNodePath(ds, "server-hooks", "strings", false); |
2020 |
|
2021 |
// check the node for existence and proper type
|
2022 |
if (nodeId == null) |
2023 |
{ |
2024 |
return;
|
2025 |
} |
2026 |
|
2027 |
// read all startup names
|
2028 |
String[] startups = ds.getNodeStrings(nodeId, "values"); |
2029 |
|
2030 |
if (startups == null || startups.length == 0) |
2031 |
{ |
2032 |
return;
|
2033 |
} |
2034 |
|
2035 |
// check that only a single hook is assigned per jar
|
2036 |
String[] loadedJars = MultiClassLoader.listRegisteredJars(); |
2037 |
Map<String, String> jarsToHooks = new HashMap<>(); |
2038 |
if (loadedJars.length > 0) |
2039 |
{ |
2040 |
for (String hookClass : startups) |
2041 |
{ |
2042 |
for (String jar : loadedJars) |
2043 |
{ |
2044 |
if (MultiClassLoader.handledByJar(hookClass, jar))
|
2045 |
{ |
2046 |
if (jarsToHooks.containsKey(jar))
|
2047 |
{ |
2048 |
throw new ConfigurationException("Cannot load hook " + |
2049 |
hookClass + " for jar " + jar + " because the hook " + |
2050 |
jarsToHooks.get(jar) + " is already assigned " +
|
2051 |
" for this jar!");
|
2052 |
} |
2053 |
|
2054 |
jarsToHooks.put(jar, hookClass); |
2055 |
break;
|
2056 |
} |
2057 |
} |
2058 |
} |
2059 |
} |
2060 |
|
2061 |
// initialize startups
|
2062 |
for (String hook : startups) |
2063 |
{ |
2064 |
InitTermListener listener = null;
|
2065 |
|
2066 |
try
|
2067 |
{ |
2068 |
listener = loadHook(hook, nodeId); |
2069 |
} |
2070 |
catch (RuntimeException rte) |
2071 |
{ |
2072 |
LOG.logp(Level.SEVERE,
|
2073 |
"StandardServer.loadStartup",
|
2074 |
"",
|
2075 |
"failed to load " + hook + " with " + rte.toString()); |
2076 |
|
2077 |
continue;
|
2078 |
} |
2079 |
|
2080 |
serverHooks.add(listener); |
2081 |
} |
2082 |
} |
2083 |
|
2084 |
/**
|
2085 |
* Initialize the list of jars from which to load application resources. This is read from
|
2086 |
* the directory. If nothing is listed in the directory, only the {@code p2j.jar} file will
|
2087 |
* be searched for resources. The {@code p2j.jar} file will be added to the end of this list,
|
2088 |
* if not explicitly listed in the directory.
|
2089 |
*
|
2090 |
* @param ds
|
2091 |
* Instance of {@code DirectoryService} from which to read configuration.
|
2092 |
*/
|
2093 |
private static void initResourceJarPaths(DirectoryService ds) |
2094 |
{ |
2095 |
String nodeId = Utils.findDirectoryNodePath(ds, "resource-jars", "strings", false); |
2096 |
Set<String> jarSet = new LinkedHashSet<>(); |
2097 |
|
2098 |
if (nodeId != null) |
2099 |
{ |
2100 |
String[] jars = ds.getNodeStrings(nodeId, "values"); |
2101 |
if (jars != null) |
2102 |
{ |
2103 |
for (String jar : jars) |
2104 |
{ |
2105 |
if (!jar.toLowerCase().endsWith(JAR_EXT))
|
2106 |
{ |
2107 |
jar = jar + JAR_EXT; |
2108 |
} |
2109 |
jarSet.add(jar); |
2110 |
} |
2111 |
} |
2112 |
} |
2113 |
|
2114 |
// p2j.jar contains resources; ensure it is always the last resource jar, unless specified
|
2115 |
// explicitly in the directory
|
2116 |
jarSet.add(P2J_JAR); |
2117 |
|
2118 |
List<String> resPaths = new ArrayList<>(); |
2119 |
String cp = System.getProperty("java.class.path"); |
2120 |
String[] paths = cp.split(File.pathSeparator); |
2121 |
|
2122 |
// loop through the unqualified resource jar names
|
2123 |
for (String jar : jarSet) |
2124 |
{ |
2125 |
// loop through the classpath parts to find the paths associated with the resource jars
|
2126 |
for (String path : paths) |
2127 |
{ |
2128 |
if (path.endsWith(jar))
|
2129 |
{ |
2130 |
resPaths.add(path); |
2131 |
|
2132 |
break;
|
2133 |
} |
2134 |
} |
2135 |
} |
2136 |
|
2137 |
resourceJarPaths = Collections.unmodifiableList(resPaths);
|
2138 |
} |
2139 |
|
2140 |
/**
|
2141 |
* Registers an entry point as an export.
|
2142 |
* <p>
|
2143 |
* The entry point method can be specified either by name or by name and
|
2144 |
* signature. The short form is acceptable when the method is unique
|
2145 |
* (not overloaded). Overloaded methods require signature specification to
|
2146 |
* be fully resolved.
|
2147 |
*
|
2148 |
* @param group
|
2149 |
* export group name
|
2150 |
* @param entry
|
2151 |
* export entry name
|
2152 |
* @param methodSpec
|
2153 |
* class and method specification as in class.method(signature)
|
2154 |
* where the (signature) is optional.
|
2155 |
*
|
2156 |
* @return <code>true</code> if succeeded
|
2157 |
*/
|
2158 |
private boolean exportMethod(String group, String entry, String methodSpec) |
2159 |
{ |
2160 |
SecurityManager sm = SecurityManager.getInstance(); |
2161 |
LOG.finer("registering " + group + ":" + entry + " as " + methodSpec); |
2162 |
|
2163 |
// parse the method specification
|
2164 |
String className = null; |
2165 |
String methodName = null; |
2166 |
String signature = null; |
2167 |
int signInd = methodSpec.indexOf('('); |
2168 |
|
2169 |
if (signInd == -1) |
2170 |
methodName = methodSpec; |
2171 |
else
|
2172 |
{ |
2173 |
methodName = methodSpec.substring(0, signInd);
|
2174 |
signature = methodSpec.substring(signInd); |
2175 |
} |
2176 |
|
2177 |
signInd = methodName.lastIndexOf('.');
|
2178 |
|
2179 |
if (signInd == -1) // fully qualified name always has dots |
2180 |
return false; |
2181 |
|
2182 |
className = methodName.substring(0, signInd);
|
2183 |
methodName = methodName.substring(signInd + 1);
|
2184 |
|
2185 |
// inspecting the class
|
2186 |
Class<?> appClass = null; |
2187 |
|
2188 |
try
|
2189 |
{ |
2190 |
// can not use Class.forName, as it caches the loaded class in
|
2191 |
// some native code from which it can never be removed
|
2192 |
appClass = ClassLoader.getSystemClassLoader().loadClass(className);
|
2193 |
} |
2194 |
catch (Exception e) |
2195 |
{ |
2196 |
return false; |
2197 |
} |
2198 |
|
2199 |
// looking up the method
|
2200 |
Method[] methods = appClass.getMethods(); |
2201 |
Method method = null; |
2202 |
int mods = 0; |
2203 |
int count = 0; |
2204 |
|
2205 |
for (int i = 0; i < methods.length; i ++) |
2206 |
{ |
2207 |
mods = methods[i].getModifiers(); |
2208 |
if (!Modifier.isPublic(mods)) |
2209 |
continue;
|
2210 |
if (methods[i].getName().equals(methodName))
|
2211 |
{ |
2212 |
count ++; |
2213 |
if (signature != null) |
2214 |
{ |
2215 |
if (getSignature(methods[i]).equals(signature))
|
2216 |
{ |
2217 |
method = methods[i]; |
2218 |
break;
|
2219 |
} |
2220 |
} |
2221 |
else
|
2222 |
method = methods[i]; |
2223 |
} |
2224 |
} |
2225 |
|
2226 |
if (method == null) // no such public method |
2227 |
return false; |
2228 |
if (signature == null && count > 1) // ambiguous method name |
2229 |
return false; |
2230 |
|
2231 |
// static or instance?
|
2232 |
mods = method.getModifiers(); |
2233 |
Object ref = null; |
2234 |
if (!Modifier.isStatic(mods)) |
2235 |
{ |
2236 |
// instance method. check if already instantiated the class
|
2237 |
if (!inst.containsKey(className))
|
2238 |
{ |
2239 |
try
|
2240 |
{ |
2241 |
ref = appClass.getDeclaredConstructor().newInstance(); |
2242 |
} |
2243 |
catch (Exception e) |
2244 |
{ |
2245 |
return false; |
2246 |
} |
2247 |
inst.put(className, ref); |
2248 |
} |
2249 |
else
|
2250 |
ref = inst.get(className); |
2251 |
} |
2252 |
|
2253 |
// finally, do the registration
|
2254 |
RoutingKey key = Dispatcher.generateRoutingKey(group, entry); |
2255 |
|
2256 |
if (key == null) |
2257 |
return false; |
2258 |
|
2259 |
try
|
2260 |
{ |
2261 |
Dispatcher.addMethod(key, ref, method, null);
|
2262 |
} |
2263 |
catch (Exception e) |
2264 |
{ |
2265 |
return false; |
2266 |
} |
2267 |
|
2268 |
return true; |
2269 |
} |
2270 |
|
2271 |
/**
|
2272 |
* Returns method's signature as text.
|
2273 |
*
|
2274 |
* @param method
|
2275 |
* <code>Method</code> to get the signature for.
|
2276 |
*
|
2277 |
* @return The method signature in parenthesis.
|
2278 |
*/
|
2279 |
private String getSignature(Method method) |
2280 |
{ |
2281 |
Class<?>[] pars = null; |
2282 |
StringBuilder sb = new StringBuilder(); |
2283 |
int i = 0; |
2284 |
|
2285 |
pars = method.getParameterTypes(); |
2286 |
sb.append("(");
|
2287 |
for (i = 0; i < pars.length; i ++) |
2288 |
{ |
2289 |
sb.append(pars[i].getName()); |
2290 |
sb.append(",");
|
2291 |
} |
2292 |
if (i > 0) |
2293 |
sb.setLength(sb.length() - 1);
|
2294 |
sb.append(")");
|
2295 |
|
2296 |
return new String(sb); |
2297 |
} |
2298 |
|
2299 |
/**
|
2300 |
* Notify server hook about application initialization.
|
2301 |
*
|
2302 |
* @throws ConfigurationException
|
2303 |
* If {@code initialize()} method any of {@code serverHooks} fails.
|
2304 |
*/
|
2305 |
private void hookInitialize() |
2306 |
throws ConfigurationException |
2307 |
{ |
2308 |
for (InitTermListener hook : serverHooks)
|
2309 |
{ |
2310 |
try
|
2311 |
{ |
2312 |
hook.initialize(); |
2313 |
} |
2314 |
catch (Throwable t) |
2315 |
{ |
2316 |
throw new ConfigurationException(" Initialization failure", t); |
2317 |
} |
2318 |
} |
2319 |
} |
2320 |
|
2321 |
/**
|
2322 |
* Notify server hook about application termination.
|
2323 |
*
|
2324 |
* @param t
|
2325 |
* Parameter which will be passed to
|
2326 |
* {@link InitTermListener#terminate(Throwable)}.
|
2327 |
*/
|
2328 |
private void hookTerminate(Throwable t) |
2329 |
{ |
2330 |
synchronized(shutdownLock)
|
2331 |
{ |
2332 |
if (!shutdownDone)
|
2333 |
{ |
2334 |
ListIterator<InitTermListener> iterator =
|
2335 |
serverHooks.listIterator(serverHooks.size()); |
2336 |
|
2337 |
while (iterator.hasPrevious())
|
2338 |
{ |
2339 |
try
|
2340 |
{ |
2341 |
iterator.previous().terminate(t); |
2342 |
} |
2343 |
catch (Throwable e) |
2344 |
{ |
2345 |
LOG.logp(Level.SEVERE,
|
2346 |
"StandardServer",
|
2347 |
"hookTerminate",
|
2348 |
"terminate() failed!",
|
2349 |
e); |
2350 |
} |
2351 |
} |
2352 |
|
2353 |
shutdownDone = true;
|
2354 |
} |
2355 |
} |
2356 |
} |
2357 |
|
2358 |
/**
|
2359 |
* Abstract adapter for a basic init/term listener.
|
2360 |
*/
|
2361 |
static abstract class AbstractInitTermListener |
2362 |
implements InitTermListener
|
2363 |
{ |
2364 |
/**
|
2365 |
* Called when the server initializes.
|
2366 |
*/
|
2367 |
@Override
|
2368 |
abstract public void initialize(); |
2369 |
|
2370 |
/**
|
2371 |
* Called when the server terminates. This routine does nothing.
|
2372 |
*
|
2373 |
* @param t
|
2374 |
* If not <code>null</code>, this describes the reason for the server's exit.
|
2375 |
*/
|
2376 |
@Override
|
2377 |
public void terminate(Throwable t) |
2378 |
{ |
2379 |
// do nothing
|
2380 |
} |
2381 |
} |
2382 |
|
2383 |
/**
|
2384 |
* Executes the exported entry point as a legacy external program.
|
2385 |
*/
|
2386 |
private static class LegacyInvoker |
2387 |
implements Isolatable
|
2388 |
{ |
2389 |
/** The external program name. */
|
2390 |
String programName;
|
2391 |
|
2392 |
/**
|
2393 |
* Initialize the invoker to execute the given program name.
|
2394 |
*/
|
2395 |
public LegacyInvoker(String programName) |
2396 |
{ |
2397 |
this.programName = programName;
|
2398 |
} |
2399 |
|
2400 |
/**
|
2401 |
* Execute the {@link #programName external program}, by emulating a RUN statement.
|
2402 |
*/
|
2403 |
@Override
|
2404 |
public Object execute() |
2405 |
throws ReflectiveOperationException
|
2406 |
{ |
2407 |
// reset the user's session-specific elapsed millis counter
|
2408 |
date.elapsed(true);
|
2409 |
|
2410 |
// password aging support
|
2411 |
SecurityManager sm = SecurityManager.getInstance(); |
2412 |
|
2413 |
if (sm.isPasswordAged())
|
2414 |
sm.changePassword(); |
2415 |
|
2416 |
InvokeConfig cfg = new InvokeConfig(programName);
|
2417 |
return ControlFlowOps.invoke(cfg);
|
2418 |
} |
2419 |
} |
2420 |
|
2421 |
/**
|
2422 |
* Prepares the exported entry point to be called in the properly
|
2423 |
* initialized transactionable environment.
|
2424 |
*/
|
2425 |
private static class MainInvoker |
2426 |
implements Isolatable
|
2427 |
{ |
2428 |
/** Class for the main entry point */
|
2429 |
Class<?> cl = null; |
2430 |
|
2431 |
/** Entry point method */
|
2432 |
Method entry = null; |
2433 |
|
2434 |
/** stop disposition value */
|
2435 |
@SuppressWarnings("unused") |
2436 |
int stopDisp = -1; |
2437 |
|
2438 |
/**
|
2439 |
* Constructor that simply remembers the parameters of the planned
|
2440 |
* entry point invocation.
|
2441 |
*
|
2442 |
* @param cls
|
2443 |
* The class where the called method is defined.
|
2444 |
* @param method
|
2445 |
* The method to call.
|
2446 |
* @param stop
|
2447 |
* The stop condition disposition value.
|
2448 |
*/
|
2449 |
private MainInvoker(Class<?> cls, Method method, int stop) |
2450 |
{ |
2451 |
cl = cls; |
2452 |
entry = method; |
2453 |
stopDisp = stop; |
2454 |
} |
2455 |
|
2456 |
/**
|
2457 |
* Specifies the method that is executed in a transaction environment,
|
2458 |
*
|
2459 |
* @return the result of the execution or <code>null</code> if void.
|
2460 |
*/
|
2461 |
public Object execute() |
2462 |
throws ReflectiveOperationException
|
2463 |
{ |
2464 |
// reset the user's session-specific elapsed millis counter
|
2465 |
date.elapsed(true);
|
2466 |
|
2467 |
// password aging support
|
2468 |
SecurityManager sm = SecurityManager.getInstance(); |
2469 |
|
2470 |
if (sm.isPasswordAged())
|
2471 |
sm.changePassword(); |
2472 |
|
2473 |
// calling the main entry
|
2474 |
return Utils.invoke(cl, entry, (Object[])null); |
2475 |
} |
2476 |
} |
2477 |
} |