Java tutorial
/*************************************** * Copyright (c) 2008-2010 * Philipp Berger 2009 * Intalio, Inc 2010 * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************/ package org.wapama.web; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.WeakHashMap; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.Namespace; import org.jdom.input.SAXBuilder; import org.jdom.output.Format; import org.jdom.output.XMLOutputter; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.javascript.EvaluatorException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wapama.web.plugin.IDiagramPlugin; import org.wapama.web.plugin.IDiagramPluginService; import org.wapama.web.plugin.impl.PluginServiceImpl; import org.wapama.web.preference.IDiagramPreference; import org.wapama.web.preference.IDiagramPreferenceService; import org.wapama.web.profile.IDiagramProfile; import org.wapama.web.profile.IDiagramProfileService; import org.wapama.web.profile.impl.ProfileServiceImpl; import com.yahoo.platform.yui.compressor.JavaScriptCompressor; /** * Servlet to load plugin and Wapama stencilset */ public class EditorHandler extends HttpServlet { private static final long serialVersionUID = -7439613152623067053L; /** * da logger */ private static final Logger _logger = LoggerFactory.getLogger(EditorHandler.class); public static IDiagramPreferenceService PREFERENCE_FACTORY = new IDiagramPreferenceService() { public IDiagramPreference createPreference(HttpServletRequest req) { //later we could read the parameters from the URL as well. return new IDiagramPreference() { public boolean isAutoSaveEnabled() { return true; } public int getAutosaveInterval() { return 120000; } }; } }; /** * The base path under which the application will be made available at runtime. * This constant should be used throughout the application. */ public static final String wapama_path = "/designer/"; /** * The designer DEV flag. * When set, the logging will be enabled at the javascript level */ public static final String DEV = "designer.dev"; /** * The profile service, a global registry to get the * profiles. */ private IDiagramProfileService _profileService = null; /** * The plugin service, a global registry for all plugins. */ private IDiagramPluginService _pluginService = null; private List<String> _envFiles = new ArrayList<String>(); private Map<String, List<IDiagramPlugin>> _pluginfiles = new HashMap<String, List<IDiagramPlugin>>(); private Map<String, List<IDiagramPlugin>> _uncompressedPlugins = new WeakHashMap<String, List<IDiagramPlugin>>(); /** * editor.html document. */ private Document _doc = null; public void init(ServletConfig config) throws ServletException { super.init(config); _profileService = ProfileServiceImpl.INSTANCE; _profileService.init(config.getServletContext()); _pluginService = PluginServiceImpl.getInstance(config.getServletContext()); String editor_file = config.getServletContext().getRealPath("/editor.html"); try { _doc = readDocument(editor_file); } catch (Exception e) { throw new ServletException("Error while parsing editor.html", e); } if (_doc == null) { _logger.error("Invalid editor.html, " + "could not be read as a document."); throw new ServletException("Invalid editor.html, " + "could not be read as a document."); } Element root = _doc.getRootElement(); Element head = root.getChild("head", root.getNamespace()); if (head == null) { _logger.error("Invalid editor.html. No html or head tag"); throw new ServletException("Invalid editor.html. " + "No html or head tag"); } try { initEnvFiles(getServletContext()); } catch (IOException e) { throw new ServletException(e); } } /** * Initiate the compression of the environment. * @param context * @throws IOException */ private void initEnvFiles(ServletContext context) throws IOException { // only do it the first time the servlet starts try { JSONObject obj = new JSONObject(readEnvFiles(context)); JSONArray array = obj.getJSONArray("files"); for (int i = 0; i < array.length(); i++) { _envFiles.add(array.getString(i)); } } catch (JSONException e) { _logger.error("invalid js_files.json"); _logger.error(e.getMessage(), e); throw new RuntimeException("Error initializing the " + "environment of the editor"); } // generate script to setup the languages //_envFiles.add("i18n/translation_ja.js"); if (System.getProperty(DEV) == null) { StringWriter sw = new StringWriter(); for (String file : _envFiles) { sw.append("/* ").append(file).append(" */\n"); InputStream input = new FileInputStream(new File(getServletContext().getRealPath(file))); try { JavaScriptCompressor compressor = new JavaScriptCompressor(new InputStreamReader(input), null); compressor.compress(sw, -1, false, false, false, false); } catch (EvaluatorException e) { _logger.error(e.getMessage(), e); } catch (IOException e) { _logger.error(e.getMessage(), e); } finally { try { input.close(); } catch (IOException e) { } } sw.append("\n"); } try { FileWriter w = new FileWriter(context.getRealPath("jsc/env_combined.js")); w.write(sw.toString()); w.close(); } catch (IOException e) { _logger.error(e.getMessage(), e); } } else { if (_logger.isInfoEnabled()) { _logger.info("The diagram editor is running in development mode. " + "Javascript will be served uncompressed"); } } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Document doc = (Document) _doc.clone(); String profileName = request.getParameter("profile"); IDiagramProfile profile = _profileService.findProfile(request, profileName); if (profile == null) { _logger.error("No profile with the name " + profileName + " was registered"); throw new IllegalArgumentException("No profile with the name " + profileName + " was registered"); } //output env javascript files if (System.getProperty(DEV) != null) { for (String jsFile : _envFiles) { addScript(doc, wapama_path + jsFile, true); } } else { addScript(doc, wapama_path + "jsc/env_combined.js", true); } // get language from cookie String lang = null; Cookie[] cookies = request.getCookies(); for (Cookie cookie : cookies) { if ("locale".equals(cookie.getName())) { lang = cookie.getValue().trim(); break; } } // i18n message resource file name String i18nJsFile = null; if ("de_DE".equals(lang)) { i18nJsFile = "i18n/translation_de.js"; } else if ("ru".equals(lang)) { i18nJsFile = "i18n/translation_ru.js"; } else if ("es".equals(lang)) { i18nJsFile = "i18n/translation_es.js"; } else if ("ja_JP".equals(lang)) { i18nJsFile = "i18n/translation_ja.js"; } else { i18nJsFile = "i18n/translation_en_us.js"; } // generate script to setup the languages addScript(doc, wapama_path + i18nJsFile, true); // generate script tags for plugins. // they are located after the initialization script. if (_pluginfiles.get(profileName) == null) { List<IDiagramPlugin> compressed = new ArrayList<IDiagramPlugin>(); List<IDiagramPlugin> uncompressed = new ArrayList<IDiagramPlugin>(); _pluginfiles.put(profileName, compressed); _uncompressedPlugins.put(profileName, uncompressed); for (String pluginName : profile.getPlugins()) { IDiagramPlugin plugin = _pluginService.findPlugin(request, pluginName); if (plugin == null) { _logger.warn("Could not find the plugin " + pluginName + " requested by the profile " + profile.getName()); continue; } if (plugin.isCompressable()) { compressed.add(plugin); } else { uncompressed.add(plugin); } } if (System.getProperty(DEV) == null) { // let's call the compression routine String rs = compressJS(_pluginfiles.get(profileName), getServletContext()); try { FileWriter w = new FileWriter( getServletContext().getRealPath("jsc/plugins_" + profileName + ".js")); w.write(rs.toString()); w.close(); } catch (Exception e) { _logger.error(e.getMessage(), e); } } } if (System.getProperty(DEV) != null) { for (IDiagramPlugin jsFile : _pluginfiles.get(profileName)) { addScript(doc, wapama_path + "plugin/" + jsFile.getName() + ".js", true); } } else { addScript(doc, wapama_path + "jsc/plugins_" + profileName + ".js", false); } for (IDiagramPlugin uncompressed : _uncompressedPlugins.get(profileName)) { addScript(doc, wapama_path + "plugin/" + uncompressed.getName() + ".js", false); } // send the updated editor.html to client if (!isIE(request)) { response.setContentType("application/xhtml+xml"); } XMLOutputter outputter = new XMLOutputter(); Format format = Format.getPrettyFormat(); format.setExpandEmptyElements(true); outputter.setFormat(format); String html = outputter.outputString(doc); StringTokenizer tokenizer = new StringTokenizer(html, "@", true); StringBuilder resultHtml = new StringBuilder(); boolean tokenFound = false; boolean replacementMade = false; IDiagramPreference pref = PREFERENCE_FACTORY.createPreference(request); int autoSaveInt = pref.getAutosaveInterval(); boolean autoSaveOn = pref.isAutoSaveEnabled(); while (tokenizer.hasMoreTokens()) { String elt = tokenizer.nextToken(); if ("title".equals(elt)) { resultHtml.append(profile.getTitle()); replacementMade = true; } else if ("stencilset".equals(elt)) { resultHtml.append(profile.getStencilSet()); replacementMade = true; } else if ("debug".equals(elt)) { resultHtml.append(System.getProperty(DEV) != null); replacementMade = true; } else if ("autosaveinterval".equals(elt)) { resultHtml.append(autoSaveInt); replacementMade = true; } else if ("autosavedefault".equals(elt)) { resultHtml.append(autoSaveOn); replacementMade = true; } else if ("profileplugins".equals(elt)) { StringBuilder plugins = new StringBuilder(); boolean commaNeeded = false; for (String ext : profile.getPlugins()) { if (commaNeeded) { plugins.append(","); } else { commaNeeded = true; } plugins.append("\"").append(ext).append("\""); } resultHtml.append(plugins.toString()); replacementMade = true; } else if ("ssextensions".equals(elt)) { StringBuilder ssexts = new StringBuilder(); boolean commaNeeded = false; for (String ext : profile.getStencilSetExtensions()) { if (commaNeeded) { ssexts.append(","); } else { commaNeeded = true; } ssexts.append("\"").append(ext).append("\""); } resultHtml.append(ssexts.toString()); replacementMade = true; } else if ("@".equals(elt)) { if (replacementMade) { tokenFound = false; replacementMade = false; } else { tokenFound = true; } } else { if (tokenFound) { tokenFound = false; resultHtml.append("@"); } resultHtml.append(elt); } } response.getWriter().write(resultHtml.toString()); } /** * Reads the document from the file at the given path * @param path the path to the file * @return a document * @throws JDOMException * @throws IOException */ private static Document readDocument(String path) throws JDOMException, IOException { SAXBuilder builder = new SAXBuilder(false); // no DTD validation builder.setValidation(false); builder.setFeature("http://xml.org/sax/features/validation", false); builder.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false); builder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); Document anotherDocument = builder.build(new File(path)); return anotherDocument; } /** * Adds a script to the head. * @param doc the document to use. * @param src the location of the script */ private void addScript(Document doc, String src, boolean isCore) { Namespace nm = doc.getRootElement().getNamespace(); Element script = new Element("script", nm); // set the attributes script.setAttribute("src", src); script.setAttribute("type", "text/javascript"); //add an empty text node in them script.addContent(""); // put it to the right place Element head = doc.getRootElement().getChild("head", nm); if (isCore) { // then place it first. //insert before the last script tag. head.addContent(head.getContentSize() - 2, script); } else { head.addContent(script); } return; } /** * @return read the files to be placed as core scripts * from a configuration file in a json file. * @throws IOException */ private static String readEnvFiles(ServletContext context) throws IOException { FileInputStream core_scripts = new FileInputStream(context.getRealPath("/js/js_files.json")); try { ByteArrayOutputStream stream = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int read; while ((read = core_scripts.read(buffer)) != -1) { stream.write(buffer, 0, read); } return stream.toString(); } finally { try { core_scripts.close(); } catch (IOException e) { _logger.error(e.getMessage(), e); } } } /** * Compress a list of js files into one combined string * @param a list of js files * @return a string that contains all the compressed data * @throws EvaluatorException * @throws IOException */ private static String compressJS(Collection<IDiagramPlugin> plugins, ServletContext context) { StringWriter sw = new StringWriter(); for (IDiagramPlugin plugin : plugins) { sw.append("/* ").append(plugin.getName()).append(" */\n"); InputStream input = plugin.getContents(); try { JavaScriptCompressor compressor = new JavaScriptCompressor(new InputStreamReader(input), null); compressor.compress(sw, -1, false, false, false, false); } catch (EvaluatorException e) { _logger.error(e.getMessage(), e); } catch (IOException e) { _logger.error(e.getMessage(), e); } finally { try { input.close(); } catch (IOException e) { } } sw.append("\n"); } return sw.toString(); } /** * Determine whether the browser is IE * @param request * @return true: IE browser false: others browsers */ private static boolean isIE(HttpServletRequest request) { return request.getHeader("USER-AGENT").toLowerCase().indexOf("msie") > 0; } }