001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.loader; 018 019import java.io.File; 020import java.net.URI; 021import java.net.URL; 022import java.security.CodeSource; 023import java.security.ProtectionDomain; 024import java.util.ArrayList; 025import java.util.List; 026 027import org.springframework.boot.loader.archive.Archive; 028import org.springframework.boot.loader.archive.ExplodedArchive; 029import org.springframework.boot.loader.archive.JarFileArchive; 030import org.springframework.boot.loader.jar.JarFile; 031 032/** 033 * Base class for launchers that can start an application with a fully configured 034 * classpath backed by one or more {@link Archive}s. 035 * 036 * @author Phillip Webb 037 * @author Dave Syer 038 */ 039public abstract class Launcher { 040 041 /** 042 * Launch the application. This method is the initial entry point that should be 043 * called by a subclass {@code public static void main(String[] args)} method. 044 * @param args the incoming arguments 045 * @throws Exception if the application fails to launch 046 */ 047 protected void launch(String[] args) throws Exception { 048 JarFile.registerUrlProtocolHandler(); 049 ClassLoader classLoader = createClassLoader(getClassPathArchives()); 050 launch(args, getMainClass(), classLoader); 051 } 052 053 /** 054 * Create a classloader for the specified archives. 055 * @param archives the archives 056 * @return the classloader 057 * @throws Exception if the classloader cannot be created 058 */ 059 protected ClassLoader createClassLoader(List<Archive> archives) throws Exception { 060 List<URL> urls = new ArrayList<>(archives.size()); 061 for (Archive archive : archives) { 062 urls.add(archive.getUrl()); 063 } 064 return createClassLoader(urls.toArray(new URL[0])); 065 } 066 067 /** 068 * Create a classloader for the specified URLs. 069 * @param urls the URLs 070 * @return the classloader 071 * @throws Exception if the classloader cannot be created 072 */ 073 protected ClassLoader createClassLoader(URL[] urls) throws Exception { 074 return new LaunchedURLClassLoader(urls, getClass().getClassLoader()); 075 } 076 077 /** 078 * Launch the application given the archive file and a fully configured classloader. 079 * @param args the incoming arguments 080 * @param mainClass the main class to run 081 * @param classLoader the classloader 082 * @throws Exception if the launch fails 083 */ 084 protected void launch(String[] args, String mainClass, ClassLoader classLoader) 085 throws Exception { 086 Thread.currentThread().setContextClassLoader(classLoader); 087 createMainMethodRunner(mainClass, args, classLoader).run(); 088 } 089 090 /** 091 * Create the {@code MainMethodRunner} used to launch the application. 092 * @param mainClass the main class 093 * @param args the incoming arguments 094 * @param classLoader the classloader 095 * @return the main method runner 096 */ 097 protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, 098 ClassLoader classLoader) { 099 return new MainMethodRunner(mainClass, args); 100 } 101 102 /** 103 * Returns the main class that should be launched. 104 * @return the name of the main class 105 * @throws Exception if the main class cannot be obtained 106 */ 107 protected abstract String getMainClass() throws Exception; 108 109 /** 110 * Returns the archives that will be used to construct the class path. 111 * @return the class path archives 112 * @throws Exception if the class path archives cannot be obtained 113 */ 114 protected abstract List<Archive> getClassPathArchives() throws Exception; 115 116 protected final Archive createArchive() throws Exception { 117 ProtectionDomain protectionDomain = getClass().getProtectionDomain(); 118 CodeSource codeSource = protectionDomain.getCodeSource(); 119 URI location = (codeSource != null) ? codeSource.getLocation().toURI() : null; 120 String path = (location != null) ? location.getSchemeSpecificPart() : null; 121 if (path == null) { 122 throw new IllegalStateException("Unable to determine code source archive"); 123 } 124 File root = new File(path); 125 if (!root.exists()) { 126 throw new IllegalStateException( 127 "Unable to determine code source archive from " + root); 128 } 129 return (root.isDirectory() ? new ExplodedArchive(root) 130 : new JarFileArchive(root)); 131 } 132 133}