001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.j2ee.appclient;
030:
031: import com.caucho.config.*;
032: import com.caucho.config.j2ee.InjectIntrospector;
033: import com.caucho.config.program.ConfigProgram;
034: import com.caucho.config.types.*;
035: import com.caucho.ejb.cfg.PostConstructConfig;
036: import com.caucho.ejb.cfg.PreDestroyConfig;
037: import com.caucho.el.*;
038: import com.caucho.j2ee.J2EEVersion;
039: import com.caucho.java.WorkDir;
040: import com.caucho.lifecycle.Lifecycle;
041: import com.caucho.loader.Environment;
042: import com.caucho.loader.EnvironmentBean;
043: import com.caucho.loader.EnvironmentClassLoader;
044: import com.caucho.loader.EnvironmentLocal; //import com.caucho.soa.client.WebServiceClient;
045: import com.caucho.server.e_app.EnterpriseApplication;
046: import com.caucho.server.util.CauchoSystem;
047: import com.caucho.util.L10N;
048: import com.caucho.vfs.JarPath;
049: import com.caucho.vfs.Path;
050: import com.caucho.vfs.Vfs;
051:
052: import org.w3c.dom.Element;
053: import org.w3c.dom.Node;
054:
055: import javax.el.*;
056: import javax.naming.Context;
057: import javax.naming.InitialContext;
058: import javax.security.auth.callback.Callback;
059: import javax.security.auth.callback.CallbackHandler;
060: import javax.security.auth.callback.NameCallback;
061: import javax.security.auth.callback.PasswordCallback;
062: import javax.security.auth.callback.UnsupportedCallbackException;
063: import java.io.InputStream;
064: import java.io.IOException;
065: import java.lang.reflect.Method;
066: import java.util.*;
067: import java.util.logging.Level;
068: import java.util.logging.Logger;
069:
070: public class AppClient implements EnvironmentBean {
071: private static L10N L = new L10N(AppClient.class);
072: private static Logger log = Logger.getLogger(AppClient.class
073: .getName());
074:
075: private static final EnvironmentLocal<AppClient> _local = new EnvironmentLocal<AppClient>();
076:
077: private final EnvironmentClassLoader _loader;
078:
079: private J2EEVersion _j2eeVersion = J2EEVersion.RESIN;
080:
081: private Path _home;
082:
083: private boolean _isMetadataComplete;
084: private Path _rootDirectory;
085: private Path _workDirectory;
086: private String _mainClassName;
087: private Path _clientJar;
088: private Path _earFile;
089:
090: private ArrayList<Path> _configList = new ArrayList<Path>();
091:
092: private Lifecycle _lifecycle = new Lifecycle(log);
093: private Method _mainMethod;
094: private String[] _mainArgs = new String[] {};
095:
096: private Hashtable _ejbEnv = new Hashtable();
097: private Context _ejbContext;
098:
099: private PreDestroyConfig _preDestroyConfig;
100: private PostConstructConfig _postConstructConfig;
101:
102: ArrayList<EjbRef> _ejbRefList = new ArrayList<EjbRef>();
103:
104: private AppClient() {
105: _loader = new EnvironmentClassLoader();
106: _local.set(this , _loader);
107:
108: _home = CauchoSystem.getResinHome();
109: }
110:
111: public ClassLoader getClassLoader() {
112: return _loader;
113: }
114:
115: public static AppClient getLocal() {
116: return _local.get();
117: }
118:
119: public PostConstructConfig getPostConstruct() {
120: return _postConstructConfig;
121: }
122:
123: public PreDestroyConfig getPreDestroy() {
124: return _preDestroyConfig;
125: }
126:
127: /**
128: * Used to distinguish the version of the configuration file.
129: */
130: public void setConfigNode(Node node) {
131: _j2eeVersion = J2EEVersion.getJ2EEVersion((Element) node);
132: }
133:
134: public J2EEVersion getJ2EEVersion() {
135: return _j2eeVersion;
136: }
137:
138: public void setMetadataComplete(boolean isComplete) {
139: _isMetadataComplete = isComplete;
140: }
141:
142: public void setRootDirectory(Path rootDirectory) {
143: _rootDirectory = rootDirectory;
144: Vfs.setPwd(_rootDirectory);
145: }
146:
147: public void setWorkDirectory(Path workDirectory) {
148: _workDirectory = workDirectory;
149: }
150:
151: public void setId(String id) {
152: }
153:
154: public void setDescription(String value) {
155: }
156:
157: public void setIcon(com.caucho.config.types.Icon icon) {
158: }
159:
160: /**
161: * Adds a web service client.
162: */
163: /*
164: public WebServiceClient createWebServiceClient()
165: {
166: return new WebServiceClient();
167: }
168: */
169:
170: private void addConfig(Path path) throws Exception {
171: _configList.add(path);
172: }
173:
174: public void setClientJar(Path clientJar) {
175: _clientJar = clientJar;
176: }
177:
178: public void setEarFile(Path earFile) {
179: _earFile = earFile;
180: }
181:
182: public void setMainClass(String mainClassName) {
183: _mainClassName = mainClassName;
184: }
185:
186: public void setMainArgs(String[] mainArgs) {
187: _mainArgs = mainArgs;
188: }
189:
190: public void setPostConstruct(PostConstructConfig postConstruct) {
191: _postConstructConfig = postConstruct;
192: }
193:
194: public void setPreDestroy(PreDestroyConfig preDestroy) {
195: _preDestroyConfig = preDestroy;
196: }
197:
198: public void setSchemaLocation(String schemaLocation) {
199: // not needed
200: }
201:
202: public void setVersion(String version) {
203: // not needed
204: }
205:
206: public void setDisplayName(String displayName) {
207: // not needed
208: }
209:
210: public void setCallbackHandler(
211: Class<CallbackHandler> callbackHandler) throws Exception {
212: CallbackManager callback = new CallbackManager();
213:
214: CallbackHandler handler = callbackHandler.newInstance();
215:
216: callback.handle(handler);
217:
218: System.setProperty(Context.SECURITY_PRINCIPAL, callback
219: .getName());
220: System.setProperty(Context.SECURITY_CREDENTIALS, callback
221: .getPassword());
222: }
223:
224: public EjbRef createEjbRef() {
225: EjbRef ejbRef = new EjbRef(_ejbContext);
226:
227: _ejbRefList.add(ejbRef);
228:
229: ejbRef.setClientClassName(_mainClassName);
230:
231: return ejbRef;
232: }
233:
234: public void init() throws Exception {
235: if (!_lifecycle.toInitializing())
236: return;
237:
238: if (_clientJar == null)
239: throw new ConfigException(L.l("'client-jar' is required"));
240:
241: // corba needs for RMI(?)
242:
243: //EnvironmentClassLoader.initializeEnvironment();
244: //System.setSecurityManager(new SecurityManager());
245:
246: if (_rootDirectory == null) {
247: /*
248: String name = _clientJar.getTail();
249:
250: int lastDot = name.lastIndexOf(".");
251:
252: if (lastDot > -1)
253: name = name.substring(0, lastDot);
254:
255: Path root = WorkDir.getLocalWorkDir(_loader).lookup("_appclient").lookup("_" + name);
256:
257: _rootDirectory = root;
258: */
259:
260: setRootDirectory(_clientJar.getParent());
261: }
262:
263: if (_workDirectory == null)
264: _workDirectory = _rootDirectory.lookup("META-INF/work");
265:
266: WorkDir.setLocalWorkDir(_workDirectory, _loader);
267:
268: _loader.setId(toString());
269: _loader.addJar(_clientJar);
270:
271: /*
272: // ejb/0fa2
273: if (_earFile != null) {
274: Path deployDir = _earFile.getParent();
275:
276: String s = _earFile.getTail();
277:
278: s = "_ear_" + s.substring(0, s.length() - 4);
279:
280: s = deployDir + "/" + s + "/";
281:
282: String libDir = "lib";
283:
284: Path applicationXml = Vfs.lookup(s + "META-INF/application.xml");
285:
286: EnterpriseApplication eapp = new EnterpriseApplication();
287:
288: InputStream is = applicationXml.openRead();
289:
290: new Config().configure(eapp, is,
291: "com/caucho/server/e_app/ear.rnc");
292:
293: // ejb/0fa0
294: libDir = eapp.getLibraryDirectory();
295:
296: Path lib = Vfs.lookup(s + libDir);
297:
298: for (String file : lib.list()) {
299: if (file.endsWith(".jar")) {
300: Path sharedLib = lib.lookup(file);
301:
302: _loader.addJar(sharedLib);
303: }
304: }
305: }
306: */
307:
308: Thread thread = Thread.currentThread();
309: ClassLoader oldLoader = thread.getContextClassLoader();
310:
311: try {
312: thread.setContextClassLoader(_loader);
313:
314: if (log.isLoggable(Level.FINE))
315: log.log(Level.FINE, L.l("root-directory is {0}",
316: _rootDirectory));
317:
318: if (log.isLoggable(Level.FINER))
319: log.log(Level.FINER, L.l("work-directory is {0}",
320: WorkDir.getLocalWorkDir()));
321:
322: _ejbContext = new InitialContext(_ejbEnv);
323:
324: JarPath jarPath = JarPath.create(_clientJar);
325:
326: configureFrom(jarPath
327: .lookup("META-INF/application-client.xml"), true);
328:
329: configureFrom(jarPath
330: .lookup("META-INF/resin-application-client.xml"),
331: true);
332:
333: for (Path configPath : _configList) {
334: configureFrom(configPath, true);
335: }
336:
337: // Merge duplicated <ejb-ref>'s
338: mergeEjbRefs();
339:
340: // jpa/0s37
341: Environment
342: .addChildLoaderListener(new com.caucho.amber.manager.PersistenceEnvironmentListener());
343:
344: if (_mainClassName == null)
345: throw new ConfigException(L
346: .l("'main-class' is required"));
347:
348: Class<?> mainClass = Class.forName(_mainClassName, false,
349: _loader);
350:
351: ArrayList<ConfigProgram> injectList = new ArrayList<ConfigProgram>();
352: // XXX: static
353: InjectIntrospector.introspectInject(injectList, mainClass);
354:
355: for (ConfigProgram inject : injectList) {
356: inject.inject((Object) null, null);
357: }
358:
359: _mainMethod = mainClass.getMethod("main", String[].class);
360:
361: _lifecycle.setName(toString());
362: _lifecycle.toInit();
363: } finally {
364: thread.setContextClassLoader(oldLoader);
365: }
366: }
367:
368: private void configureFrom(Path xml, boolean optional)
369: throws Exception {
370: if (xml.canRead()) {
371: if (log.isLoggable(Level.FINE))
372: log.log(Level.FINE, L.l(
373: "reading configuration file {0}", xml));
374:
375: HashMap<String, Object> variableMap = new HashMap<String, Object>();
376: variableMap.put("resin", new ResinVar());
377:
378: ELResolver varResolver = new MapVariableResolver(
379: variableMap);
380: ConfigELContext elContext = new ConfigELContext(varResolver);
381:
382: EL.setEnvironment(elContext, _loader);
383: EL.setVariableMap(variableMap, _loader);
384:
385: Config config = new Config();
386:
387: config.configureBean(this , xml,
388: "com/caucho/server/e_app/app-client.rnc");
389: } else {
390: if (!optional)
391: throw new ConfigException(L.l(
392: "missing required configuration file {0}", xml));
393:
394: if (log.isLoggable(Level.FINEST))
395: log.log(Level.FINEST, L.l("no configuration file {0}",
396: xml));
397: }
398: }
399:
400: private void mergeEjbRefs() throws Exception {
401: for (int i = 0; i < _ejbRefList.size(); i++) {
402: EjbRef ref = _ejbRefList.get(i);
403: String refName = ref.getEjbRefName();
404:
405: for (int j = i + 1; j < _ejbRefList.size(); j++) {
406: EjbRef other = _ejbRefList.get(j);
407:
408: if (refName.equals(other.getEjbRefName())) {
409: ref.mergeFrom(other);
410: _ejbRefList.remove(j);
411: j--;
412: }
413: }
414:
415: // After we merge all the information, the <ejb-ref> can be initialized.
416: ref.bind();
417: }
418: }
419:
420: public void run() throws Exception {
421: init();
422:
423: Thread thread = Thread.currentThread();
424: ClassLoader oldLoader = thread.getContextClassLoader();
425:
426: try {
427: thread.setContextClassLoader(_loader);
428:
429: _mainMethod.invoke(null, new Object[] { _mainArgs });
430: } finally {
431: thread.setContextClassLoader(oldLoader);
432: }
433: }
434:
435: public String toString() {
436: return "AppClient[" + _clientJar + "," + _mainClassName + "]";
437: }
438:
439: public static void main(String[] args) throws Throwable {
440: String clientJar = null;
441: String earFile = null;
442: String main = null;
443: String conf = null;
444: String workDir = null;
445: String[] mainArgs = null;
446:
447: EnvironmentClassLoader.initializeEnvironment();
448:
449: for (int i = 0; i < args.length; i++) {
450: String arg = args[i];
451:
452: if (arg.startsWith("-")) {
453: String option = arg
454: .substring((arg.startsWith("--")) ? 2 : 1);
455:
456: if (option.equals("conf")) {
457: conf = args[++i];
458: continue;
459: } else if (option.equals("client-jar")) {
460: clientJar = args[++i];
461: continue;
462: } else if (option.equals("ear-file")) {
463: earFile = args[++i];
464: continue;
465: } else if (option.equals("work-dir")) {
466: workDir = args[++i];
467: continue;
468: } else if (option.equals("main")) {
469: main = args[++i];
470:
471: mainArgs = new String[args.length - i - 1];
472: System.arraycopy(args, i + 1, mainArgs, 0,
473: mainArgs.length);
474: break;
475: }
476: }
477:
478: throw new ConfigException(L.l("unknown arg '{0}'", args[i]));
479: }
480:
481: AppClient appClient = new AppClient();
482:
483: if (workDir != null)
484: appClient.setWorkDirectory(Vfs.lookup(workDir));
485:
486: if (clientJar != null)
487: appClient.setClientJar(Vfs.lookup(clientJar));
488:
489: if (earFile != null)
490: appClient.setEarFile(Vfs.lookup(earFile));
491:
492: if (conf != null)
493: appClient.addConfig(Vfs.lookup(conf));
494:
495: if (main != null)
496: appClient.setMainClass(main);
497:
498: if (mainArgs != null)
499: appClient.setMainArgs(mainArgs);
500:
501: appClient.run();
502: }
503:
504: public class CallbackManager {
505: private final NameCallback _nameCallback;
506: private final PasswordCallback _passwordCallback;
507:
508: public CallbackManager() {
509: _nameCallback = new NameCallback(L.l("Name"));
510: _passwordCallback = new PasswordCallback(L.l("Password"),
511: false);
512:
513: }
514:
515: public void handle(CallbackHandler handler) throws IOException,
516: UnsupportedCallbackException {
517: Callback[] callbacks = new Callback[] { _nameCallback,
518: _passwordCallback };
519:
520: handler.handle(callbacks);
521: }
522:
523: public String getName() {
524: return _nameCallback.getName();
525: }
526:
527: public String getPassword() {
528: return new String(_passwordCallback.getPassword());
529: }
530: }
531:
532: public class ResinVar {
533: public Path getHome() {
534: System.out.println("GET_HOME: " + _home);
535: return _home;
536: }
537:
538: public Path getRoot() {
539: return _rootDirectory;
540: }
541: }
542: }
|