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.xbean.server.spring.main; 018 019 import java.beans.PropertyEditorManager; 020 import java.io.File; 021 import java.net.JarURLConnection; 022 import java.net.MalformedURLException; 023 import java.net.URI; 024 import java.net.URL; 025 import java.util.Arrays; 026 import java.util.Collections; 027 import java.util.LinkedList; 028 import java.util.List; 029 import java.util.jar.Attributes; 030 import java.util.jar.Manifest; 031 032 import org.apache.xbean.server.main.FatalStartupError; 033 import org.apache.xbean.server.main.Main; 034 import org.apache.xbean.spring.context.ClassPathXmlApplicationContext; 035 import org.apache.xbean.spring.context.FileSystemXmlApplicationContext; 036 import org.apache.xbean.spring.context.SpringApplicationContext; 037 038 /** 039 * SpringBootstrap is the main class used by a Spring based server. This class uses the following strategies to determine 040 * the configuration file to load: 041 * 042 * Command line parameter --bootstrap FILE 043 * Manifest entry XBean-Bootstrap in the startup jar 044 * META-INF/xbean-bootstrap.xml 045 * 046 * This class atempts to first load the configuration file from the local file system and if that fails it attempts to 047 * load it from the classpath. 048 * 049 * SpringBootstrap expects the configuration to contain a service with the id "main" which is an implementation of 050 * org.apache.xbean.server.main.Main. 051 * 052 * This class will set the system property xbean.base.dir to the directory containing the startup jar if the property 053 * has not alredy been set (on the command line). 054 * 055 * @author Dain Sundstrom 056 * @version $Id$ 057 * @since 2.0 058 */ 059 public class SpringBootstrap { 060 private static final String XBEAN_BOOTSTRAP_MANIFEST = "XBean-Bootstrap"; 061 private static final String BOOTSTRAP_FLAG = "--bootstrap"; 062 private static final String DEFAULT_BOOTSTRAP = "META-INF/xbean-bootstrap.xml"; 063 private static final List DEFAULT_PROPERTY_EDITOR_PATHS = Collections.singletonList("org.apache.xbean.server.propertyeditor"); 064 065 private String configurationFile; 066 private String[] mainArguments; 067 private List propertyEditorPaths = DEFAULT_PROPERTY_EDITOR_PATHS; 068 private String serverBaseDirectory; 069 070 /** 071 * Initializes and boots the server using the supplied arguments. If an error is thrown from the boot method, 072 * this method will pring the error to standard error along with the stack trace and exit with the exit specified 073 * in the FatalStartupError or exit code 9 if the error was not a FatalStartupError. 074 * @param args the arguments used to start the server 075 */ 076 public static void main(String[] args) { 077 SpringBootstrap springBootstrap = new SpringBootstrap(); 078 main(args, springBootstrap); 079 } 080 081 /** 082 * Like the main(args) method but allows a configured bootstrap instance to be passed in. 083 * 084 * @see #main(String[]) 085 */ 086 public static void main(String[] args, SpringBootstrap springBootstrap) { 087 springBootstrap.initialize(args); 088 089 try { 090 springBootstrap.boot(); 091 } catch (FatalStartupError e) { 092 System.err.println(e.getMessage()); 093 if (e.getCause() != null) { 094 e.getCause().printStackTrace(); 095 } 096 System.exit(e.getExitCode()); 097 } catch (Throwable e) { 098 System.err.println("Unknown error"); 099 e.printStackTrace(); 100 System.exit(9); 101 } 102 } 103 104 /** 105 * Gets the configuration file from which the main instance is loaded. 106 * @return the configuration file from which the main instance is loaded 107 */ 108 public String getConfigurationFile() { 109 return configurationFile; 110 } 111 112 /** 113 * Sets the configuration file from which the main instance is loaded. 114 * @param configurationFile the configuration file from which the main instance is loaded 115 */ 116 public void setConfigurationFile(String configurationFile) { 117 this.configurationFile = configurationFile; 118 } 119 120 /** 121 * Gets the arguments passed to the main instance. 122 * @return the arguments passed to the main instance 123 */ 124 public String[] getMainArguments() { 125 return mainArguments; 126 } 127 128 /** 129 * Sets the arguments passed to the main instance. 130 * @param mainArguments the arguments passed to the main instance 131 */ 132 public void setMainArguments(String[] mainArguments) { 133 this.mainArguments = mainArguments; 134 } 135 136 /** 137 * Gets the paths that are appended to the system property editors search path. 138 * @return the paths that are appended to the system property editors search path 139 */ 140 public List getPropertyEditorPaths() { 141 return propertyEditorPaths; 142 } 143 144 /** 145 * Sets the paths that are appended to the system property editors search path. 146 * @param propertyEditorPaths the paths that are appended to the system property editors search path 147 */ 148 public void setPropertyEditorPaths(List propertyEditorPaths) { 149 this.propertyEditorPaths = propertyEditorPaths; 150 } 151 152 /** 153 * Gets the base directory of the server. 154 * @return the base directory of the server 155 */ 156 public String getServerBaseDirectory() { 157 return serverBaseDirectory; 158 } 159 160 /** 161 * Sets the base directory of the server. 162 * @param serverBaseDirectory the base directory of the server 163 */ 164 public void setServerBaseDirectory(String serverBaseDirectory) { 165 this.serverBaseDirectory = serverBaseDirectory; 166 } 167 168 /** 169 * Determines the configuration file and server base directory. 170 * @param args the arguments passed to main 171 */ 172 public void initialize(String[] args) { 173 // check if bootstrap configuration was specified on the command line 174 if (args.length > 1 && BOOTSTRAP_FLAG.equals(args[0])) { 175 configurationFile = args[1]; 176 this.mainArguments = new String[args.length - 2]; 177 System.arraycopy(args, 2, this.mainArguments, 0, args.length); 178 } else { 179 if (configurationFile == null) { 180 configurationFile = DEFAULT_BOOTSTRAP; 181 } 182 this.mainArguments = args; 183 } 184 185 // Determine the xbean installation directory 186 // guess from the location of the jar 187 URL url = SpringBootstrap.class.getClassLoader().getResource("META-INF/startup-jar"); 188 if (url != null) { 189 try { 190 JarURLConnection jarConnection = (JarURLConnection) url.openConnection(); 191 url = jarConnection.getJarFileURL(); 192 193 if (serverBaseDirectory == null) { 194 URI baseURI = new URI(url.toString()).resolve(".."); 195 serverBaseDirectory = new File(baseURI).getAbsolutePath(); 196 } 197 198 Manifest manifest; 199 manifest = jarConnection.getManifest(); 200 Attributes mainAttributes = manifest.getMainAttributes(); 201 if (configurationFile == null) { 202 configurationFile = mainAttributes.getValue(XBEAN_BOOTSTRAP_MANIFEST); 203 } 204 } catch (Exception e) { 205 System.err.println("Could not determine xbean installation directory"); 206 e.printStackTrace(); 207 System.exit(9); 208 return; 209 } 210 } else { 211 if (serverBaseDirectory == null) { 212 String dir = System.getProperty("xbean.base.dir", System.getProperty("user.dir")); 213 serverBaseDirectory = new File(dir).getAbsolutePath(); 214 } 215 } 216 } 217 218 /** 219 * Loads the main instance from the configuration file. 220 * @return the main instance 221 */ 222 public Main loadMain() { 223 if (serverBaseDirectory == null) { 224 throw new NullPointerException("serverBaseDirectory is null"); 225 226 } 227 File baseDirectory = new File(serverBaseDirectory); 228 if (!baseDirectory.isDirectory()) { 229 throw new IllegalArgumentException("serverBaseDirectory is not a directory: " + serverBaseDirectory); 230 231 } 232 if (configurationFile == null) { 233 throw new NullPointerException("configurationFile is null"); 234 235 } 236 237 ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); 238 Thread.currentThread().setContextClassLoader(SpringBootstrap.class.getClassLoader()); 239 try { 240 // add our property editors into the system 241 if (propertyEditorPaths != null && !propertyEditorPaths.isEmpty()) { 242 List editorSearchPath = new LinkedList(Arrays.asList(PropertyEditorManager.getEditorSearchPath())); 243 editorSearchPath.addAll(propertyEditorPaths); 244 PropertyEditorManager.setEditorSearchPath((String[]) editorSearchPath.toArray(new String[editorSearchPath.size()])); 245 } 246 247 // set the server base directory system property 248 System.setProperty("xbean.base.dir", baseDirectory.getAbsolutePath()); 249 250 // load the configuration file 251 SpringApplicationContext factory; 252 File file = new File(configurationFile); 253 if (!file.isAbsolute()) { 254 file = new File(baseDirectory, configurationFile); 255 } 256 if (file.canRead()) { 257 try { 258 // configuration file is on the local file system 259 factory = new FileSystemXmlApplicationContext(file.toURL().toString()); 260 } catch (MalformedURLException e) { 261 throw new FatalStartupError("Error creating url for bootstrap file", e); 262 } 263 } else { 264 // assume it is a classpath resource 265 factory = new ClassPathXmlApplicationContext(configurationFile); 266 } 267 268 // get the main service from the configuration file 269 String[] names = factory.getBeanNamesForType(Main.class); 270 Main main = null; 271 if (names.length == 0) { 272 throw new FatalStartupError("No bean of type: " + Main.class.getName() + " found in the bootstrap file: " + configurationFile, 10); 273 } 274 main = (Main) factory.getBean(names[0]); 275 return main; 276 } 277 finally { 278 Thread.currentThread().setContextClassLoader(oldClassLoader); 279 } 280 } 281 282 /** 283 * Loads the main instance from the Spring configuration file and executes it. 284 */ 285 public void boot() { 286 // load the main instance 287 Main main = loadMain(); 288 289 // start it up 290 main.main(mainArguments); 291 292 } 293 }