001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.geronimo.client;
017:
018: import java.lang.reflect.InvocationTargetException;
019: import java.lang.reflect.Method;
020: import java.security.PrivilegedAction;
021: import java.util.ArrayList;
022: import java.util.List;
023:
024: import javax.naming.Context;
025: import javax.naming.NamingException;
026: import javax.security.auth.Subject;
027: import javax.security.auth.callback.CallbackHandler;
028: import javax.security.auth.login.LoginContext;
029: import javax.security.auth.login.LoginException;
030:
031: import org.apache.geronimo.gbean.AbstractName;
032: import org.apache.geronimo.gbean.GBeanInfo;
033: import org.apache.geronimo.gbean.GBeanInfoBuilder;
034: import org.apache.geronimo.gbean.GBeanLifecycle;
035: import org.apache.geronimo.j2ee.annotation.Holder;
036: import org.apache.geronimo.j2ee.annotation.Injection;
037: import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
038: import org.apache.geronimo.kernel.Kernel;
039: import org.apache.geronimo.security.Callers;
040: import org.apache.geronimo.security.ContextManager;
041: import org.apache.geronimo.security.credentialstore.CredentialStore;
042: import org.apache.geronimo.security.deploy.SubjectInfo;
043: import org.apache.xbean.recipe.ObjectRecipe;
044: import org.apache.xbean.recipe.Option;
045: import org.apache.xbean.recipe.StaticRecipe;
046:
047: /**
048: * @version $Rev: 565657 $ $Date: 2007-08-14 01:24:58 -0700 (Tue, 14 Aug 2007) $
049: */
050: public final class AppClientContainer implements GBeanLifecycle {
051: private static final Class[] MAIN_ARGS = { String[].class };
052:
053: private LoginContext loginContext;
054:
055: private final String mainClassName;
056: private final AppClientPlugin jndiContext;
057: private final AbstractName appClientModuleName;
058: private final String realmName;
059: private final String callbackHandlerClass;
060: private final Subject defaultSubject;
061: private final Method mainMethod;
062: private final ClassLoader classLoader;
063: private final Kernel kernel;
064: private final Holder holder;
065: private CallbackHandler callbackHandler;
066:
067: public AppClientContainer(String mainClassName,
068: AbstractName appClientModuleName, String realmName,
069: String callbackHandlerClassName,
070: SubjectInfo defaultSubject, Holder holder,
071: AppClientPlugin jndiContext,
072: CredentialStore credentialStore, ClassLoader classLoader,
073: Kernel kernel) throws Exception {
074: // set the geronimo identity resolver hook for openejb
075: System.setProperty("openejb.client.identityResolver",
076: "geronimo");
077:
078: this .mainClassName = mainClassName;
079: this .appClientModuleName = appClientModuleName;
080: if ((realmName == null) != (callbackHandlerClassName == null)) {
081: throw new IllegalArgumentException(
082: "You must supply both realmName and callbackHandlerClass or neither");
083: }
084: this .realmName = realmName;
085: this .callbackHandlerClass = callbackHandlerClassName;
086:
087: if (defaultSubject != null) {
088: this .defaultSubject = credentialStore.getSubject(
089: defaultSubject.getRealm(), defaultSubject.getId());
090: } else {
091: this .defaultSubject = null;
092: }
093: this .holder = holder == null ? Holder.EMPTY : holder;
094: this .classLoader = classLoader;
095: this .kernel = kernel;
096: this .jndiContext = jndiContext;
097:
098: try {
099: Class mainClass = classLoader.loadClass(mainClassName);
100: mainMethod = mainClass.getMethod("main", MAIN_ARGS);
101: } catch (ClassNotFoundException e) {
102: throw new AppClientInitializationException(
103: "Unable to load Main-Class " + mainClassName, e);
104: } catch (NoSuchMethodException e) {
105: throw new AppClientInitializationException("Main-Class "
106: + mainClassName + " does not have a main method", e);
107: }
108: }
109:
110: public AbstractName getAppClientModuleName() {
111: return appClientModuleName;
112: }
113:
114: public String getMainClassName() {
115: return mainClassName;
116: }
117:
118: public void main(final String[] args) throws Exception {
119: //TODO reorganize this so it makes more sense. maybe use an interceptor stack.
120: //TODO track resource ref shared and app managed security
121: Thread thread = Thread.currentThread();
122:
123: ClassLoader oldClassLoader = thread.getContextClassLoader();
124: Callers oldCallers = ContextManager.getCallers();
125: Subject clientSubject = defaultSubject;
126: try {
127: thread.setContextClassLoader(classLoader);
128: jndiContext.startClient(appClientModuleName, kernel,
129: classLoader);
130: Context componentContext = jndiContext.getJndiContext();
131:
132: if (callbackHandlerClass != null) {
133: callbackHandler = (CallbackHandler) holder.newInstance(
134: callbackHandlerClass, classLoader,
135: componentContext);
136: loginContext = ContextManager.login(realmName,
137: callbackHandler);
138: clientSubject = loginContext.getSubject();
139: }
140: ContextManager.setCallers(clientSubject, clientSubject);
141: ObjectRecipe objectRecipe = new ObjectRecipe(mainClassName);
142: objectRecipe.allow(Option.FIELD_INJECTION);
143: objectRecipe.allow(Option.PRIVATE_PROPERTIES);
144: objectRecipe.allow(Option.STATIC_PROPERTIES);
145: Class mainClass = classLoader.loadClass(mainClassName);
146: List<Injection> injections = new ArrayList<Injection>();
147: while (mainClass != null && mainClass != Object.class) {
148: List<Injection> perClass = holder
149: .getInjections(mainClass.getName());
150: if (perClass != null) {
151: injections.addAll(perClass);
152: }
153: mainClass = mainClass.getSuperclass();
154: }
155: if (injections != null) {
156: List<NamingException> problems = new ArrayList<NamingException>();
157: for (Injection injection : injections) {
158: try {
159: String jndiName = injection.getJndiName();
160: //our componentContext is attached to jndi at "java:comp" so we remove that when looking stuff up in it
161: Object object = componentContext.lookup("env/"
162: + jndiName);
163: if (object instanceof String) {
164: String string = (String) object;
165: // Pass it in raw so it could be potentially converted to
166: // another data type by an xbean-reflect property editor
167: objectRecipe.setProperty(injection
168: .getTargetName(), string);
169: } else {
170: objectRecipe.setProperty(injection
171: .getTargetName(), new StaticRecipe(
172: object));
173: }
174: } catch (NamingException e) {
175: problems.add(e);
176: }
177: }
178: if (!problems.isEmpty()) {
179: throw new Exception(
180: "Some objects to be injected were not found in jndi: "
181: + problems);
182: }
183: }
184: Class clazz = objectRecipe.setStaticProperties(classLoader);
185: if (holder.getPostConstruct() != null) {
186: Holder.apply(null, clazz, holder.getPostConstruct());
187: }
188:
189: if (clientSubject == null) {
190: mainMethod.invoke(null, new Object[] { args });
191: } else {
192: Subject.doAs(clientSubject, new PrivilegedAction() {
193: public Object run() {
194: try {
195: mainMethod.invoke(null,
196: new Object[] { args });
197: } catch (IllegalAccessException e) {
198: throw new RuntimeException(e);
199: } catch (InvocationTargetException e) {
200: throw new RuntimeException(e);
201: }
202: return null;
203: }
204: });
205: }
206: } catch (InvocationTargetException e) {
207: Throwable cause = e.getCause();
208: if (cause instanceof Exception) {
209: throw (Exception) cause;
210: } else if (cause instanceof Error) {
211: throw (Error) cause;
212: }
213: throw new Error(e);
214: } finally {
215: //How can this work??
216: thread.setContextClassLoader(oldClassLoader);
217: ContextManager.popCallers(oldCallers);
218: }
219: }
220:
221: public void doStart() throws Exception {
222: }
223:
224: public void doStop() throws Exception {
225: if (callbackHandler != null) {
226: holder.destroyInstance(callbackHandler);
227: }
228: if (loginContext != null) {
229: ContextManager.logout(loginContext);
230: }
231: jndiContext.stopClient(appClientModuleName);
232: }
233:
234: public void doFail() {
235: try {
236: doStop();
237: } catch (Exception e) {
238: //ignore
239: }
240: }
241:
242: public static final GBeanInfo GBEAN_INFO;
243:
244: static {
245: GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(
246: AppClientContainer.class, NameFactory.APP_CLIENT);
247:
248: infoFactory.addAttribute("mainClassName", String.class, true);
249: infoFactory.addAttribute("appClientModuleName",
250: AbstractName.class, true);
251: infoFactory.addAttribute("realmName", String.class, true);
252: infoFactory.addAttribute("callbackHandlerClassName",
253: String.class, true);
254: infoFactory.addAttribute("defaultSubject", SubjectInfo.class,
255: true);
256: infoFactory.addAttribute("holder", Holder.class, true);
257:
258: infoFactory.addReference("JNDIContext", AppClientPlugin.class,
259: NameFactory.GERONIMO_SERVICE);
260: infoFactory.addReference("CredentialStore",
261: CredentialStore.class, NameFactory.GERONIMO_SERVICE);
262:
263: infoFactory.addAttribute("classLoader", ClassLoader.class,
264: false);
265: infoFactory.addAttribute("kernel", Kernel.class, false);
266:
267: infoFactory.setConstructor(new String[] { "mainClassName",
268: "appClientModuleName", "realmName",
269: "callbackHandlerClassName", "defaultSubject", "holder",
270: "JNDIContext", "CredentialStore", "classLoader",
271: "kernel" });
272:
273: GBEAN_INFO = infoFactory.getBeanInfo();
274: }
275:
276: public static GBeanInfo getGBeanInfo() {
277: return GBEAN_INFO;
278: }
279:
280: }
|