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 */
017 package org.apache.geronimo.client;
018
019 import java.lang.reflect.Constructor;
020 import java.lang.reflect.InvocationTargetException;
021 import java.lang.reflect.Method;
022 import java.security.PrivilegedAction;
023
024 import javax.security.auth.Subject;
025 import javax.security.auth.callback.CallbackHandler;
026 import javax.security.auth.login.LoginContext;
027 import javax.security.auth.login.LoginException;
028
029 import org.apache.geronimo.gbean.AbstractName;
030 import org.apache.geronimo.gbean.GBeanInfo;
031 import org.apache.geronimo.gbean.GBeanInfoBuilder;
032 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
033 import org.apache.geronimo.kernel.Kernel;
034 import org.apache.geronimo.security.ContextManager;
035 import org.apache.geronimo.security.Callers;
036 import org.apache.geronimo.security.deploy.DefaultPrincipal;
037 import org.apache.geronimo.security.util.ConfigurationUtil;
038
039 /**
040 * @version $Rev: 487175 $ $Date: 2006-12-14 03:10:31 -0800 (Thu, 14 Dec 2006) $
041 */
042 public final class AppClientContainer {
043 private static final Class[] MAIN_ARGS = {String[].class};
044
045 private final String mainClassName;
046 private final AppClientPlugin jndiContext;
047 private final AbstractName appClientModuleName;
048 private final String realmName;
049 private final Class callbackHandlerClass;
050 private final Subject defaultSubject;
051 private final Method mainMethod;
052 private final ClassLoader classLoader;
053 private final Kernel kernel;
054
055 public AppClientContainer(String mainClassName,
056 AbstractName appClientModuleName,
057 String realmName,
058 String callbackHandlerClassName,
059 DefaultPrincipal defaultPrincipal,
060 AppClientPlugin jndiContext,
061 ClassLoader classLoader,
062 Kernel kernel
063 ) throws Exception {
064 this.mainClassName = mainClassName;
065 this.appClientModuleName = appClientModuleName;
066 if ((realmName == null) != (callbackHandlerClassName == null)) {
067 throw new IllegalArgumentException("You must supply both realmName and callbackHandlerClass or neither");
068 }
069 this.realmName = realmName;
070 if (callbackHandlerClassName != null) {
071 try {
072 this.callbackHandlerClass = classLoader.loadClass(callbackHandlerClassName);
073 } catch (ClassNotFoundException e) {
074 throw new AppClientInitializationException("Could not load callbackHandlerClass", e);
075 }
076 } else {
077 callbackHandlerClass = null;
078 }
079 if (defaultPrincipal != null) {
080 defaultSubject = ConfigurationUtil.generateDefaultSubject(defaultPrincipal, classLoader);
081 } else {
082 defaultSubject = null;
083 }
084 this.classLoader = classLoader;
085 this.kernel = kernel;
086 this.jndiContext = jndiContext;
087
088 try {
089 Class mainClass = classLoader.loadClass(mainClassName);
090 mainMethod = mainClass.getMethod("main", MAIN_ARGS);
091 } catch (ClassNotFoundException e) {
092 throw new AppClientInitializationException("Unable to load Main-Class " + mainClassName, e);
093 } catch (NoSuchMethodException e) {
094 throw new AppClientInitializationException("Main-Class " + mainClassName + " does not have a main method", e);
095 }
096 }
097
098 public AbstractName getAppClientModuleName() {
099 return appClientModuleName;
100 }
101
102 public String getMainClassName() {
103 return mainClassName;
104 }
105
106 public void main(final String[] args) throws Exception {
107 //TODO reorganize this so it makes more sense. maybe use an interceptor stack.
108 //TODO track resource ref shared and app managed security
109 Thread thread = Thread.currentThread();
110
111 ClassLoader oldClassLoader = thread.getContextClassLoader();
112 Callers oldCallers = ContextManager.getCallers();
113 Subject clientSubject = defaultSubject;
114 LoginContext loginContext = null;
115 try {
116 thread.setContextClassLoader(classLoader);
117 if (callbackHandlerClass != null) {
118 //look for a constructor taking the args
119 CallbackHandler callbackHandler;
120 try {
121 Constructor cArgs = callbackHandlerClass.getConstructor(new Class[] {String[].class});
122 callbackHandler = (CallbackHandler) cArgs.newInstance(new Object[] {args});
123 } catch (NoSuchMethodException e) {
124 callbackHandler = (CallbackHandler) callbackHandlerClass.newInstance();
125 }
126 loginContext = new LoginContext(realmName, callbackHandler);
127 try {
128 loginContext.login();
129 } catch (LoginException e) {
130 loginContext = null;
131 throw e;
132 }
133 clientSubject = loginContext.getSubject();
134 }
135 ContextManager.setCallers(clientSubject, clientSubject);
136 jndiContext.startClient(appClientModuleName, kernel, classLoader);
137 if (clientSubject == null) {
138 mainMethod.invoke(null, new Object[]{args});
139 } else {
140 Subject.doAs(clientSubject, new PrivilegedAction() {
141 public Object run() {
142 try {
143 mainMethod.invoke(null, new Object[]{args});
144 } catch (IllegalAccessException e) {
145 throw new RuntimeException(e);
146 } catch (InvocationTargetException e) {
147 throw new RuntimeException(e);
148 }
149 return null;
150 }
151 });
152 }
153 } catch (InvocationTargetException e) {
154 Throwable cause = e.getCause();
155 if (cause instanceof Exception) {
156 throw (Exception) cause;
157 } else if (cause instanceof Error) {
158 throw (Error) cause;
159 }
160 throw new Error(e);
161 } finally {
162 if (loginContext != null) {
163 loginContext.logout();
164 }
165 jndiContext.stopClient(appClientModuleName);
166
167 thread.setContextClassLoader(oldClassLoader);
168 ContextManager.popCallers(oldCallers);
169 }
170 }
171
172 public static final GBeanInfo GBEAN_INFO;
173
174 static {
175 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(AppClientContainer.class, NameFactory.APP_CLIENT);
176
177 infoFactory.addOperation("main", new Class[]{String[].class});
178
179 infoFactory.addAttribute("mainClassName", String.class, true);
180 infoFactory.addAttribute("appClientModuleName", AbstractName.class, true);
181 infoFactory.addAttribute("realmName", String.class, true);
182 infoFactory.addAttribute("callbackHandlerClassName", String.class, true);
183 infoFactory.addAttribute("defaultPrincipal", DefaultPrincipal.class, true);
184
185 infoFactory.addReference("JNDIContext", AppClientPlugin.class, NameFactory.GERONIMO_SERVICE);
186
187 infoFactory.addAttribute("classLoader", ClassLoader.class, false);
188 infoFactory.addAttribute("kernel", Kernel.class, false);
189
190
191 infoFactory.setConstructor(new String[]{"mainClassName",
192 "appClientModuleName",
193 "realmName",
194 "callbackHandlerClassName",
195 "defaultPrincipal",
196 "JNDIContext",
197 "classLoader",
198 "kernel"
199 });
200
201 GBEAN_INFO = infoFactory.getBeanInfo();
202 }
203
204 public static GBeanInfo getGBeanInfo() {
205 return GBEAN_INFO;
206 }
207 }