001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017 018package io.stallion.boot; 019 020import io.stallion.templating.JinjaTemplating; 021import org.apache.commons.io.FileUtils; 022import org.apache.commons.io.IOUtils; 023import org.apache.commons.lang3.ArrayUtils; 024 025import java.io.File; 026import java.io.IOException; 027import java.nio.file.FileSystems; 028import java.nio.file.Files; 029import java.nio.file.Paths; 030import java.nio.file.attribute.PosixFileAttributes; 031import java.nio.file.attribute.PosixFilePermission; 032import java.util.List; 033import java.util.Map; 034import java.util.Scanner; 035 036import static io.stallion.utils.Literals.*; 037 038/** 039 * A command-line StallionRunAction that will walk the user through generating the 040 * scaffolding for a brand new Stallion java plugin, including setting up the maven 041 * pom.xml and a directory structure. 042 */ 043public class NewJavaPluginRunAction implements StallionRunAction<CommandOptionsBase> { 044 Scanner scanner = new Scanner (System.in); 045 private String targetFolder = ""; 046 private CommandOptionsBase options; 047 private JinjaTemplating templating; 048 049 private String pluginName = ""; 050 private String pluginNameTitleCase = ""; 051 private String javaPackageName = ""; 052 private String groupId = ""; 053 private String artifactId = ""; 054 055 056 @Override 057 public String getActionName() { 058 return "new-java-plugin"; 059 } 060 061 @Override 062 public String getHelp() { 063 return "Create a stallion plugin with a maven pom.xml"; 064 } 065 066 @Override 067 public void loadApp(CommandOptionsBase options) { 068 069 } 070 071 @Override 072 public CommandOptionsBase newCommandOptions() { 073 return new CommandOptionsBase(); 074 } 075 076 @Override 077 public void execute(CommandOptionsBase options) throws Exception { 078 this.options = options; 079 String folder = or(options.getTargetPath(), Paths.get(".").toAbsolutePath().normalize().toString()); 080 makeNewApp(folder); 081 } 082 public void makeNewApp(String javaFolder) throws Exception { 083 targetFolder = javaFolder; 084 085 templating = new JinjaTemplating(targetFolder, false); 086 087 088 setGroupId(promptForInputOfLength("What is the maven group name? ", 5)); 089 setPluginName(promptForInputOfLength("What is the plugin package name (will be appended to the groupid)? ", 2)); 090 setArtifactId(promptForInputOfLength("What maven artifact id? ", 5)); 091 092 093 setPluginNameTitleCase(getPluginName().substring(0, 1).toUpperCase() + getPluginName().substring(1)); 094 setJavaPackageName(getGroupId() + "." + getPluginName()); 095 096 097 File dir = new File(javaFolder); 098 if (!dir.isDirectory()) { 099 FileUtils.forceMkdir(dir); 100 } 101 102 103 String sourceFolder = "/src/main/java/" + getJavaPackageName().replaceAll("\\.", "/"); 104 List<String> paths = list( 105 targetFolder + "/src/main/resources", 106 targetFolder + "/src/test/resources", 107 targetFolder + "/src/main/java/" + getJavaPackageName().replaceAll("\\.", "/"), 108 targetFolder + "/src/test/java/" + getJavaPackageName().replaceAll("\\.", "/") 109 ); 110 for(String path: paths) { 111 File file = new File(path); 112 if (!file.isDirectory()) { 113 FileUtils.forceMkdir(file); 114 } 115 } 116 Map ctx = map(val("config", this)); 117 copyTemplate("/templates/wizard/pom.xml.jinja", "pom.xml", ctx); 118 copyTemplate("/templates/wizard/PluginBooter.java.jinja", sourceFolder + "/" + pluginNameTitleCase + "Plugin.java", ctx); 119 copyTemplate("/templates/wizard/PluginSettings.java.jinja", sourceFolder + "/" + pluginNameTitleCase + "Settings.java", ctx); 120 copyTemplate("/templates/wizard/MainRunner.java.jinja", sourceFolder + "/MainRunner.java", ctx); 121 copyTemplate("/templates/wizard/Endpoints.java.jinja", sourceFolder + "/Endpoints.java", ctx); 122 copyTemplate("/templates/wizard/build.py.jinja", "build.py", ctx); 123 copyTemplate("/templates/wizard/run-dev.jinja", "run-dev.sh", ctx); 124 125 Files.setPosixFilePermissions(FileSystems.getDefault().getPath(targetFolder + "/build.py"), 126 set(PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE) 127 ); 128 Files.setPosixFilePermissions(FileSystems.getDefault().getPath(targetFolder + "/run-dev.sh"), 129 set(PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE) 130 ); 131 copyFile("/templates/wizard/app.bundle", "src/main/resources/assets/app.bundle"); 132 copyFile("/templates/wizard/app.js", "src/main/resources/assets/app.js"); 133 copyFile("/templates/wizard/app.scss", "src/main/resources/assets/app.scss"); 134 copyFile("/templates/wizard/file1.js", "src/main/resources/assets/common/file1.js"); 135 copyFile("/templates/wizard/file2.js", "src/main/resources/assets/common/file2.js"); 136 copyFile("/assets/vendor/jquery-1.11.3.js", "src/main/resources/assets/vendor/jquery-1.11.3.js"); 137 copyFile("/assets/basic/stallion.js", "src/main/resources/assets/vendor/stallion.js"); 138 copyFile("/templates/wizard/app.jinja", "src/main/resources/templates/app.jinja"); 139 140 } 141 142 143 144 public void copyTemplate(String resourcePath, String relativePath, Map ctx) throws IOException { 145 String source = templating.renderTemplate( 146 IOUtils.toString(getClass().getResource(resourcePath), UTF8), 147 ctx); 148 File file = new File(targetFolder + "/" + relativePath); 149 File parent = file.getParentFile(); 150 if (!parent.isDirectory()) { 151 parent.mkdirs(); 152 } 153 FileUtils.writeStringToFile(file, source, "utf-8"); 154 } 155 156 public void copyFile(String resourcePath, String relativePath) throws IOException { 157 File file = new File(targetFolder + "/" + relativePath); 158 File parent = file.getParentFile(); 159 if (!parent.isDirectory()) { 160 parent.mkdirs(); 161 } 162 String source = IOUtils.toString(getClass().getResource(resourcePath), UTF8); 163 FileUtils.writeStringToFile(file, source, "utf-8"); 164 } 165 166 167 168 public boolean promptYesNo(String prompt) { 169 System.out.print(prompt + " Answer yes/no or y/n: "); 170 for (int i : safeLoop(100)) { 171 String input = scanner.next().trim().toLowerCase(); 172 if (input.startsWith("y")) { 173 return true; 174 } else if (input.startsWith("n")) { 175 return false; 176 } 177 System.out.println("Please answer y or n."); 178 } 179 return false; 180 } 181 182 public String promptForInputOfLength(String prompt, int length) { 183 System.out.print(prompt); 184 for (int i : safeLoop(100)) { 185 String input = scanner.next().trim(); 186 if (input.length() >= length) { 187 return input; 188 } 189 System.out.println("Input must be at least" + length + " characters."); 190 } 191 return ""; 192 } 193 194 public String promptForInputOfLength(String prompt, int length, String contains) { 195 System.out.print(prompt); 196 for (int i : safeLoop(100)) { 197 String input = scanner.next().trim(); 198 if (input.length() >= length && input.contains(contains)) { 199 return input; 200 } 201 System.out.println("Input must be at least" + length + " characters and contain " + contains); 202 } 203 return ""; 204 } 205 206 207 public String promptForValidInput(String prompt, String...validChoices) { 208 System.out.print(prompt); 209 for (int i: safeLoop(100)) { 210 String input = scanner.next().trim(); 211 if (ArrayUtils.contains(validChoices, input)) { 212 return input; 213 } 214 System.out.println("Invalid choice!"); 215 } 216 return ""; 217 } 218 219 220 public String getPluginName() { 221 return pluginName; 222 } 223 224 public NewJavaPluginRunAction setPluginName(String pluginName) { 225 this.pluginName = pluginName; 226 return this; 227 } 228 229 public String getJavaPackageName() { 230 return javaPackageName; 231 } 232 233 public NewJavaPluginRunAction setJavaPackageName(String javaPackageName) { 234 this.javaPackageName = javaPackageName; 235 return this; 236 } 237 238 public String getPluginNameTitleCase() { 239 return pluginNameTitleCase; 240 } 241 242 public NewJavaPluginRunAction setPluginNameTitleCase(String pluginNameTitleCase) { 243 this.pluginNameTitleCase = pluginNameTitleCase; 244 return this; 245 } 246 247 public String getGroupId() { 248 return groupId; 249 } 250 251 public NewJavaPluginRunAction setGroupId(String groupId) { 252 this.groupId = groupId; 253 return this; 254 } 255 256 public String getArtifactId() { 257 return artifactId; 258 } 259 260 public NewJavaPluginRunAction setArtifactId(String artifactId) { 261 this.artifactId = artifactId; 262 return this; 263 } 264}