| skipped 7 lines |
8 | 8 | | import java.io.IOException; |
9 | 9 | | import java.io.InputStream; |
10 | 10 | | import java.io.ObjectInputStream; |
| 11 | + | import java.lang.reflect.Method; |
11 | 12 | | import java.net.MalformedURLException; |
12 | | - | import java.net.URISyntaxException; |
13 | 13 | | import java.net.URL; |
14 | 14 | | import java.net.URLClassLoader; |
15 | 15 | | import java.nio.file.Files; |
| skipped 28 lines |
44 | 44 | | |
45 | 45 | | public static final String LOGBACK_CONFIG_FILE_PROPERTY_NAME = "logback.configurationFile"; |
46 | 46 | | |
| 47 | + | private static final String BOOTSTRAP = "io.onedev.commons.bootstrap.Bootstrap"; |
| 48 | + | |
47 | 49 | | public static final String APP_LOADER = "io.onedev.commons.loader.AppLoader"; |
48 | 50 | | |
49 | 51 | | public static File installDir; |
| skipped 6 lines |
56 | 58 | | |
57 | 59 | | public static Command command; |
58 | 60 | | |
59 | | - | @SuppressWarnings("unchecked") |
60 | 61 | | public static void main(String[] args) { |
61 | 62 | | try { |
62 | | - | Locale.setDefault(Locale.US); |
63 | | - | /* |
64 | | - | * Sandbox mode might be checked frequently so we cache the result here |
65 | | - | * to avoid calling File.exists() frequently. |
66 | | - | */ |
67 | | - | sandboxMode = new File("target/sandbox").exists(); |
68 | | - | prodMode = (System.getProperty("prod") != null); |
69 | | - | |
70 | | - | String path; |
71 | | - | try { |
72 | | - | path = Bootstrap.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath(); |
73 | | - | } catch (URISyntaxException e) { |
74 | | - | throw new RuntimeException(e); |
75 | | - | } |
76 | | - | File loadedFrom = new File(path); |
| 63 | + | File loadedFrom = new File(Bootstrap.class.getProtectionDomain() |
| 64 | + | .getCodeSource().getLocation().toURI().getPath()); |
77 | 65 | | |
78 | 66 | | if (new File(loadedFrom.getParentFile(), "bootstrap.keys").exists()) |
79 | 67 | | installDir = loadedFrom.getParentFile().getParentFile(); |
| skipped 2 lines |
82 | 70 | | else |
83 | 71 | | throw new RuntimeException("Unable to find product directory."); |
84 | 72 | | |
| 73 | + | boolean launchedFromIDE = false; |
85 | 74 | | try { |
86 | | - | installDir = installDir.getCanonicalFile(); |
87 | | - | } catch (IOException e) { |
88 | | - | throw new RuntimeException(e); |
| 75 | + | Class.forName(APP_LOADER); |
| 76 | + | launchedFromIDE = true; |
| 77 | + | } catch (ClassNotFoundException e) { |
89 | 78 | | } |
90 | 79 | | |
91 | | - | if (args.length != 0) { |
92 | | - | String[] commandArgs = new String[args.length-1]; |
93 | | - | System.arraycopy(args, 1, commandArgs, 0, commandArgs.length); |
94 | | - | command = new Command(args[0], commandArgs); |
| 80 | + | if (launchedFromIDE) { |
| 81 | + | Map<String, File> systemClasspath = getSystemClasspath(); |
| 82 | + | if (systemClasspath == null) { |
| 83 | + | throw new RuntimeException("File '" + getSystemClasspathFile().getAbsolutePath() |
| 84 | + | + "' is expected to exist when launched from IDE"); |
| 85 | + | } |
| 86 | + | |
| 87 | + | Set<String> bootstrapKeys = getBootstrapKeys(); |
| 88 | + | |
| 89 | + | List<File> bootstrapLibs = new ArrayList<>(); |
| 90 | + | for (Map.Entry<String, File> entry : systemClasspath.entrySet()) { |
| 91 | + | if (bootstrapKeys.contains(entry.getKey())) |
| 92 | + | bootstrapLibs.add(entry.getValue()); |
| 93 | + | } |
| 94 | + | |
| 95 | + | List<URL> urls = new ArrayList<>(); |
| 96 | + | for (File file: bootstrapLibs) |
| 97 | + | urls.add(file.toURI().toURL()); |
| 98 | + | |
| 99 | + | URLClassLoader bootClassLoader = new URLClassLoader( |
| 100 | + | urls.toArray(new URL[0]), Bootstrap.class.getClassLoader().getParent()); |
| 101 | + | |
| 102 | + | Class<?> bootstrapClass = bootClassLoader.loadClass(BOOTSTRAP); |
| 103 | + | Method mainMethod = bootstrapClass.getDeclaredMethod("main", String[].class); |
| 104 | + | mainMethod.invoke(null, (Object)args); |
95 | 105 | | } else { |
96 | | - | command = null; |
97 | | - | } |
| 106 | + | Locale.setDefault(Locale.US); |
| 107 | + | /* |
| 108 | + | * Sandbox mode might be checked frequently so we cache the result here |
| 109 | + | * to avoid calling File.exists() frequently. |
| 110 | + | */ |
| 111 | + | sandboxMode = new File("target/sandbox").exists(); |
| 112 | + | prodMode = (System.getProperty("prod") != null); |
98 | 113 | | |
99 | | - | configureLogging(); |
100 | | - | try { |
101 | | - | logger.info("Launching application from '" + installDir.getAbsolutePath() + "'..."); |
102 | | - | |
103 | | - | File tempDir = getTempDir(); |
104 | | - | if (tempDir.exists()) { |
105 | | - | logger.info("Cleaning temp directory..."); |
106 | | - | Files.walk(tempDir.toPath()).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); |
| 114 | + | if (args.length != 0) { |
| 115 | + | String[] commandArgs = new String[args.length-1]; |
| 116 | + | System.arraycopy(args, 1, commandArgs, 0, commandArgs.length); |
| 117 | + | command = new Command(args[0], commandArgs); |
| 118 | + | } else { |
| 119 | + | command = null; |
107 | 120 | | } |
108 | 121 | | |
109 | | - | if (!tempDir.mkdirs()) |
110 | | - | throw new RuntimeException("Can not create directory '" + tempDir.getAbsolutePath() + "'"); |
| 122 | + | configureLogging(); |
| 123 | + | try { |
| 124 | + | logger.info("Launching application from '" + installDir.getAbsolutePath() + "'..."); |
111 | 125 | | |
112 | | - | System.setProperty("java.io.tmpdir", tempDir.getAbsolutePath()); |
| 126 | + | File tempDir = getTempDir(); |
| 127 | + | if (tempDir.exists()) { |
| 128 | + | logger.info("Cleaning temp directory..."); |
| 129 | + | Files.walk(tempDir.toPath()).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); |
| 130 | + | } |
113 | 131 | | |
114 | | - | libCacheDir = createTempDir("libcache"); |
| 132 | + | if (!tempDir.mkdirs()) |
| 133 | + | throw new RuntimeException("Can not create directory '" + tempDir.getAbsolutePath() + "'"); |
115 | 134 | | |
116 | | - | List<File> libFiles = new ArrayList<>(); |
| 135 | + | System.setProperty("java.io.tmpdir", tempDir.getAbsolutePath()); |
117 | 136 | | |
118 | | - | File classpathFile = new File(installDir, "boot/system.classpath"); |
119 | | - | if (classpathFile.exists()) { |
120 | | - | Map<String, File> systemClasspath; |
121 | | - | try (ObjectInputStream is = new ObjectInputStream(new FileInputStream(classpathFile))) { |
122 | | - | systemClasspath = (Map<String, File>) is.readObject(); |
123 | | - | } |
| 137 | + | libCacheDir = createTempDir("libcache"); |
124 | 138 | | |
125 | | - | File keysFile = new File(Bootstrap.installDir, "boot/bootstrap.keys"); |
126 | | - | Set<String> bootstrapKeys; |
127 | | - | try (ObjectInputStream is = new ObjectInputStream(new FileInputStream(keysFile))) { |
128 | | - | bootstrapKeys = (Set<String>) is.readObject(); |
129 | | - | } |
| 139 | + | List<File> libFiles = new ArrayList<>(); |
130 | 140 | | |
131 | | - | for (Map.Entry<String, File> entry : systemClasspath.entrySet()) { |
132 | | - | if (!bootstrapKeys.contains(entry.getKey())) |
133 | | - | libFiles.add(entry.getValue()); |
| 141 | + | Map<String, File> systemClasspath = getSystemClasspath(); |
| 142 | + | if (systemClasspath != null) { |
| 143 | + | Set<String> bootstrapKeys = getBootstrapKeys(); |
| 144 | + | for (Map.Entry<String, File> entry : systemClasspath.entrySet()) { |
| 145 | + | if (!bootstrapKeys.contains(entry.getKey())) |
| 146 | + | libFiles.add(entry.getValue()); |
| 147 | + | } |
| 148 | + | } else { |
| 149 | + | libFiles.addAll(getLibFiles(getLibDir())); |
| 150 | + | cacheLibFiles(getLibDir()); |
134 | 151 | | } |
135 | | - | } else { |
136 | | - | libFiles.addAll(getLibFiles(getLibDir())); |
137 | | - | cacheLibFiles(getLibDir()); |
138 | | - | } |
139 | 152 | | |
140 | | - | File siteLibDir = new File(getSiteDir(), "lib"); |
141 | | - | libFiles.addAll(getLibFiles(siteLibDir)); |
142 | | - | cacheLibFiles(siteLibDir); |
143 | | - | libFiles.addAll(getLibFiles(libCacheDir)); |
| 153 | + | File siteLibDir = new File(getSiteDir(), "lib"); |
| 154 | + | libFiles.addAll(getLibFiles(siteLibDir)); |
| 155 | + | cacheLibFiles(siteLibDir); |
| 156 | + | libFiles.addAll(getLibFiles(libCacheDir)); |
144 | 157 | | |
145 | | - | List<URL> urls = new ArrayList<URL>(); |
| 158 | + | List<URL> urls = new ArrayList<URL>(); |
146 | 159 | | |
147 | | - | // load our jars first so that we can override classes in third party |
148 | | - | // jars if necessary. |
149 | | - | for (File file : libFiles) { |
150 | | - | if (isPriorityLib(file)) { |
151 | | - | try { |
152 | | - | urls.add(file.toURI().toURL()); |
153 | | - | } catch (MalformedURLException e) { |
154 | | - | throw new RuntimeException(e); |
| 160 | + | // load our jars first so that we can override classes in third party |
| 161 | + | // jars if necessary. |
| 162 | + | for (File file : libFiles) { |
| 163 | + | if (isPriorityLib(file)) { |
| 164 | + | try { |
| 165 | + | urls.add(file.toURI().toURL()); |
| 166 | + | } catch (MalformedURLException e) { |
| 167 | + | throw new RuntimeException(e); |
| 168 | + | } |
155 | 169 | | } |
156 | 170 | | } |
157 | | - | } |
158 | | - | for (File file : libFiles) { |
159 | | - | if (!isPriorityLib(file)) { |
160 | | - | try { |
161 | | - | urls.add(file.toURI().toURL()); |
162 | | - | } catch (MalformedURLException e) { |
163 | | - | throw new RuntimeException(e); |
| 171 | + | for (File file : libFiles) { |
| 172 | + | if (!isPriorityLib(file)) { |
| 173 | + | try { |
| 174 | + | urls.add(file.toURI().toURL()); |
| 175 | + | } catch (MalformedURLException e) { |
| 176 | + | throw new RuntimeException(e); |
| 177 | + | } |
164 | 178 | | } |
165 | 179 | | } |
166 | | - | } |
167 | 180 | | |
168 | | - | ClassLoader appClassLoader = new URLClassLoader(urls.toArray(new URL[0]), |
169 | | - | Bootstrap.class.getClassLoader()); |
170 | | - | Thread.currentThread().setContextClassLoader(appClassLoader); |
| 181 | + | ClassLoader appClassLoader = new URLClassLoader(urls.toArray(new URL[0]), |
| 182 | + | Bootstrap.class.getClassLoader()); |
| 183 | + | Thread.currentThread().setContextClassLoader(appClassLoader); |
171 | 184 | | |
172 | | - | Lifecycle appLoader; |
173 | | - | try { |
174 | | - | Class<?> appLoaderClass = appClassLoader.loadClass(APP_LOADER); |
175 | | - | appLoader = (Lifecycle) appLoaderClass.newInstance(); |
176 | | - | appLoader.start(); |
177 | | - | } catch (Exception e) { |
178 | | - | throw unchecked(e); |
179 | | - | } |
| 185 | + | Lifecycle appLoader; |
| 186 | + | try { |
| 187 | + | Class<?> appLoaderClass = appClassLoader.loadClass(APP_LOADER); |
| 188 | + | appLoader = (Lifecycle) appLoaderClass.newInstance(); |
| 189 | + | appLoader.start(); |
| 190 | + | } catch (Exception e) { |
| 191 | + | throw unchecked(e); |
| 192 | + | } |
180 | 193 | | |
181 | | - | Runtime.getRuntime().addShutdownHook(new Thread() { |
182 | | - | public void run() { |
183 | | - | try { |
184 | | - | appLoader.stop(); |
185 | | - | } catch (Exception e) { |
186 | | - | throw unchecked(e); |
| 194 | + | Runtime.getRuntime().addShutdownHook(new Thread() { |
| 195 | + | public void run() { |
| 196 | + | try { |
| 197 | + | appLoader.stop(); |
| 198 | + | } catch (Exception e) { |
| 199 | + | throw unchecked(e); |
| 200 | + | } |
187 | 201 | | } |
188 | | - | } |
189 | | - | }); |
190 | | - | } catch (Exception e) { |
191 | | - | logger.error("Error booting application", e); |
192 | | - | System.exit(1); |
| 202 | + | }); |
| 203 | + | } catch (Exception e) { |
| 204 | + | logger.error("Error booting application", e); |
| 205 | + | System.exit(1); |
| 206 | + | } |
193 | 207 | | } |
194 | 208 | | } catch (Exception e) { |
195 | 209 | | e.printStackTrace(); |
196 | 210 | | System.exit(1); |
197 | 211 | | } |
| 212 | + | } |
| 213 | + | |
| 214 | + | @SuppressWarnings("unchecked") |
| 215 | + | private static Map<String, File> getSystemClasspath() throws Exception { |
| 216 | + | File classpathFile = getSystemClasspathFile(); |
| 217 | + | if (classpathFile.exists()) { |
| 218 | + | try (ObjectInputStream is = new ObjectInputStream(new FileInputStream(classpathFile))) { |
| 219 | + | return (Map<String, File>) is.readObject(); |
| 220 | + | } |
| 221 | + | } else { |
| 222 | + | return null; |
| 223 | + | } |
| 224 | + | } |
| 225 | + | |
| 226 | + | @SuppressWarnings("unchecked") |
| 227 | + | private static Set<String> getBootstrapKeys() throws Exception { |
| 228 | + | try (ObjectInputStream is = new ObjectInputStream(new FileInputStream(getBootstrapKeysFile()))) { |
| 229 | + | return (Set<String>) is.readObject(); |
| 230 | + | } |
| 231 | + | } |
| 232 | + | |
| 233 | + | private static File getBootstrapKeysFile() { |
| 234 | + | return new File(installDir, "boot/bootstrap.keys"); |
| 235 | + | } |
| 236 | + | |
| 237 | + | private static File getSystemClasspathFile() { |
| 238 | + | return new File(installDir, "boot/system.classpath"); |
198 | 239 | | } |
199 | 240 | | |
200 | 241 | | private static boolean isPriorityLib(File lib) { |
| skipped 203 lines |