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 }