001 /**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements. See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.geronimo.client;
019
020 import java.lang.reflect.Constructor;
021 import java.lang.reflect.InvocationTargetException;
022 import java.lang.reflect.Method;
023 import java.security.PrivilegedAction;
024
025 import javax.security.auth.Subject;
026 import javax.security.auth.callback.CallbackHandler;
027 import javax.security.auth.login.LoginContext;
028 import javax.security.auth.login.LoginException;
029
030 import org.apache.geronimo.gbean.AbstractName;
031 import org.apache.geronimo.gbean.GBeanInfo;
032 import org.apache.geronimo.gbean.GBeanInfoBuilder;
033 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
034 import org.apache.geronimo.kernel.Kernel;
035 import org.apache.geronimo.security.ContextManager;
036 import org.apache.geronimo.security.Callers;
037 import org.apache.geronimo.security.deploy.DefaultPrincipal;
038 import org.apache.geronimo.security.util.ConfigurationUtil;
039
040 /**
041 * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
042 */
043 public final class AppClientContainer {
044 private static final Class[] MAIN_ARGS = {String[].class};
045
046 private final String mainClassName;
047 private final AppClientPlugin jndiContext;
048 private final AbstractName appClientModuleName;
049 private final String realmName;
050 private final Class callbackHandlerClass;
051 private final Subject defaultSubject;
052 private final Method mainMethod;
053 private final ClassLoader classLoader;
054 private final Kernel kernel;
055
056 public AppClientContainer(String mainClassName,
057 AbstractName appClientModuleName,
058 String realmName,
059 String callbackHandlerClassName,
060 DefaultPrincipal defaultPrincipal,
061 AppClientPlugin jndiContext,
062 ClassLoader classLoader,
063 Kernel kernel
064 ) throws Exception {
065 this.mainClassName = mainClassName;
066 this.appClientModuleName = appClientModuleName;
067 if ((realmName == null) != (callbackHandlerClassName == null)) {
068 throw new IllegalArgumentException("You must supply both realmName and callbackHandlerClass or neither");
069 }
070 this.realmName = realmName;
071 if (callbackHandlerClassName != null) {
072 try {
073 this.callbackHandlerClass = classLoader.loadClass(callbackHandlerClassName);
074 } catch (ClassNotFoundException e) {
075 throw new AppClientInitializationException("Could not load callbackHandlerClass", e);
076 }
077 } else {
078 callbackHandlerClass = null;
079 }
080 if (defaultPrincipal != null) {
081 defaultSubject = ConfigurationUtil.generateDefaultSubject(defaultPrincipal, classLoader);
082 } else {
083 defaultSubject = null;
084 }
085 this.classLoader = classLoader;
086 this.kernel = kernel;
087 this.jndiContext = jndiContext;
088
089 try {
090 Class mainClass = classLoader.loadClass(mainClassName);
091 mainMethod = mainClass.getMethod("main", MAIN_ARGS);
092 } catch (ClassNotFoundException e) {
093 throw new AppClientInitializationException("Unable to load Main-Class " + mainClassName, e);
094 } catch (NoSuchMethodException e) {
095 throw new AppClientInitializationException("Main-Class " + mainClassName + " does not have a main method", e);
096 }
097 }
098
099 public AbstractName getAppClientModuleName() {
100 return appClientModuleName;
101 }
102
103 public String getMainClassName() {
104 return mainClassName;
105 }
106
107 public void main(final String[] args) throws Exception {
108 //TODO reorganize this so it makes more sense. maybe use an interceptor stack.
109 //TODO track resource ref shared and app managed security
110 Thread thread = Thread.currentThread();
111
112 ClassLoader oldClassLoader = thread.getContextClassLoader();
113 Callers oldCallers = ContextManager.getCallers();
114 Subject clientSubject = defaultSubject;
115 LoginContext loginContext = null;
116 try {
117 thread.setContextClassLoader(classLoader);
118 if (callbackHandlerClass != null) {
119 //look for a constructor taking the args
120 CallbackHandler callbackHandler;
121 try {
122 Constructor cArgs = callbackHandlerClass.getConstructor(new Class[] {String[].class});
123 callbackHandler = (CallbackHandler) cArgs.newInstance(new Object[] {args});
124 } catch (NoSuchMethodException e) {
125 callbackHandler = (CallbackHandler) callbackHandlerClass.newInstance();
126 }
127 loginContext = new LoginContext(realmName, callbackHandler);
128 try {
129 loginContext.login();
130 } catch (LoginException e) {
131 loginContext = null;
132 throw e;
133 }
134 clientSubject = loginContext.getSubject();
135 }
136 ContextManager.setCallers(clientSubject, clientSubject);
137 jndiContext.startClient(appClientModuleName, kernel, classLoader);
138 if (clientSubject == null) {
139 mainMethod.invoke(null, new Object[]{args});
140 } else {
141 Subject.doAs(clientSubject, new PrivilegedAction() {
142 public Object run() {
143 try {
144 mainMethod.invoke(null, new Object[]{args});
145 } catch (IllegalAccessException e) {
146 throw new RuntimeException(e);
147 } catch (InvocationTargetException e) {
148 throw new RuntimeException(e);
149 }
150 return null;
151 }
152 });
153 }
154 } catch (InvocationTargetException e) {
155 Throwable cause = e.getCause();
156 if (cause instanceof Exception) {
157 throw (Exception) cause;
158 } else if (cause instanceof Error) {
159 throw (Error) cause;
160 }
161 throw new Error(e);
162 } finally {
163 if (loginContext != null) {
164 loginContext.logout();
165 }
166 jndiContext.stopClient(appClientModuleName);
167
168 thread.setContextClassLoader(oldClassLoader);
169 ContextManager.popCallers(oldCallers);
170 }
171 }
172
173 public static final GBeanInfo GBEAN_INFO;
174
175 static {
176 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(AppClientContainer.class, NameFactory.APP_CLIENT);
177
178 infoFactory.addOperation("main", new Class[]{String[].class});
179
180 infoFactory.addAttribute("mainClassName", String.class, true);
181 infoFactory.addAttribute("appClientModuleName", AbstractName.class, true);
182 infoFactory.addAttribute("realmName", String.class, true);
183 infoFactory.addAttribute("callbackHandlerClassName", String.class, true);
184 infoFactory.addAttribute("defaultPrincipal", DefaultPrincipal.class, true);
185
186 infoFactory.addReference("JNDIContext", AppClientPlugin.class, NameFactory.GERONIMO_SERVICE);
187
188 infoFactory.addAttribute("classLoader", ClassLoader.class, false);
189 infoFactory.addAttribute("kernel", Kernel.class, false);
190
191
192 infoFactory.setConstructor(new String[]{"mainClassName",
193 "appClientModuleName",
194 "realmName",
195 "callbackHandlerClassName",
196 "defaultPrincipal",
197 "JNDIContext",
198 "classLoader",
199 "kernel"
200 });
201
202 GBEAN_INFO = infoFactory.getBeanInfo();
203 }
204
205 public static GBeanInfo getGBeanInfo() {
206 return GBEAN_INFO;
207 }
208 }