001/* 002 * Copyright 2012-2016 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.tools; 018 019import java.io.ByteArrayOutputStream; 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.io.OutputStream; 025import java.nio.charset.Charset; 026import java.util.Map; 027import java.util.regex.Matcher; 028import java.util.regex.Pattern; 029 030/** 031 * Default implementation of {@link LaunchScript}. Provides the default Spring Boot launch 032 * script or can load a specific script File. Also support mustache style template 033 * expansion of the form <code>{{name:default}}</code>. 034 * 035 * @author Phillip Webb 036 * @since 1.3.0 037 */ 038public class DefaultLaunchScript implements LaunchScript { 039 040 private static final Charset UTF_8 = Charset.forName("UTF-8"); 041 042 private static final int BUFFER_SIZE = 4096; 043 044 private static final Pattern PLACEHOLDER_PATTERN = Pattern 045 .compile("\\{\\{(\\w+)(:.*?)?\\}\\}(?!\\})"); 046 047 private final String content; 048 049 /** 050 * Create a new {@link DefaultLaunchScript} instance. 051 * @param file the source script file or {@code null} to use the default 052 * @param properties an optional set of script properties used for variable expansion 053 * @throws IOException if the script cannot be loaded 054 */ 055 public DefaultLaunchScript(File file, Map<?, ?> properties) throws IOException { 056 String content = loadContent(file); 057 this.content = expandPlaceholders(content, properties); 058 } 059 060 private String loadContent(File file) throws IOException { 061 if (file == null) { 062 return loadContent(getClass().getResourceAsStream("launch.script")); 063 } 064 return loadContent(new FileInputStream(file)); 065 } 066 067 private String loadContent(InputStream inputStream) throws IOException { 068 try { 069 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 070 copy(inputStream, outputStream); 071 return new String(outputStream.toByteArray(), UTF_8); 072 } 073 finally { 074 inputStream.close(); 075 } 076 } 077 078 private void copy(InputStream inputStream, OutputStream outputStream) 079 throws IOException { 080 byte[] buffer = new byte[BUFFER_SIZE]; 081 int bytesRead; 082 while ((bytesRead = inputStream.read(buffer)) != -1) { 083 outputStream.write(buffer, 0, bytesRead); 084 } 085 outputStream.flush(); 086 } 087 088 private String expandPlaceholders(String content, Map<?, ?> properties) { 089 StringBuffer expanded = new StringBuffer(); 090 Matcher matcher = PLACEHOLDER_PATTERN.matcher(content); 091 while (matcher.find()) { 092 String name = matcher.group(1); 093 String value = matcher.group(2); 094 if (properties != null && properties.containsKey(name)) { 095 value = (String) properties.get(name); 096 } 097 else { 098 value = (value == null ? matcher.group(0) : value.substring(1)); 099 } 100 matcher.appendReplacement(expanded, value.replace("$", "\\$")); 101 } 102 matcher.appendTail(expanded); 103 return expanded.toString(); 104 } 105 106 @Override 107 public byte[] toByteArray() { 108 return this.content.getBytes(UTF_8); 109 } 110 111}