View Javadoc

1   /**
2    *
3    *  Licensed to the Apache Software Foundation (ASF) under one or more
4    *  contributor license agreements.  See the NOTICE file distributed with
5    *  this work for additional information regarding copyright ownership.
6    *  The ASF licenses this file to You under the Apache License, Version 2.0
7    *  (the "License"); you may not use this file except in compliance with
8    *  the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing, software
13   *  distributed under the License is distributed on an "AS IS" BASIS,
14   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *  See the License for the specific language governing permissions and
16   *  limitations under the License.
17   */
18  package org.apache.geronimo.client;
19  
20  import java.lang.reflect.Constructor;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.security.PrivilegedAction;
24  
25  import javax.security.auth.Subject;
26  import javax.security.auth.callback.CallbackHandler;
27  import javax.security.auth.login.LoginContext;
28  import javax.security.auth.login.LoginException;
29  
30  import org.apache.geronimo.gbean.AbstractName;
31  import org.apache.geronimo.gbean.GBeanInfo;
32  import org.apache.geronimo.gbean.GBeanInfoBuilder;
33  import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
34  import org.apache.geronimo.kernel.Kernel;
35  import org.apache.geronimo.security.ContextManager;
36  import org.apache.geronimo.security.Callers;
37  import org.apache.geronimo.security.deploy.DefaultPrincipal;
38  import org.apache.geronimo.security.util.ConfigurationUtil;
39  
40  /**
41   * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
42   */
43  public final class AppClientContainer {
44      private static final Class[] MAIN_ARGS = {String[].class};
45  
46      private final String mainClassName;
47      private final AppClientPlugin jndiContext;
48      private final AbstractName appClientModuleName;
49      private final String realmName;
50      private final Class callbackHandlerClass;
51      private final Subject defaultSubject;
52      private final Method mainMethod;
53      private final ClassLoader classLoader;
54      private final Kernel kernel;
55  
56      public AppClientContainer(String mainClassName,
57              AbstractName appClientModuleName,
58              String realmName,
59              String callbackHandlerClassName,
60              DefaultPrincipal defaultPrincipal,
61              AppClientPlugin jndiContext,
62              ClassLoader classLoader,
63              Kernel kernel
64      ) throws Exception {
65          this.mainClassName = mainClassName;
66          this.appClientModuleName = appClientModuleName;
67          if ((realmName == null) != (callbackHandlerClassName == null)) {
68              throw new IllegalArgumentException("You must supply both realmName and callbackHandlerClass or neither");
69          }
70          this.realmName = realmName;
71          if (callbackHandlerClassName != null) {
72              try {
73                  this.callbackHandlerClass = classLoader.loadClass(callbackHandlerClassName);
74              } catch (ClassNotFoundException e) {
75                  throw new AppClientInitializationException("Could not load callbackHandlerClass", e);
76              }
77          } else {
78              callbackHandlerClass = null;
79          }
80          if (defaultPrincipal != null) {
81              defaultSubject = ConfigurationUtil.generateDefaultSubject(defaultPrincipal, classLoader);
82          } else {
83              defaultSubject = null;
84          }
85          this.classLoader = classLoader;
86          this.kernel = kernel;
87          this.jndiContext = jndiContext;
88  
89          try {
90              Class mainClass = classLoader.loadClass(mainClassName);
91              mainMethod = mainClass.getMethod("main", MAIN_ARGS);
92          } catch (ClassNotFoundException e) {
93              throw new AppClientInitializationException("Unable to load Main-Class " + mainClassName, e);
94          } catch (NoSuchMethodException e) {
95              throw new AppClientInitializationException("Main-Class " + mainClassName + " does not have a main method", e);
96          }
97      }
98  
99      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 }