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 }