package com.adaptrex.tools.webapp;
import static com.adaptrex.tools.Tools.CR;
import static com.adaptrex.tools.Tools.TAB;
import static com.adaptrex.tools.webapp.Webapp.*;
import static com.adaptrex.tools.framework.javascript.JavaScriptFramework.*;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.ini4j.Ini;
import com.adaptrex.tools.Tools;
import com.adaptrex.tools.environment.Environment;
import com.adaptrex.tools.environment.IEnvironmentService;
import com.adaptrex.tools.framework.javascript.AdaptrexJS;
import com.adaptrex.tools.framework.javascript.ExtJS;
import com.adaptrex.tools.framework.javascript.JavaScriptFramework;
import com.adaptrex.tools.framework.javascript.SenchaTouch;
import com.adaptrex.tools.log.ILogService;
import com.adaptrex.tools.log.Log;
import com.google.inject.Inject;
public class WebappService implements IWebappService {
IEnvironmentService environmentService;
ILogService logService;
@Inject
public WebappService(IEnvironmentService environmentService, ILogService logService) {
this.environmentService = environmentService;
this.logService = logService;
}
@Override
public Webapp loadWebapp(String fullPath) throws IOException {
Environment env = environmentService.getEnvironment();
Webapp webapp = new Webapp();
webapp.setFullPath(fullPath);
/*
* Create collections to hold our settings, frameworks and webapps for later processing
*/
Ini ini = new Ini();
File appSettings = new File(fullPath + "/src/main/webapp/adaptrex/app.config");
if (!appSettings.exists()) return null;
ini.load(new FileReader(appSettings));
/*
* Get our app name
*/
Ini.Section settingsSection = ini.get(SETTINGS);
webapp.setName(settingsSection.get("name"));
webapp.setBasePackage(settingsSection.get(BASE_PACKAGE));
webapp.setGlobalFolder(settingsSection.get(GLOBAL_FOLDER));
webapp.setGlobalNamespace(settingsSection.get(GLOBAL_NAMESPACE));
/*
* Get information about our webapp's frameworks
*/
Ini.Section frameworkSection = ini.get(FRAMEWORKS);
if (frameworkSection.containsKey(ADAPTREXJS))
webapp.setAdaptrex(env.getAdaptrexFrameworks().get(frameworkSection.get(ADAPTREXJS)));
if (frameworkSection.containsKey(EXTJS))
webapp.setExt(env.getExtFrameworks().get(frameworkSection.get(EXTJS)));
if (frameworkSection.containsKey(SENCHA_TOUCH))
webapp.setSenchaTouch(env.getSenchaTouchFrameworks().get(frameworkSection.get(SENCHA_TOUCH)));
if (frameworkSection.containsKey(ORM))
webapp.setOrm(frameworkSection.get(ORM));
if (frameworkSection.containsKey(PRESENTATION))
webapp.setPresentation(frameworkSection.get(PRESENTATION));
if (frameworkSection.containsKey(DI))
webapp.setDi(frameworkSection.get(DI));
/*
* Get our list of pages
*/
Map<String,String> pagesSection = ini.get(PAGES);
Map<String,Page> pages = webapp.getPages();
if (pagesSection != null) {
for (String key : pagesSection.keySet()) {
String[] valueParts = pagesSection.get(key).split(",");
String path = key.replace(".", "/");
String name = valueParts[0];
String sdkType = valueParts[1];
Page page = new Page(path, name, sdkType);
pages.put(key, page);
}
}
return webapp;
}
@Override
public boolean compilePage(Webapp webapp, Page page) throws IOException {
String sdkType = page.getFrameworkType();
boolean isExt = sdkType.equals(JavaScriptFramework.EXTJS);
JavaScriptFramework framework = isExt ? webapp.getExt() : webapp.getSenchaTouch();
String sdkPath = isExt ? webapp.getExt().getFullPath() : webapp.getSenchaTouch().getFullPath();
String dependencyFile = isExt ? "ExtDependencies.js" : "SenchaTouchDependencies.js";
String webappSrcAbsolutePath = webapp.getFullPath() + "/src/main/webapp";
String pageAbsolutePath = webappSrcAbsolutePath + "/" + page.getPath();
/*
* Create ghosts
*/
try {
if (!createGhosts(webapp, webappSrcAbsolutePath, pageAbsolutePath, sdkType, sdkPath)) return false;
} catch (Exception e) {
throw new RuntimeException(e);
}
/*
* Compile Page
*/
String compileOutput = null;
framework.cleanCmdBugs();
try {
String[] cmdString = {
environmentService.getEnvironment().getCmdPath() + "/sencha",
"-d",
"-sdk=" + sdkPath,
"compile",
"-classpath=" +
webappSrcAbsolutePath + "/adaptrex/ghosts.js," +
pageAbsolutePath + "/app/app.js," +
pageAbsolutePath + "/app," +
webappSrcAbsolutePath + "/" + webapp.getGlobalFolder() + "," +
webapp.getAdaptrex().getFullPath() + "/" + sdkType + "/src," +
sdkPath + "/src",
"exclude","-all",
"and","include","-r","-f", pageAbsolutePath + "/app/app.js",
"and","exclude","-f", sdkPath + "/src",
"and","exclude","-f", webappSrcAbsolutePath + "/adaptrex/ghosts.js",
"and","concat", pageAbsolutePath + "/build/app-all-debug.js",
"and","concat", "-compress",pageAbsolutePath + "/build/app-all.js",
"and","save", "appset",
"and","exclude","-all",
"and","include","-r","-f", pageAbsolutePath + "/app/app.js",
"and","include","-na", "Adaptrex.override",
"and","include","-r","-f", dependencyFile,
"and","exclude","-s", "appset",
"and","exclude","-f", dependencyFile,
"and","concat", pageAbsolutePath + "/build/app-" + sdkType + "-debug.js",
"and","concat", "-compress",pageAbsolutePath + "/build/app-" + sdkType + ".js"
};
ProcessBuilder processBuilder = new ProcessBuilder(cmdString);
processBuilder.redirectErrorStream();
Process cmd = processBuilder.start();
Tools.StreamGobbler gobbler = new Tools.StreamGobbler(cmd.getInputStream());
gobbler.start();
cmd.waitFor();
compileOutput = gobbler.getGobblerOutput();
if (compileOutput.contains("[ERR]")) {
logService.log(Log.ERROR, "Error compiling page: " + page.getPath(), compileOutput);
return false;
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
framework.restoreCmdBugs();
}
logService.log(Log.SUCCESS, "Successfully compiled page: " + page.getPath());
return true;
}
@Override
public boolean compileWebapp(Webapp webapp) {
// TODO Auto-generated method stub
return true;
}
/*
* Create Ghosts File
*/
private String failPattern = "failed to find meta class definition for name";
private boolean createGhosts(Webapp webapp, String webappPath, String pageAbsolutePath, String sdkType, String sdkPath) throws IOException, InterruptedException {
/*
* Make sure ghosts exist... otherwise create it
*/
String adaptrexFolderPath = webappPath + "/adaptrex";
String ghostsPath = adaptrexFolderPath + "/ghosts.js";
File adaptrexFolder = new File(adaptrexFolderPath);
adaptrexFolder.mkdirs();
File ghostsFile = new File(ghostsPath);
if (!ghostsFile.exists()) {
FileUtils.writeStringToFile(new File(ghostsPath), "// Ghosts");
}
/*
* Noop compile to check for missing dependencies
*/
String[] cmdString = {environmentService.getEnvironment().getCmdPath() + "/sencha",
"-d",
"-sdk=" + sdkPath,
"compile",
"-classpath=" +
ghostsPath + "," +
pageAbsolutePath + "/app/app.js," +
pageAbsolutePath + "/app," +
webappPath + "/" + webapp.getGlobalFolder() + "," +
webapp.getAdaptrex().getFullPath() + "/" + sdkType + "/src," +
sdkPath + "/src",
"exclude", "-a"};
ProcessBuilder processBuilder = new ProcessBuilder(cmdString);
processBuilder.redirectErrorStream();
Process cmd = processBuilder.start();
Tools.StreamGobbler gobbler = new Tools.StreamGobbler(cmd.getInputStream());
gobbler.start();
cmd.waitFor();
/*
* If we have an "ERR" in the response, check to see if if it's a model or a store... in which
* case, we assume it's an inline store created by adaptrex and add the ghost.
*/
String compileOutput = gobbler.getGobblerOutput();
if (compileOutput.contains("[ERR]")) {
if (compileOutput.contains(failPattern)) {
String failedClass = compileOutput.split(failPattern)[1].split(Tools.CR)[0].trim();
if (failedClass.contains(".store.") || failedClass.contains(".model.")) {
FileUtils.writeStringToFile(new File(ghostsPath), "Ext.define('" + failedClass + "',{});" + Tools.CR, true);
return createGhosts(webapp, webappPath, pageAbsolutePath, sdkType, sdkPath);
}
} else {
logService.log(Log.ERROR, "Error building ghosts file", compileOutput);
return false;
}
}
return true;
}
@Override
public boolean deletePage(Webapp webapp, Page page, boolean deleteFiles) {
webapp.getPages().values().remove(page);
logService.log(Log.SUCCESS, "Page at \"" + page.getPath() +
"\" removed from your configuration");
return true;
}
@Override
public Page getPage(Webapp webapp, String path) {
return getPage(webapp, path, false);
}
@Override
public Page getPage(Webapp webapp, String path, boolean suppressWarnings) {
Map<String,Page> pages = webapp.getPages();
Page page = pages.get(path);
if (!suppressWarnings && page == null) {
logService.log(Log.ERROR, "Page at \"" + path + "\" cannot be found");
return null;
}
return pages.get(path);
}
@Override
public Page importPage(Webapp webapp, String pagePath, String namespace, String framework) {
/*
* Make sure page doesn't already exist
*/
Page page = getPage(webapp, pagePath, true);
if (page != null) {
logService.log(Log.ERROR, "Page at \"" + pagePath + "\" already configured");
return null;
}
boolean success = true;
/*
* Make sure the path exists
*/
String fullPath = webapp.getFullPath() + "/src/main/webapp/" + pagePath;
File file = new File(fullPath);
if (!file.exists()) {
logService.log(Log.ERROR, "There is no folder at \"" + pagePath + "\"");
success = false;
}
/*
* Make sure the path is a valid page folder (app.js and /app)
*/
File appjs = new File(fullPath + "/app.js");
File appFolder = new File(fullPath + "/app");
if (!appjs.exists() || !appFolder.exists()) {
logService.log(Log.ERROR, "Invalid page",
"The folder at \"" + pagePath + "\" is not a valid page. Adaptrex requires " + CR +
"a page to contain an \"app.js\" file and an \"app\" folder.");
success = false;
}
/*
* Make sure we have a valid framework
*/
if (!framework.equals(EXTJS) && !framework.equals(SENCHA_TOUCH)) {
logService.log(Log.ERROR, "Invalid framework",
"You must specify either \"extjs\" or \"sencha-touch\" as the framework for this page.");
success = false;
}
if (!success) return null;
page = new Page(pagePath, namespace, framework);
webapp.getPages().put(pagePath, page);
logService.log(Log.SUCCESS, "Page imported");
return page;
}
@Override
public boolean setName(Webapp webapp, String name) {
Webapp existing = environmentService.getWebapp(name, true);
if (existing != null) {
logService.log(Log.ERROR, "A webapp named \"" + name + "\" already exists");
return false;
}
webapp.setName(name);
logService.log(Log.SUCCESS, "Webapp name changed to " + name);
return true;
}
@Override
public boolean setAdaptrexJS(Webapp webapp, String newFolder) {
Map<String,?> fws = environmentService.getEnvironment().getAdaptrexFrameworks();
JavaScriptFramework fw = getNewFramework(webapp, newFolder, fws, "AdaptrexJS", ADAPTREXJS, "a");
if (fw != null) {
webapp.setAdaptrex((AdaptrexJS) fw);
return true;
} else {
return false;
}
}
@Override
public boolean setExtJS(Webapp webapp, String newFolder) {
Map<String,?> fws = environmentService.getEnvironment().getExtFrameworks();
JavaScriptFramework fw = getNewFramework(webapp, newFolder, fws, "ExtJS", EXTJS, "e");
if (fw != null) {
webapp.setExt((ExtJS) fw);
return true;
} else {
return false;
}
}
@Override
public boolean setSenchaTouch(Webapp webapp, String newFolder) {
Map<String,?> fws = environmentService.getEnvironment().getSenchaTouchFrameworks();
JavaScriptFramework fw = getNewFramework(webapp, newFolder, fws, "Sencha Touch", SENCHA_TOUCH, "s");
if (fw != null) {
webapp.setSenchaTouch((SenchaTouch) fw);
return true;
} else {
return false;
}
}
private JavaScriptFramework getNewFramework(Webapp webapp, String newFolder,
Map<String,?> fws, String title, String target, String option) {
if (fws.size() == 0) {
logService.log(Log.ERROR, "No " + title + " folders available",
"Your environment does not have any " + title + " folders configured." + CR +
"In order to set the " + title + " folder to use with this webapp, you" + CR +
"Need to first add it to your webapp folder by doing one of the following:" + CR +
TAB + "- Manually placing the " + title + " folder in the weblib folder" + CR +
TAB + "- Run \"adaptrex install " + target + "\" to automatically " + CR +
TAB + " download and install " + title + "."
);
return null;
}
JavaScriptFramework fw = null;
if (newFolder.equals(JavaScriptFramework.DEFAULT)) {
if (fws.size() > 1) {
List<String> tryAgain = new ArrayList<String>();
for (String key : fws.keySet()) {
tryAgain.add(TAB +
"adaptrex configure webapp -p=\"" + webapp.getFullPath() + "\"" +
" -" + option + "=" + key);
}
logService.log(Log.ERROR, "Multiple " + title + " folders available",
"Adaptrex tools can't determine which " + title + " folder you want to use." + CR +
"Specify the correct folder by running one of the following:" + CR +
StringUtils.join(tryAgain, CR)
);
return null;
} else {
fw = (JavaScriptFramework) fws.get(fws.keySet().iterator().next());
}
} else {
fw = (JavaScriptFramework) fws.get(newFolder);
if (fw == null) {
List<String> tryAgain = new ArrayList<String>();
for (String key : fws.keySet()) {
tryAgain.add(TAB +
"adaptrex configure webapp -n=\"" + webapp.getName() + "\"" +
" -" + option + "=" + key);
}
logService.log(Log.ERROR, title + " folders not found",
"No " + title + " folder was found at " + newFolder + CR +
"Specify the correct folder by running one of the following:" + CR +
StringUtils.join(tryAgain, CR)
);
return null;
}
}
logService.log(Log.SUCCESS, title + " set to " + fw.getFolderName());
return fw;
}
@Override
public void saveWebapp(Webapp webapp) throws IOException {
File configFile = new File(webapp.getFullPath() + "/src/main/webapp/adaptrex/app.config");
if (!configFile.exists()) {
File adaptrexFolder = new File(webapp.getFullPath() + "/src/main/webapp/adaptrex");
adaptrexFolder.mkdirs();
configFile.createNewFile();
}
Ini ini = new Ini();
ini.load(new FileReader(configFile));
Ini.Section settingsSection = ini.get(SETTINGS);
if (settingsSection == null) settingsSection = ini.add(SETTINGS);
settingsSection.put(NAME, webapp.getName());
settingsSection.put(BASE_PACKAGE, webapp.getBasePackage());
settingsSection.put(GLOBAL_NAMESPACE, webapp.getGlobalNamespace());
settingsSection.put(GLOBAL_FOLDER, webapp.getGlobalFolder());
Ini.Section frameworksSection = ini.get(FRAMEWORKS);
if (frameworksSection == null) frameworksSection = ini.add(FRAMEWORKS);
if (webapp.getAdaptrex() != null)
frameworksSection.put(ADAPTREXJS, webapp.getAdaptrex().getFolderName());
if (webapp.getExt() != null)
frameworksSection.put(EXTJS, webapp.getExt().getFolderName());
if (webapp.getSenchaTouch() != null)
frameworksSection.put(SENCHA_TOUCH, webapp.getSenchaTouch().getFolderName());
if (webapp.getOrm() != null)
frameworksSection.put(ORM, webapp.getOrm());
if (webapp.getPresentation() != null)
frameworksSection.put(PRESENTATION, webapp.getPresentation());
if (webapp.getDi() != null)
frameworksSection.put(DI, webapp.getDi());
Ini.Section pagesSection = ini.get(PAGES);
if (pagesSection == null) pagesSection = ini.add(PAGES);
pagesSection.clear();
Map<String,Page> pages = webapp.getPages();
for (String key : pages.keySet()) {
Page page = pages.get(key);
pagesSection.put(key, page.getNamespace() + "," + page.getFrameworkType());
}
ini.store(configFile);
}
}