com.hortonworks.amstore.view.AmbariStoreServlet.java Source code

Java tutorial

Introduction

Here is the source code for com.hortonworks.amstore.view.AmbariStoreServlet.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hortonworks.amstore.view;

import org.apache.ambari.view.ViewContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hortonworks.amstore.view.AmbariEndpoint.ServicesConfiguration;
import com.hortonworks.amstore.view.StoreException.CODE;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import static org.apache.commons.lang.StringEscapeUtils.escapeHtml;

/**
 * Servlet for Ambari Store view.
 */
public class AmbariStoreServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    private final static Logger LOG = LoggerFactory.getLogger(AmbariStoreServlet.class);

    private ViewContext viewContext;
    private boolean endpointIssues = true; // we assume the worst

    // The StoreApplication representing the store instance (ie "this")
    private MainStoreApplication mainStoreApplication = null;

    private PrintWriter writer = null;
    private List<StoreException> latestExceptions = new LinkedList<StoreException>();

    /*
     * List of all available Store Applications. Refreshed on load and when user
     * hits 'Check for Updates' Indexed on app_id
     */

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        ServletContext context = config.getServletContext();
        viewContext = (ViewContext) context.getAttribute(ViewContext.CONTEXT_ATTRIBUTE);

        // main Store Instance (this)
        mainStoreApplication = new MainStoreApplication(viewContext);
    }

    // Security
    protected boolean isAdmin() {
        try {
            return mainStoreApplication.getAmbariLocal().isAdmin(viewContext.getUsername());
        } catch (IOException e) {
            // when in doubt return false
            LOG.warn("IOException getting admin status for user " + viewContext.getUsername());
            return false;
        }
    }

    // GET
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        response.setStatus(HttpServletResponse.SC_OK);
        // very important, used throughout
        writer = response.getWriter();

        // Reload all settings in case they changed
        mainStoreApplication.reloadConfiguration();

        bootstrapjs(response);

        writer.println("<h2>Ambari Store</h2>");
        displayExceptions(latestExceptions);
        latestExceptions = new LinkedList<StoreException>();
        try {

            // TODO: remove use of global variable endpointIssues
            if (endpointIssues) {
                displayChecks(request, response);
                // If we still have issues after the checks
                if (endpointIssues)
                    return;
            }

            if (request.getParameter("app_id") != null) {
                displayApplicationDetails(request.getParameter("app_id"), response);
                return;
            }

            displayAllApplications(request, response);
            // displayInstalledServicesInformation();

        } catch (NullPointerException e) {
            writer.println("NullPointerException caught.<br>");
            writer.println(GenericException.prettyTrace(e));

        } catch (Exception e) {
            writer.println("Catch All Exception: " + e.toString() + "<br/>");
            writer.println(org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(e));
        }

    }

    // POST - ROUTING
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html");
        response.setStatus(HttpServletResponse.SC_OK);
        // very important, used throughout
        writer = response.getWriter();

        // Duplicates Get()
        bootstrapjs(response);

        writer.println("<h2>Ambari Store</h2>");

        displayExceptions(latestExceptions);
        latestExceptions = new LinkedList<StoreException>();
        try {
            if (!isAdmin())
                throw new org.apache.ambari.view.SecurityException("User is not an administrator. Cannot post.");

            if (endpointIssues) {
                displayChecks(request, response);
                // If we still have issues after the checks
                if (endpointIssues)
                    return;
            }

            String action = null;

            if (request.getParameter("install") != null)
                action = "install";
            else if (request.getParameter("update") != null)
                action = "update";
            else if (request.getParameter("delete") != null)
                action = "delete";
            else if (request.getParameter("uninstall") != null)
                action = "uninstall";
            else if (request.getParameter("restart_ambari") != null)
                action = "restart_ambari";
            else if (request.getParameter("reconfigure") != null)
                action = "reconfigure";
            else if (request.getParameter("cleartasks") != null)
                action = "cleartasks";
            else if (request.getParameter("reconfigurepage") != null)
                action = "reconfigurepage";
            else if (request.getParameter("check_updates") != null)
                action = "check_updates";
            else if (request.getParameter("post_install") != null)
                action = "post_install";
            else if (request.getParameter("home") != null)
                action = "home";

            if (action.equals("reconfigurepage")) {
                displayPreferences(request, response);
            } else if (action.equals("reconfigure")) {
                reconfigure(request, response);
                displayPreferences(request, response);
            } else if (action.equals("restart_ambari")) {
                restartAmbari(response);
            } else if (action.equals("home")) {
                redirectHome(response);
            } else if (action.equals("cleartasks")) {
                mainStoreApplication.cleartasks();
                redirectHome(response);
            } else if (action.equals("check_updates")) {
                mainStoreApplication.getStoreEndpoint().refresh();
                displayAllApplications(request, response);
            } else if (action.equals("post_install")) {
                if (mainStoreApplication.getPostUpdateTasks().size()
                        + mainStoreApplication.getPostInstallTasks().size() == 0)
                    redirectHome(response);
                List<StoreException> updateExceptions = new LinkedList<StoreException>();
                List<StoreException> installExceptions = new LinkedList<StoreException>();
                ;
                try {
                    updateExceptions = mainStoreApplication.doPostUpdateTasks();
                    installExceptions = mainStoreApplication.doPostInstallTasks();
                    writer.println("Please refresh your browser.");
                } catch (GenericException e) {
                    writer.println("Could not proceed. Make sure you have restarted Ambari");
                }
                if (mainStoreApplication.getPostUpdateTasks().size()
                        + mainStoreApplication.getPostInstallTasks().size() != 0)
                    writer.println(
                            "Not all tasks completed. Make sure you have restarted Ambari. Services must be installed manually.");
                displayExceptions(updateExceptions);
                displayExceptions(installExceptions);
            } else { // Process checked apps
                String[] checked = null;
                if (request.getParameter("checked") != null) {

                    checked = request.getParameterValues("checked");

                    if (action.equals("install")) {
                        List<StoreException> exceptions = doInstallation(checked);
                        latestExceptions.addAll(exceptions);
                    } else if (action.equals("update")) {
                        List<StoreException> exceptions = doUpdate(checked);
                        latestExceptions.addAll(exceptions);
                    } else if (action.equals("delete")) {
                        List<StoreException> exceptions = doDelete(checked);
                        latestExceptions.addAll(exceptions);
                    } else if (action.equals("uninstall")) {
                        List<StoreException> exceptions = doUninstall(checked);
                        latestExceptions.addAll(exceptions);
                    }
                }
                redirectHome(response);
            }

        } catch (org.apache.ambari.view.SecurityException e) {
            writer.println("Error: " + e.getMessage() + "<br>");
        } catch (GenericException e) {
            writer.println("Warning: " + e.getMessage() + "<br>");
            displayAllApplications(request, response);
        } catch (NullPointerException e) {
            writer.println("NullPointerException caught.<br>");
            writer.println(GenericException.prettyTrace(e));
        } catch (Exception e) {
            writer.println("Catch All Exception: test == " + e.getMessage());
            writer.println(org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(e));
        }

    }

    protected void displayApplicationDetails(String app_id, HttpServletResponse response) throws IOException {

        PrintWriter writer = response.getWriter();

        // TODO: simplify the next 2 statements
        Map<String, StoreApplication> availableApplications = mainStoreApplication.getStoreEndpoint()
                .getAllApplicationsFromStore();
        StoreApplication app = BackendStoreEndpoint
                .netgetApplicationFromStoreByUri(availableApplications.get(app_id).uri);

        writer.println("<table class=table>");
        writer.println("<tr><td>Display Name</td><td><b>" + app.instanceDisplayName + "</b></td></tr>");
        writer.println("<tr></tr>");
        writer.println("<tr><td>app_id</td><td>" + app.app_id + "</td></tr>");
        writer.println("<tr><td>Name</td><td>" + app.getName() + "</td></tr>");
        writer.println("<tr><td>Version</td><td>" + app.version + "</td></tr>");
        writer.println("<tr><td>Instance Name</td><td>" + app.instanceName + "</td></tr>");
        writer.println("<tr><td>Download link</td><td>" + app.getPackage_uri() + "</td></tr>");
        writer.println("<tr><td>Readiness</td><td>" + app.readiness + "</td></tr>");
        writer.println("<tr><td>Contributed by</td><td>" + escapeHtml(app.getContributor()) + "</td></tr>");
        writer.println("<tr><td>Packaged by</td><td>" + escapeHtml(app.getPackager()) + "</td></tr>");
        writer.println("<tr><td>Description</td><td>" + app.description + "</td></tr>");
        writer.println("<tr><td>Homepage</td><td><a target='_blank' href='" + app.homepage + "'>" + app.homepage
                + "</a></td></tr>");
        writer.println("<tr><td>Properties</td></tr>");
        writer.println("<tr></tr>");

        try {
            Map<String, String> mapped = mainStoreApplication.getMappedProperties(app);
            for (Entry<String, String> e : app.getBackendProperties().entrySet()) {
                writer.println("<tr><td>" + e.getKey() + "</td><td>" + e.getValue() + "</td><td>"
                        + mapped.get(e.getKey()) + "</td></tr>");
            }
        } catch (StoreException e) {
            writer.println("<tr><td>Exception:" + e.getMessage() + "</td></tr>");
        }
        writer.println("</table>");

    }

    // list all amstore applications
    private void displayAllApplications(HttpServletRequest request, HttpServletResponse response)
            throws IOException {

        PrintWriter writer = response.getWriter();
        // Post Install Tasks
        List<String> updateTasks = mainStoreApplication.getPostUpdateTasks();
        List<String> installTasks = mainStoreApplication.getPostInstallTasks();

        Set<String> allTasks = new HashSet<String>(updateTasks);
        allTasks.addAll(installTasks);

        String disabledhtml = "";
        if (!isAdmin()) {
            writer.println("Welcome " + viewContext.getUsername() + ". You are in read-only mode.");
            disabledhtml = "disabled";
        }

        if (allTasks.size() != 0) {
            writer.println(
                    "<h3>Update steps remaining.</h3> After restarting Ambari, click \"Finish Installations\" to complete the installation. The following applications need a restart or finalize:");
            writer.println("<br><table class=table><tr>");
            for (String uri : allTasks) {
                StoreApplication application = BackendStoreEndpoint.netgetApplicationFromStoreByUri(uri);
                writer.println("<td>" + application.instanceDisplayName + "</td>");
            }
            writer.println("</tr></table>");
        }

        // TODO: unsafe. Indexed by canonicalName
        Map<String, StoreApplication> installedApplications = mainStoreApplication.getInstalledApplications();

        writer.println("<form name=\"input\" method=\"POST\">");

        writer.println(
                "<input type=\"submit\" value=\"Check for updates\" name=\"check_updates\" " + disabledhtml + "/>");
        writer.println(
                "<input type=\"submit\" value=\"Restart Ambari\" name=\"restart_ambari\" " + disabledhtml + "/>");
        writer.println("<input type=\"submit\" value=\"Finish installations\" name=\"post_install\" " + disabledhtml
                + "/>");
        writer.println("<p></p>");

        writer.println("<div class=\"panel panel-success\">\n" + "  <!-- Default panel contents -->\n"
                + "  <div class=\"panel-heading\">All Applications</div>\n" + "  <div class=\"panel-body\">\n"
                + "    <p> Please select the applications you want to install. </p>\n" + "  </div>");

        writer.println("<table class=table>");
        writer.println("<tr>");
        writer.println("<th>Category</th>");
        writer.println("<th>Name</th>");
        writer.println("<th>Version</th>");
        writer.println("<th>Description</th>");
        writer.println("<th>Type</th>");
        writer.println("<th>Readiness</th>");
        writer.println("<th>Installed</th>");
        writer.println("<th></th>");
        writer.println("<th>Select</th>");
        writer.println("</tr>");

        Map<String, StoreApplication> availableApplications = mainStoreApplication.getStoreEndpoint()
                .getAllApplicationsFromStore();

        for (Map.Entry<String, StoreApplication> e : availableApplications.entrySet()) {
            StoreApplication app = e.getValue();
            writer.println("<tr>");
            writer.println("<td>" + app.category + "</td>");

            writer.println("<td><a href='?app_id=" + app.app_id + "'>" + app.instanceDisplayName + "</a></td>");

            writer.println("<td>");
            writer.println(app.version);
            writer.println("</td>");
            writer.println("<td>" + app.description + "</td>");
            writer.println("<td>" + app.getType() + "</td>");
            writer.println("<td>");
            writer.println(app.readiness);
            writer.println("</td>");

            writer.println("<td>");
            if (installedApplications.containsKey(app.getCanonicalName())) {
                writer.println(installedApplications.get(app.getCanonicalName()).getVersion());
            }
            writer.println("</td>");

            writer.println("<td>");
            try {
                if (installedApplications.containsKey(app.getCanonicalName())
                        && installedApplications.get(app.getCanonicalName()).compareTo(app) < 0) {
                    writer.println("update");
                }
            } catch (Exception ex) {
                // Update not implemented
            }
            writer.println("</td>");

            writer.println("<td align='center'>");
            if (isAdmin())
                writer.println("<input type='checkbox' name='checked' value='" + app.app_id + "'>");
            writer.println("</td>");
            writer.println("</tr>");
        }

        writer.println("</table>");
        writer.println("</div>");
        writer.println("<br/>");
        writer.println("<input " + disabledhtml + " type=\"submit\" value=\"Install Selected\" name=\"install\"/>");
        writer.println(
                "<input disabled type=\"submit\" value=\"Update Selected (not implemented)\" name=\"update\"/>");
        writer.println("<input " + disabledhtml + " type=\"submit\" value=\"Delete Selected\" name=\"delete\"/>");
        writer.println(
                "<input " + disabledhtml + " type=\"submit\" value=\"Uninstall Selected\" name=\"uninstall\"/>");
        writer.println("<input " + disabledhtml
                + " type=\"submit\" value=\"Reconfigure Store\" name=\"reconfigurepage\">");
        writer.println("</form>");

        // writer.println("<br>");
        // displayInstalledApplicationInformation(response);
    }

    private void displayPreferences(HttpServletRequest request, HttpServletResponse response) throws IOException {

        Map<String, String> data = new TreeMap<String, String>(viewContext.getProperties());

        PrintWriter writer = response.getWriter();

        writer.println("<form name=\"preferences\" method=\"POST\">");
        writer.println("<table><tr><td>");
        writer.println("<h1>Current Preferences</h1>");
        writer.println("<table border=\"1\" style=\"width:300px\">");
        writer.println("<tr>");
        writer.println("<td>Property</td>");
        writer.println("<td>Value</td>");
        writer.println("</tr>");
        for (Map.Entry<String, String> entry : data.entrySet()) {
            writer.println("<tr>");
            writer.println("<td>" + entry.getKey() + "</td>");
            writer.println("<td>" + entry.getValue() + "</td>");
            writer.println("</tr>");
        }
        writer.println("</table>");
        writer.println("</td><td valign='top'>");

        Map<String, String> amstore = new LinkedHashMap<String, String>();
        try {
            amstore = mainStoreApplication.netgetUpdatedStoreProperties();
        } catch (Exception e) {
            writer.println("Exception getting settings:" + e.getMessage());
        }

        writer.println("<h1>Proposed Configuration</h1>");

        writer.println("<table border=\"1\" style=\"width:300px\">");
        writer.println("<tr>");
        writer.println("<td>Property</td>");
        writer.println("<td>Value</td>");
        writer.println("</tr>");
        // Get Inferred values
        for (Map.Entry<String, String> entry : amstore.entrySet()) {
            writer.println("<tr>");
            writer.println("<td>" + entry.getKey() + "</td>");
            writer.println("<td>" + entry.getValue() + "</td>");
            writer.println("</tr>");
        }
        writer.println("</table>");

        writer.println("<tr><td colspan=2 align=center>");
        writer.println("<input type=\"submit\" value=\"Reconfigure\" name=\"reconfigure\">");
        writer.println("</td></tr>");

        writer.println("<tr><td colspan=2 align=center>");
        writer.println(
                "<input disabled type=\"submit\" value=\"Apply to all Installed Applications (not implemented)\" name=\"reconfigureapps\">");
        writer.println("</td></tr>");

        writer.println("<tr><td colspan=2 align=center>");
        writer.println("<input type=\"submit\" value=\"Clear tasks\" name=\"cleartasks\">");
        writer.println("</td></tr>");

        writer.println("<tr><td colspan=2 align=center>");
        writer.println("<input type=\"submit\" value=\"Return home\" name=\"home\">");
        writer.println("</td></tr>");

        writer.println("</table>");

        writer.println("</form>");
    }

    /*
     * Reconfigure the Ambari Store Properties, based on configured Ambari
     * Server Endpoints
     */
    protected void reconfigure(HttpServletRequest request, HttpServletResponse response) throws IOException {
        PrintWriter writer = response.getWriter();

        try {
            mainStoreApplication.reconfigure();
            writer.println("<h2> Successful reconfigure </h2>");
        } catch (Exception e) {
            writer.println("<h2> Reconfiguration failure</h2>");
            writer.println("Exception reconfiguring:" + e.getMessage());
        }
    }

    // TODO: hopefully we can remove this soon
    // WARNING: hardcoded url
    protected void restartAmbari(HttpServletResponse response) throws IOException {
        PrintWriter writer = response.getWriter();

        try {
            String output = mainStoreApplication.getAmbariViews().restartAmbari();
            writer.println(output);
            writer.println(waitForAmbariHtml());
        } catch (IOException e) {
            writer.println("Error: Ambari restart daemon unreachable. Please restart Ambari manually. <br>"
                    + "http://localhost:5026/amstore/restart-ambari: " + e.getMessage());
        }
    }

    // We get called if endpointChecks == false
    private void displayChecks(HttpServletRequest request, HttpServletResponse response) throws IOException {

        PrintWriter writer = response.getWriter();
        boolean issue = false;

        if (!mainStoreApplication.getStoreEndpoint().isAvailable()) {
            displayStoreEndpointHelp(request, response);
            issue = true;
        }

        if (!mainStoreApplication.getAmbariViews().isAvailable()) {
            writer.println("<h1>Error connecting to local Ambari</h1>");
            writer.println("Issue connecting to Ambari Views Server. Please verify connectivity to "
                    + mainStoreApplication.getAmbariViews().getUrl() + "<br>");
            issue = true;
        }

        if (mainStoreApplication.getAmbariRemote() != null
                && !mainStoreApplication.getAmbariRemote().isAvailable()) {
            writer.println("<h1>Error connecting to remote Ambari</h1>");
            writer.println("Issue connecting to Ambari Remote Server. Please verify connectivity to "
                    + mainStoreApplication.getAmbariRemote().getUrl() + "<br>");
            issue = true;
        }

        if (mainStoreApplication.getAmbariCluster().isAvailable()
                && !mainStoreApplication.getAmbariCluster().isCluster()) {
            writer.println("<h1>No Hadoop Cluster found.</h1>");
            writer.println(
                    "You do not have any configured Ambari Cluster Servers. If your local Ambari is a Views only server, please specify an additional Ambari in the store configuration. <br>");
            writer.println(mainStoreApplication.getAmbariCluster().getUrl() + " is not managing a cluster.");
            issue = true;
        }

        if (!issue)
            endpointIssues = false;
    }

    // What to do if endpoint not available.
    private void displayStoreEndpointHelp(HttpServletRequest request, HttpServletResponse response)
            throws IOException {

        PrintWriter writer = response.getWriter();
        writer.println("<h1>Unable to access Ambari Store</h1>");
        writer.println(
                "Please verify connectivity to " + mainStoreApplication.getStoreEndpoint().getUrl() + ".<br>");
        writer.println("The store URL can be changed in the view configuration.<br>");
    }

    // For debug
    private void displayInstalledApplicationInformation(HttpServletResponse response) throws IOException {
        PrintWriter writer = response.getWriter();
        Map<String, StoreApplication> installedApplications = mainStoreApplication.getInstalledApplications();
        writer.println("<table>");
        for (Entry<String, StoreApplication> app : installedApplications.entrySet()) {
            writer.println("<tr>");
            writer.println("<td>" + app.getKey() + "</td>");
            if (app.getValue().isView())
                writer.println("<td>" + ((StoreApplicationView) app).getViewName() + "</td>");
            else
                writer.println("<td>" + ((StoreApplicationService) app.getValue()).getServiceName() + "</td>");

            writer.println("<td>" + app.getValue().getInstanceName() + "</td>");
            writer.println("</tr>");
        }
        writer.println("</table>");
    }

    // For debug
    private void displayInstalledServicesInformation() throws IOException {
        AmbariEndpoint.ServicesConfiguration servicesConfiguration = mainStoreApplication.getAmbariCluster()
                .getServicesConfiguration();
        writer.println("<table>");

        for (Entry<String, AmbariEndpoint.ServiceConfiguration> s : servicesConfiguration.getMap().entrySet()) {
            writer.println("<tr>");
            writer.println("<td>" + s.getKey() + "</td>");
            writer.println("<td>");

            writer.println("<table>");
            for (Entry<String, String> e : s.getValue().getMap().entrySet()) {
                writer.println("<tr>");
                writer.println("<td>" + e.getKey() + "</td>");
                writer.println("<td>" + e.getValue() + "</td>");
                writer.println("</tr>");
            }
            writer.println("</table>");

            writer.println("</td>");
            writer.println("<tr>");

        }
        writer.println("</table>");
    }

    private void displayExceptions(List<StoreException> exceptions) {
        for (StoreException e : exceptions) {
            // Only display errors or warnings
            if (e.getCode() == CODE.ERROR || e.getCode() == CODE.WARNING) {
                writer.println("<br><pre>" + "Exception:\n" + e.getMessage() + "</pre>");
            }
        }
    }

    private List<StoreException> doInstallation(String[] app_ids) {
        List<StoreException> exceptions = new LinkedList<StoreException>();
        if (app_ids == null || app_ids.length == 0)
            return exceptions;
        LOG.debug("Starting Installations.\n");

        for (String app_id : app_ids) {
            try {
                mainStoreApplication.installApplication(app_id);
            } catch (StoreException e) {
                exceptions.add(e);
            }
        }
        List<StoreException> postInstallExceptions = mainStoreApplication.doPostInstallTasks();
        exceptions.addAll(postInstallExceptions);
        return exceptions;
    }

    private List<StoreException> doUpdate(String[] app_ids) {
        List<StoreException> exceptions = new LinkedList<StoreException>();
        if (app_ids == null || app_ids.length == 0)
            return exceptions;
        LOG.debug("Starting Updates.\n");

        for (String app_id : app_ids) {
            try {
                mainStoreApplication.updateApplication(app_id);
            } catch (IOException e) {
                // TODO: we ignore any issues
            } catch (StoreException e) {
                exceptions.add(e);
            }
        }
        return exceptions;
    }

    // deletes instance
    // TODO: we ignore all exceptions
    private List<StoreException> doDelete(String[] app_ids) throws IOException {
        List<StoreException> exceptions = new LinkedList<StoreException>();
        if (app_ids == null || app_ids.length == 0)
            return exceptions;
        LOG.debug("Starting deletions.\n");
        for (String app_id : app_ids) {
            try {
                mainStoreApplication.deinstantiateApplication(app_id);
            } catch (StoreException e) {
                exceptions.add(e);
            }
        }
        return exceptions;
    }

    // Like doDelete, deletes instance but also all associated files.
    // TODO: add exceptions
    private List<StoreException> doUninstall(String[] app_ids) {
        List<StoreException> exceptions = new LinkedList<StoreException>();
        if (app_ids == null || app_ids.length == 0)
            return exceptions;
        LOG.debug("Starting Uninstall.\n");
        for (String app_id : app_ids) {
            try {
                mainStoreApplication.uninstallApplication(app_id);
            } catch (IOException e) {
                // TODO: we ignore any issues
            } catch (StoreException e) {
                exceptions.add(e);
            }
        }
        return exceptions;
    }

    protected void redirectHome(HttpServletResponse response) throws IOException {
        response.sendRedirect("");
    }

    protected void bootstrapjs(HttpServletResponse response) throws IOException {
        PrintWriter writer = response.getWriter();
        writer.println("      <!-- Latest compiled and minified CSS -->\n"
                + "   <head>   <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css\">\n"
                + "\n" + "      <!-- Optional theme -->\n"
                + "      <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css\">\n"
                + "\n" + "      <!-- Latest compiled and minified JavaScript -->\n"
                + "      <script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js\"></script>\n"
                + "</head>");
    }

    /*
     * Outputs Javascript that waits for Ambari to restart. Will be part of
     * proper template once we move to Ember.
     */
    protected String waitForAmbariHtml() {
        String html = "<div id='display'></div>"
                + "<script src=\"http://code.jquery.com/jquery-1.11.1.min.js\"></script>" + "<script>\n"
                + "    function doPoll(){\n" + "       $.ajax({\n" + "          url: '/api/v1/',\n"
                + "success: function(result){\n"
                + " $('#display').html('Ambari ready. Please refresh your browser');"
                + "         alert('Ambari is ready. Please refresh your browser')\n" + "          },     \n"
                + "          error: function(result){\n"
                + "          if( result.status == 403 ) { $('#display').html('<font color=red>Ambari ready. Please refresh your browser</font>'); }    "
                + "              //alert('timeout/error');\n"
                + "          else {$('#display').html('Ambari is restarting. Please wait ...');"
                + "           setTimeout(doPoll,1000);}\n" + "          }\n" + "       });\n" + "    }\n"
                + "   setTimeout(doPoll,5000);" + "</script>\n" + "";
        return html;
    }

}