@Test
  @PrepareForTest({Encode.class})
  public void testStartExecutionTransServletEscapesHtmlWhenTransFound()
      throws ServletException, IOException {
    KettleLogStore.init();
    HttpServletRequest mockHttpServletRequest = mock(HttpServletRequest.class);
    HttpServletResponse mockHttpServletResponse = mock(HttpServletResponse.class);
    Trans mockTrans = mock(Trans.class);
    TransMeta mockTransMeta = mock(TransMeta.class);
    LogChannelInterface mockChannelInterface = mock(LogChannelInterface.class);
    StringWriter out = new StringWriter();
    PrintWriter printWriter = new PrintWriter(out);

    PowerMockito.spy(Encode.class);
    when(mockHttpServletRequest.getContextPath())
        .thenReturn(StartExecutionTransServlet.CONTEXT_PATH);
    when(mockHttpServletRequest.getParameter(anyString()))
        .thenReturn(ServletTestUtils.BAD_STRING_TO_TEST);
    when(mockHttpServletResponse.getWriter()).thenReturn(printWriter);
    when(mockTransformationMap.getTransformation(any(CarteObjectEntry.class)))
        .thenReturn(mockTrans);
    when(mockTrans.getLogChannel()).thenReturn(mockChannelInterface);
    when(mockTrans.isReadyToStart()).thenReturn(true);
    when(mockTrans.getLogChannelId()).thenReturn("test");
    when(mockTrans.getTransMeta()).thenReturn(mockTransMeta);
    when(mockTransMeta.getMaximum()).thenReturn(new Point(10, 10));

    startExecutionTransServlet.doGet(mockHttpServletRequest, mockHttpServletResponse);
    assertFalse(ServletTestUtils.hasBadText(ServletTestUtils.getInsideOfTag("H1", out.toString())));

    PowerMockito.verifyStatic(atLeastOnce());
    Encode.forHtml(anyString());
  }
  public static synchronized void init() throws KettleException {
    if (initialized != null) {
      return;
    }

    if (KettleClientEnvironment.instance == null) {
      KettleClientEnvironment.instance = new KettleClientEnvironment();
    }

    createKettleHome();

    // Read the kettle.properties file before anything else
    //
    EnvUtil.environmentInit();

    // Initialize the logging back-end.
    //
    KettleLogStore.init();

    // Add console output so that folks see what's going on...
    // TODO: make this configurable...
    //
    if (!"Y".equalsIgnoreCase(System.getProperty(Const.KETTLE_DISABLE_CONSOLE_LOGGING, "N"))) {
      KettleLogStore.getAppender().addLoggingEventListener(new ConsoleLoggingEventListener());
    }

    // Load value meta data plugins
    //
    PluginRegistry.addPluginType(LoggingPluginType.getInstance());
    PluginRegistry.addPluginType(ValueMetaPluginType.getInstance());
    PluginRegistry.addPluginType(DatabasePluginType.getInstance());
    PluginRegistry.addPluginType(ExtensionPointPluginType.getInstance());
    PluginRegistry.init(true);

    initialized = new Boolean(true);
  }
  @Test
  public void testStopTransServletEscapesHtmlWhenTransFound() throws ServletException, IOException {
    KettleLogStore.init();
    HttpServletRequest mockHttpServletRequest = mock(HttpServletRequest.class);
    HttpServletResponse mockHttpServletResponse = mock(HttpServletResponse.class);
    Trans mockTrans = mock(Trans.class);
    TransMeta mockTransMeta = mock(TransMeta.class);
    LogChannelInterface mockChannelInterface = mock(LogChannelInterface.class);
    StringWriter out = new StringWriter();
    PrintWriter printWriter = new PrintWriter(out);

    when(mockHttpServletRequest.getContextPath()).thenReturn(StopTransServlet.CONTEXT_PATH);
    when(mockHttpServletRequest.getParameter(anyString())).thenReturn(ServletTestUtils.BAD_STRING);
    when(mockHttpServletResponse.getWriter()).thenReturn(printWriter);
    when(mockTransformationMap.getTransformation(any(CarteObjectEntry.class)))
        .thenReturn(mockTrans);
    when(mockTrans.getLogChannel()).thenReturn(mockChannelInterface);
    when(mockTrans.getLogChannelId()).thenReturn("test");
    when(mockTrans.getTransMeta()).thenReturn(mockTransMeta);
    when(mockTransMeta.getMaximum()).thenReturn(new Point(10, 10));

    stopTransServlet.doGet(mockHttpServletRequest, mockHttpServletResponse);
    assertFalse(ServletTestUtils.hasBadText(ServletTestUtils.getInsideOfTag("H1", out.toString())));
  }
  /**
   * <div id="mindtouch">
   *
   * <h1>/kettle/prepareExec</h1>
   *
   * <a name="GET"></a>
   *
   * <h2>GET</h2>
   *
   * <p>Prepares previously loaded transformation for execution. Method is used for preparing
   * previously uploaded transformation for execution by its name.
   *
   * <p><b>Example Request:</b><br>
   *
   * <pre function="syntax.xml">
   * GET /kettle/prepareExec/?name=dummy-trans2&xml=Y
   * </pre>
   *
   * <h3>Parameters</h3>
   *
   * <table class="pentaho-table">
   * <tbody>
   * <tr>
   * <th>name</th>
   * <th>description</th>
   * <th>type</th>
   * </tr>
   * <tr>
   * <td>name</td>
   * <td>Name of the transformation to be prepared for execution.</td>
   * <td>query</td>
   * </tr>
   * <tr>
   * <td>xml</td>
   * <td>Boolean flag which sets the output format required. Use <code>Y</code> to receive XML response.</td>
   * <td>boolean</td>
   * </tr>
   * <tr>
   * <td>id</td>
   * <td>Carte transformation ID of the transformation to be prepared for execution.</td>
   * <td>query, optional</td>
   * </tr>
   * </tbody>
   * </table>
   *
   * <h3>Response Body</h3>
   *
   * <table class="pentaho-table">
   * <tbody>
   * <tr>
   * <td align="right">text:</td>
   * <td>HTML</td>
   * </tr>
   * <tr>
   * <td align="right">media types:</td>
   * <td>text/xml, text/html</td>
   * </tr>
   * </tbody>
   * </table>
   *
   * <p>Response XML or HTML containing operation result. When using xml=Y <code>result</code> field
   * indicates whether operation was successful (<code>OK</code>) or not (<code>ERROR</code>).
   *
   * <p><b>Example Response:</b>
   *
   * <pre function="syntax.xml">
   * <?xml version="1.0" encoding="UTF-8"?>
   * <webresult>
   * <result>OK</result>
   * <message/>
   * <id/>
   * </webresult>
   * </pre>
   *
   * <h3>Status Codes</h3>
   *
   * <table class="pentaho-table">
   * <tbody>
   * <tr>
   * <th>code</th>
   * <th>description</th>
   * </tr>
   * <tr>
   * <td>200</td>
   * <td>Request was processed.</td>
   * </tr>
   * <tr>
   * <td>500</td>
   * <td>Internal server error occurs during request processing.</td>
   * </tr>
   * </tbody>
   * </table>
   *
   * </div>
   */
  public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    if (isJettyMode() && !request.getContextPath().startsWith(CONTEXT_PATH)) {
      return;
    }

    if (log.isDebug()) {
      logDebug(
          BaseMessages.getString(
              PKG, "PrepareExecutionTransServlet.TransPrepareExecutionRequested"));
    }

    String transName = request.getParameter("name");
    String id = request.getParameter("id");
    boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));

    response.setStatus(HttpServletResponse.SC_OK);

    PrintWriter out = response.getWriter();
    if (useXML) {
      response.setContentType("text/xml");
      out.print(XMLHandler.getXMLHeader(Const.XML_ENCODING));
    } else {

      response.setCharacterEncoding("UTF-8");
      response.setContentType("text/html;charset=UTF-8");

      out.println("<HTML>");
      out.println("<HEAD>");
      out.println(
          "<TITLE>"
              + BaseMessages.getString(PKG, "PrepareExecutionTransServlet.TransPrepareExecution")
              + "</TITLE>");
      out.println(
          "<META http-equiv=\"Refresh\" content=\"2;url="
              + convertContextPath(GetTransStatusServlet.CONTEXT_PATH)
              + "?name="
              + URLEncoder.encode(transName, "UTF-8")
              + "\">");
      out.println("<META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
      out.println("</HEAD>");
      out.println("<BODY>");
    }

    try {
      // ID is optional...
      //
      Trans trans;
      CarteObjectEntry entry;
      if (Utils.isEmpty(id)) {
        // get the first transformation that matches...
        //
        entry = getTransformationMap().getFirstCarteObjectEntry(transName);
        if (entry == null) {
          trans = null;
        } else {
          id = entry.getId();
          trans = getTransformationMap().getTransformation(entry);
        }
      } else {
        // Take the ID into account!
        //
        entry = new CarteObjectEntry(transName, id);
        trans = getTransformationMap().getTransformation(entry);
      }

      TransConfiguration transConfiguration = getTransformationMap().getConfiguration(entry);

      if (trans != null && transConfiguration != null) {
        TransExecutionConfiguration executionConfiguration =
            transConfiguration.getTransExecutionConfiguration();
        // Set the appropriate logging, variables, arguments, replay date, ...
        // etc.
        trans.setArguments(executionConfiguration.getArgumentStrings());
        trans.setReplayDate(executionConfiguration.getReplayDate());
        trans.setSafeModeEnabled(executionConfiguration.isSafeModeEnabled());
        trans.setGatheringMetrics(executionConfiguration.isGatheringMetrics());
        trans.injectVariables(executionConfiguration.getVariables());
        trans.setPreviousResult(executionConfiguration.getPreviousResult());

        try {
          trans.prepareExecution(null);

          if (useXML) {
            out.println(WebResult.OK.getXML());
          } else {

            out.println(
                "<H1>"
                    + Encode.forHtml(
                        BaseMessages.getString(
                            PKG, "PrepareExecutionTransServlet.TransPrepared", transName))
                    + "</H1>");
            out.println(
                "<a href=\""
                    + convertContextPath(GetTransStatusServlet.CONTEXT_PATH)
                    + "?name="
                    + URLEncoder.encode(transName, "UTF-8")
                    + "&id="
                    + URLEncoder.encode(id, "UTF-8")
                    + "\">"
                    + BaseMessages.getString(PKG, "TransStatusServlet.BackToTransStatusPage")
                    + "</a><p>");
          }
        } catch (Exception e) {

          String logText =
              KettleLogStore.getAppender()
                  .getBuffer(trans.getLogChannel().getLogChannelId(), true)
                  .toString();
          if (useXML) {
            out.println(
                new WebResult(
                    WebResult.STRING_ERROR,
                    BaseMessages.getString(
                        PKG,
                        "PrepareExecutionTransServlet.Error.TransInitFailed",
                        Const.CR + logText + Const.CR + Const.getStackTracker(e))));
          } else {
            out.println(
                "<H1>"
                    + Encode.forHtml(
                        BaseMessages.getString(
                            PKG, "PrepareExecutionTransServlet.Log.TransNotInit", transName))
                    + "</H1>");

            out.println("<pre>");
            out.println(Encode.forHtml(logText));
            out.println(Encode.forHtml(Const.getStackTracker(e)));
            out.println("</pre>");
            out.println(
                "<a href=\""
                    + convertContextPath(GetTransStatusServlet.CONTEXT_PATH)
                    + "?name="
                    + URLEncoder.encode(transName, "UTF-8")
                    + "&id="
                    + id
                    + "\">"
                    + BaseMessages.getString(PKG, "TransStatusServlet.BackToTransStatusPage")
                    + "</a><p>");
          }
        }
      } else {
        if (useXML) {
          out.println(
              new WebResult(
                  WebResult.STRING_ERROR,
                  BaseMessages.getString(
                      PKG, "TransStatusServlet.Log.CoundNotFindSpecTrans", transName)));
        } else {
          out.println(
              "<H1>"
                  + Encode.forHtml(
                      BaseMessages.getString(
                          PKG, "TransStatusServlet.Log.CoundNotFindTrans", transName))
                  + "</H1>");
          out.println(
              "<a href=\""
                  + convertContextPath(GetStatusServlet.CONTEXT_PATH)
                  + "\">"
                  + BaseMessages.getString(PKG, "TransStatusServlet.BackToStatusPage")
                  + "</a><p>");
        }
      }
    } catch (Exception ex) {
      if (useXML) {
        out.println(
            new WebResult(
                WebResult.STRING_ERROR,
                BaseMessages.getString(
                    PKG,
                    "PrepareExecutionTransServlet.Error.UnexpectedError",
                    Const.CR + Const.getStackTracker(ex))));

      } else {
        out.println("<p>");
        out.println("<pre>");
        out.println(Encode.forHtml(Const.getStackTracker(ex)));
        out.println("</pre>");
      }
    }

    if (!useXML) {
      out.println("<p>");
      out.println("</BODY>");
      out.println("</HTML>");
    }
  }
  /**
   * <div id="mindtouch">
   *
   * <h1>/kettle/startJob</h1>
   *
   * <a name="GET"></a>
   *
   * <h2>GET</h2>
   *
   * <p>Starts the job. If the job cannot be started, an error is returned.
   *
   * <p><b>Example Request:</b><br>
   *
   * <pre function="syntax.xml">
   * GET /kettle/startJob/?name=dummy_job&xml=Y
   * </pre>
   *
   * <h3>Parameters</h3>
   *
   * <table class="pentaho-table">
   * <tbody>
   * <tr>
   * <th>name</th>
   * <th>description</th>
   * <th>type</th>
   * </tr>
   * <tr>
   * <td>name</td>
   * <td>Name of the job to be executed.</td>
   * <td>query</td>
   * </tr>
   * <tr>
   * <td>xml</td>
   * <td>Boolean flag which sets the output format required. Use <code>Y</code> to receive XML response.</td>
   * <td>boolean, optional</td>
   * </tr>
   * <tr>
   * <td>id</td>
   * <td>Carte job ID of the job to be executed. This parameter is optional when xml=Y is used.</td>
   * <td>query, optional</td>
   * </tr>
   * </tbody>
   * </table>
   *
   * <h3>Response Body</h3>
   *
   * <table class="pentaho-table">
   * <tbody>
   * <tr>
   * <td align="right">text:</td>
   * <td>HTML</td>
   * </tr>
   * <tr>
   * <td align="right">media types:</td>
   * <td>text/xml, text/html</td>
   * </tr>
   * </tbody>
   * </table>
   *
   * <p>Response XML or HTML containing operation result. When using xml=Y <code>result</code> field
   * indicates whether operation was successful (<code>OK</code>) or not (<code>ERROR</code>).
   *
   * <p><b>Example Response:</b>
   *
   * <pre function="syntax.xml">
   * <?xml version="1.0" encoding="UTF-8"?>
   * <webresult>
   * <result>OK</result>
   * <message>Job &#x5b;dummy_job&#x5d; was started.</message>
   * <id>abd61143-8174-4f27-9037-6b22fbd3e229</id>
   * </webresult>
   * </pre>
   *
   * <h3>Status Codes</h3>
   *
   * <table class="pentaho-table">
   * <tbody>
   * <tr>
   * <th>code</th>
   * <th>description</th>
   * </tr>
   * <tr>
   * <td>200</td>
   * <td>Request was processed.</td>
   * </tr>
   * <tr>
   * <td>500</td>
   * <td>Internal server error occurs during request processing.</td>
   * </tr>
   * </tbody>
   * </table>
   *
   * </div>
   */
  public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    if (isJettyMode() && !request.getContextPath().startsWith(CONTEXT_PATH)) {
      return;
    }

    if (log.isDebug()) {
      logDebug(BaseMessages.getString(PKG, "StartJobServlet.Log.StartJobRequested"));
    }

    String jobName = request.getParameter("name");
    String id = request.getParameter("id");
    boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));

    response.setStatus(HttpServletResponse.SC_OK);

    Encoder encoder = ESAPI.encoder();

    PrintWriter out = response.getWriter();
    if (useXML) {
      response.setContentType("text/xml");
      response.setCharacterEncoding(Const.XML_ENCODING);
      out.print(XMLHandler.getXMLHeader(Const.XML_ENCODING));
    } else {
      response.setContentType("text/html;charset=UTF-8");
      out.println("<HTML>");
      out.println("<HEAD>");
      out.println("<TITLE>Start job</TITLE>");
      out.println(
          "<META http-equiv=\"Refresh\" content=\"2;url="
              + convertContextPath(GetStatusServlet.CONTEXT_PATH)
              + "?name="
              + URLEncoder.encode(jobName, "UTF-8")
              + "\">");
      out.println("<META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
      out.println("</HEAD>");
      out.println("<BODY>");
    }

    try {
      // ID is optional...
      //
      Job job;
      CarteObjectEntry entry;
      if (Const.isEmpty(id)) {
        // get the first job that matches...
        //
        entry = getJobMap().getFirstCarteObjectEntry(jobName);
        if (entry == null) {
          job = null;
        } else {
          id = entry.getId();
          job = getJobMap().getJob(entry);
        }
      } else {
        // Take the ID into account!
        //
        entry = new CarteObjectEntry(jobName, id);
        job = getJobMap().getJob(entry);
      }

      if (job != null) {
        // First see if this job already ran to completion.
        // If so, we get an exception is we try to start() the job thread
        //
        if (job.isInitialized() && !job.isActive()) {
          // Re-create the job from the jobMeta
          //
          // We might need to re-connect to the repository
          //
          if (job.getRep() != null && !job.getRep().isConnected()) {
            if (job.getRep().getUserInfo() != null) {
              job.getRep()
                  .connect(
                      job.getRep().getUserInfo().getLogin(),
                      job.getRep().getUserInfo().getPassword());
            } else {
              job.getRep().connect(null, null);
            }
          }

          // Create a new job object to start from a sane state. Then replace
          // the new job in the job map
          //
          synchronized (getJobMap()) {
            JobConfiguration jobConfiguration = getJobMap().getConfiguration(jobName);

            String carteObjectId = UUID.randomUUID().toString();
            SimpleLoggingObject servletLoggingObject =
                new SimpleLoggingObject(CONTEXT_PATH, LoggingObjectType.CARTE, null);
            servletLoggingObject.setContainerObjectId(carteObjectId);

            Job newJob = new Job(job.getRep(), job.getJobMeta(), servletLoggingObject);
            newJob.setLogLevel(job.getLogLevel());

            // Discard old log lines from the old job
            //
            KettleLogStore.discardLines(job.getLogChannelId(), true);

            getJobMap().replaceJob(entry, newJob, jobConfiguration);
            job = newJob;
          }
        }

        runJob(job);

        String message = BaseMessages.getString(PKG, "StartJobServlet.Log.JobStarted", jobName);
        if (useXML) {
          out.println(new WebResult(WebResult.STRING_OK, message, id).getXML());
        } else {

          out.println("<H1>" + encoder.encodeForHTML(message) + "</H1>");
          out.println(
              "<a href=\""
                  + convertContextPath(GetJobStatusServlet.CONTEXT_PATH)
                  + "?name="
                  + URLEncoder.encode(jobName, "UTF-8")
                  + "&id="
                  + URLEncoder.encode(id, "UTF-8")
                  + "\">"
                  + BaseMessages.getString(PKG, "JobStatusServlet.BackToJobStatusPage")
                  + "</a><p>");
        }
      } else {
        String message =
            BaseMessages.getString(PKG, "StartJobServlet.Log.SpecifiedJobNotFound", jobName);
        if (useXML) {
          out.println(new WebResult(WebResult.STRING_ERROR, message));
        } else {
          out.println("<H1>" + encoder.encodeForHTML(message) + "</H1>");
          out.println(
              "<a href=\""
                  + convertContextPath(GetStatusServlet.CONTEXT_PATH)
                  + "\">"
                  + BaseMessages.getString(PKG, "TransStatusServlet.BackToStatusPage")
                  + "</a><p>");
        }
      }
    } catch (Exception ex) {
      if (useXML) {
        out.println(
            new WebResult(
                WebResult.STRING_ERROR,
                BaseMessages.getString(
                    PKG,
                    "StartJobServlet.Error.UnexpectedError",
                    Const.CR + Const.getStackTracker(ex))));
      } else {
        out.println("<p>");
        out.println("<pre>");
        out.println(encoder.encodeForHTML(Const.getStackTracker(ex)));
        out.println("</pre>");
      }
    }

    if (!useXML) {
      out.println("<p>");
      out.println("</BODY>");
      out.println("</HTML>");
    }
  }
  public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    if (isJettyMode() && !request.getContextPath().startsWith(CONTEXT_PATH)) {
      return;
    }

    if (log.isDebug()) {
      logDebug(BaseMessages.getString(PKG, "TransStatusServlet.Log.TransStatusRequested"));
    }

    String transName = request.getParameter("name");
    String id = request.getParameter("id");
    boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));
    int startLineNr = Const.toInt(request.getParameter("from"), 0);

    response.setStatus(HttpServletResponse.SC_OK);

    if (useXML) {
      response.setContentType("text/xml");
      response.setCharacterEncoding(Const.XML_ENCODING);
    } else {
      response.setCharacterEncoding("UTF-8");
      response.setContentType("text/html;charset=UTF-8");
    }

    PrintWriter out = response.getWriter();

    // ID is optional...
    //
    Trans trans;
    CarteObjectEntry entry;
    if (Const.isEmpty(id)) {
      // get the first transformation that matches...
      //
      entry = getTransformationMap().getFirstCarteObjectEntry(transName);
      if (entry == null) {
        trans = null;
      } else {
        id = entry.getId();
        trans = getTransformationMap().getTransformation(entry);
      }
    } else {
      // Take the ID into account!
      //
      entry = new CarteObjectEntry(transName, id);
      trans = getTransformationMap().getTransformation(entry);
    }

    Encoder encoder = ESAPI.encoder();

    if (trans != null) {
      String status = trans.getStatus();
      int lastLineNr = KettleLogStore.getLastBufferLineNr();
      String logText =
          KettleLogStore.getAppender()
              .getBuffer(trans.getLogChannel().getLogChannelId(), false, startLineNr, lastLineNr)
              .toString();

      if (useXML) {
        response.setContentType("text/xml");
        response.setCharacterEncoding(Const.XML_ENCODING);
        out.print(XMLHandler.getXMLHeader(Const.XML_ENCODING));

        SlaveServerTransStatus transStatus =
            new SlaveServerTransStatus(transName, entry.getId(), status);
        transStatus.setFirstLoggingLineNr(startLineNr);
        transStatus.setLastLoggingLineNr(lastLineNr);

        for (int i = 0; i < trans.nrSteps(); i++) {
          StepInterface baseStep = trans.getRunThread(i);
          if ((baseStep.isRunning()) || baseStep.getStatus() != StepExecutionStatus.STATUS_EMPTY) {
            StepStatus stepStatus = new StepStatus(baseStep);
            transStatus.getStepStatusList().add(stepStatus);
          }
        }

        // The log can be quite large at times, we are going to put a base64 encoding around a
        // compressed stream
        // of bytes to handle this one.
        String loggingString = HttpUtil.encodeBase64ZippedString(logText);
        transStatus.setLoggingString(loggingString);

        // Also set the result object...
        //
        transStatus.setResult(trans.getResult());

        // Is the transformation paused?
        //
        transStatus.setPaused(trans.isPaused());

        // Send the result back as XML
        //
        try {
          out.println(transStatus.getXML());
        } catch (KettleException e) {
          throw new ServletException("Unable to get the transformation status in XML format", e);
        }
      } else {
        response.setContentType("text/html;charset=UTF-8");

        out.println("<HTML>");
        out.println("<HEAD>");
        out.println(
            "<TITLE>"
                + BaseMessages.getString(PKG, "TransStatusServlet.KettleTransStatus")
                + "</TITLE>");
        out.println(
            "<META http-equiv=\"Refresh\" content=\"10;url="
                + convertContextPath(CONTEXT_PATH)
                + "?name="
                + URLEncoder.encode(transName, "UTF-8")
                + "&id="
                + URLEncoder.encode(id, "UTF-8")
                + "\">");
        out.println("<META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
        out.println("</HEAD>");
        out.println("<BODY>");
        out.println(
            "<H1>"
                + encoder.encodeForHTML(
                    BaseMessages.getString(PKG, "TransStatusServlet.TopTransStatus", transName))
                + "</H1>");

        try {
          out.println("<table border=\"1\">");
          out.print(
              "<tr> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.TransName")
                  + "</th> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.CarteObjectId")
                  + "</th> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.TransStatus")
                  + "</th> </tr>");

          out.print("<tr>");
          out.print("<td>" + encoder.encodeForHTML(transName) + "</td>");
          out.print("<td>" + encoder.encodeForHTML(id) + "</td>");
          out.print("<td>" + encoder.encodeForHTML(status) + "</td>");
          out.print("</tr>");
          out.print("</table>");

          out.print("<p>");

          // Get the transformation image
          //
          // out.print("<a href=\"" + convertContextPath(GetTransImageServlet.CONTEXT_PATH) +
          // "?name=" +
          // URLEncoder.encode(transName, "UTF-8") + "&id="+id+"\">"
          // + BaseMessages.getString(PKG, "TransStatusServlet.GetTransImage") + "</a>");
          Point max = trans.getTransMeta().getMaximum();
          max.x += 20;
          max.y += 20;
          out.print(
              "<iframe height=\""
                  + max.y
                  + "\" width=\""
                  + max.x
                  + "\" seamless src=\""
                  + convertContextPath(GetTransImageServlet.CONTEXT_PATH)
                  + "?name="
                  + URLEncoder.encode(transName, "UTF-8")
                  + "&id="
                  + URLEncoder.encode(id, "UTF-8")
                  + "\"></iframe>");
          out.print("<p>");

          if ((trans.isFinished() && trans.isRunning())
              || (!trans.isRunning() && !trans.isPreparing() && !trans.isInitializing())) {
            out.print(
                "<a href=\""
                    + convertContextPath(StartTransServlet.CONTEXT_PATH)
                    + "?name="
                    + URLEncoder.encode(transName, "UTF-8")
                    + "&id="
                    + URLEncoder.encode(id, "UTF-8")
                    + "\">"
                    + BaseMessages.getString(PKG, "TransStatusServlet.StartTrans")
                    + "</a>");
            out.print("<p>");
            out.print(
                "<a href=\""
                    + convertContextPath(PrepareExecutionTransServlet.CONTEXT_PATH)
                    + "?name="
                    + URLEncoder.encode(transName, "UTF-8")
                    + "&id="
                    + URLEncoder.encode(id, "UTF-8")
                    + "\">"
                    + BaseMessages.getString(PKG, "TransStatusServlet.PrepareTrans")
                    + "</a><br>");
          } else if (trans.isRunning()) {
            out.print(
                "<a href=\""
                    + convertContextPath(PauseTransServlet.CONTEXT_PATH)
                    + "?name="
                    + URLEncoder.encode(transName, "UTF-8")
                    + "&id="
                    + URLEncoder.encode(id, "UTF-8")
                    + "\">"
                    + BaseMessages.getString(PKG, "PauseStatusServlet.PauseResumeTrans")
                    + "</a><br>");
            out.print(
                "<a href=\""
                    + convertContextPath(StopTransServlet.CONTEXT_PATH)
                    + "?name="
                    + URLEncoder.encode(transName, "UTF-8")
                    + "&id="
                    + URLEncoder.encode(id, "UTF-8")
                    + "\">"
                    + BaseMessages.getString(PKG, "TransStatusServlet.StopTrans")
                    + "</a>");
            out.print("<p>");
          }
          out.print(
              "<a href=\""
                  + convertContextPath(CleanupTransServlet.CONTEXT_PATH)
                  + "?name="
                  + URLEncoder.encode(transName, "UTF-8")
                  + "&id="
                  + URLEncoder.encode(id, "UTF-8")
                  + "\">"
                  + BaseMessages.getString(PKG, "TransStatusServlet.CleanupTrans")
                  + "</a>");
          out.print("<p>");

          out.println("<table border=\"1\">");
          out.print(
              "<tr> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.Stepname")
                  + "</th> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.CopyNr")
                  + "</th> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.Read")
                  + "</th> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.Written")
                  + "</th> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.Input")
                  + "</th> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.Output")
                  + "</th> "
                  + "<th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.Updated")
                  + "</th> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.Rejected")
                  + "</th> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.Errors")
                  + "</th> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.Active")
                  + "</th> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.Time")
                  + "</th> "
                  + "<th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.Speed")
                  + "</th> <th>"
                  + BaseMessages.getString(PKG, "TransStatusServlet.prinout")
                  + "</th> </tr>");

          for (int i = 0; i < trans.nrSteps(); i++) {
            StepInterface step = trans.getRunThread(i);
            if ((step.isRunning()) || step.getStatus() != StepExecutionStatus.STATUS_EMPTY) {
              StepStatus stepStatus = new StepStatus(step);
              boolean snif = false;
              if (step.isRunning() && !step.isStopped() && !step.isPaused()) {
                snif = true;
                String sniffLink =
                    " <a href=\""
                        + convertContextPath(SniffStepServlet.CONTEXT_PATH)
                        + "?trans="
                        + URLEncoder.encode(transName, "UTF-8")
                        + "&id="
                        + URLEncoder.encode(id, "UTF-8")
                        + "&lines=50"
                        + "&copynr="
                        + step.getCopy()
                        + "&type="
                        + SniffStepServlet.TYPE_OUTPUT
                        + "&step="
                        + URLEncoder.encode(step.getStepname(), "UTF-8")
                        + "\">"
                        + encoder.encodeForHTML(stepStatus.getStepname())
                        + "</a>";
                stepStatus.setStepname(sniffLink);
              }

              out.print(stepStatus.getHTMLTableRow(snif));
            }
          }
          out.println("</table>");
          out.println("<p>");

          out.print(
              "<a href=\""
                  + convertContextPath(GetTransStatusServlet.CONTEXT_PATH)
                  + "?name="
                  + URLEncoder.encode(transName, "UTF-8")
                  + "&id="
                  + URLEncoder.encode(id, "UTF-8")
                  + "&xml=y\">"
                  + BaseMessages.getString(PKG, "TransStatusServlet.ShowAsXml")
                  + "</a><br>");
          out.print(
              "<a href=\""
                  + convertContextPath(GetStatusServlet.CONTEXT_PATH)
                  + "\">"
                  + BaseMessages.getString(PKG, "TransStatusServlet.BackToStatusPage")
                  + "</a><br>");
          out.print(
              "<p><a href=\""
                  + convertContextPath(GetTransStatusServlet.CONTEXT_PATH)
                  + "?name="
                  + URLEncoder.encode(transName, "UTF-8")
                  + "&id="
                  + URLEncoder.encode(id, "UTF-8")
                  + "\">"
                  + BaseMessages.getString(PKG, "TransStatusServlet.Refresh")
                  + "</a>");

          // Put the logging below that.

          out.println("<p>");
          out.println(
              "<textarea id=\"translog\" cols=\"120\" rows=\"20\" "
                  + "wrap=\"off\" name=\"Transformation log\" readonly=\"readonly\">"
                  + encoder.encodeForHTML(logText)
                  + "</textarea>");

          out.println("<script type=\"text/javascript\"> ");
          out.println("  translog.scrollTop=translog.scrollHeight; ");
          out.println("</script> ");
          out.println("<p>");
        } catch (Exception ex) {
          out.println("<p>");
          out.println("<pre>");
          out.println(encoder.encodeForHTML(Const.getStackTracker(ex)));
          out.println("</pre>");
        }

        out.println("<p>");
        out.println("</BODY>");
        out.println("</HTML>");
      }
    } else {
      if (useXML) {
        out.println(
            new WebResult(
                WebResult.STRING_ERROR,
                BaseMessages.getString(
                    PKG, "TransStatusServlet.Log.CoundNotFindSpecTrans", transName)));
      } else {
        out.println(
            "<H1>"
                + encoder.encodeForHTML(
                    BaseMessages.getString(
                        PKG, "TransStatusServlet.Log.CoundNotFindTrans", transName))
                + "</H1>");
        out.println(
            "<a href=\""
                + convertContextPath(GetStatusServlet.CONTEXT_PATH)
                + "\">"
                + BaseMessages.getString(PKG, "TransStatusServlet.BackToStatusPage")
                + "</a><p>");
      }
    }
  }
  @BeforeClass
  public static void setUpBeforeClass() throws Exception {

    KettleLogStore.init();
  }
/** Storage for bean annotations info for Metadata Injection and Load/Save. */
public class BeanInjectionInfo {
  private static LogChannelInterface LOG =
      KettleLogStore.getLogChannelInterfaceFactory().create(BeanInjectionInfo.class);

  protected final Class<?> clazz;
  private final InjectionSupported clazzAnnotation;
  private Map<String, Property> properties = new HashMap<>();
  private List<Group> groupsList = new ArrayList<>();
  /** Used only for fast group search during initialize. */
  private Map<String, Group> groupsMap = new HashMap<>();

  public static boolean isInjectionSupported(Class<?> clazz) {
    InjectionSupported annotation = clazz.getAnnotation(InjectionSupported.class);
    return annotation != null;
  }

  public BeanInjectionInfo(Class<?> clazz) {
    LOG.logDebug("Collect bean injection info for " + clazz);
    try {
      this.clazz = clazz;
      clazzAnnotation = clazz.getAnnotation(InjectionSupported.class);
      if (clazzAnnotation == null) {
        throw new RuntimeException("Injection not supported in " + clazz);
      }

      Group gr0 = new Group("");
      groupsList.add(gr0);
      groupsMap.put(gr0.getName(), gr0);
      for (String group : clazzAnnotation.groups()) {
        Group gr = new Group(group);
        groupsList.add(gr);
        groupsMap.put(gr.getName(), gr);
      }

      BeanLevelInfo root = new BeanLevelInfo();
      root.leafClass = clazz;
      root.init(this);

      properties = Collections.unmodifiableMap(properties);
      groupsList = Collections.unmodifiableList(groupsList);
      groupsMap = null;
    } catch (Throwable ex) {
      LOG.logError(
          "Error bean injection info collection for " + clazz + ": " + ex.getMessage(), ex);
      throw ex;
    }
  }

  public String getLocalizationPrefix() {
    return clazzAnnotation.localizationPrefix();
  }

  public Map<String, Property> getProperties() {
    return properties;
  }

  public List<Group> getGroups() {
    return groupsList;
  }

  protected void addInjectionProperty(Injection metaInj, BeanLevelInfo leaf) {
    if (StringUtils.isBlank(metaInj.name())) {
      throw new RuntimeException("Property name shouldn't be blank in the " + clazz);
    }

    String propertyName = calcPropertyName(metaInj, leaf);
    if (properties.containsKey(propertyName)) {
      throw new RuntimeException("Property '" + propertyName + "' already defined for " + clazz);
    }
    Property prop = new Property(propertyName, metaInj.group(), leaf.createCallStack());
    properties.put(prop.name, prop);
    Group gr = groupsMap.get(metaInj.group());
    if (gr == null) {
      throw new RuntimeException(
          "Group '"
              + metaInj.group()
              + "' for property '"
              + metaInj.name()
              + "' is not defined "
              + clazz);
    }
    gr.groupProperties.add(prop);
  }

  public String getDescription(String name) {
    String description = BaseMessages.getString(clazz, clazzAnnotation.localizationPrefix() + name);
    if (description != null && description.startsWith("!") && description.endsWith("!")) {
      Class baseClass = clazz.getSuperclass();
      while (baseClass != null) {
        InjectionSupported baseAnnotation =
            (InjectionSupported) baseClass.getAnnotation(InjectionSupported.class);
        if (baseAnnotation != null) {
          description =
              BaseMessages.getString(baseClass, baseAnnotation.localizationPrefix() + name);
          if (description != null && !description.startsWith("!") && !description.endsWith("!")) {
            return description;
          }
        }
        baseClass = baseClass.getSuperclass();
      }
    }
    return description;
  }

  private String calcPropertyName(Injection metaInj, BeanLevelInfo leaf) {
    String name = metaInj.name();
    while (leaf != null) {
      if (StringUtils.isNotBlank(leaf.prefix)) {
        name = leaf.prefix + '.' + name;
      }
      leaf = leaf.parent;
    }
    if (!name.equals(metaInj.name()) && !metaInj.group().isEmpty()) {
      // group exist with prefix
      throw new RuntimeException("Group shouldn't be declared with prefix in " + clazz);
    }
    return name;
  }

  public class Property {
    private final String name;
    private final String groupName;
    protected final List<BeanLevelInfo> path;
    protected final int pathArraysCount;

    public Property(String name, String groupName, List<BeanLevelInfo> path) {
      this.name = name;
      this.groupName = groupName;
      this.path = path;
      int ac = 0;
      for (BeanLevelInfo level : path) {
        if (level.dim != BeanLevelInfo.DIMENSION.NONE) {
          ac++;
        }
      }
      pathArraysCount = ac;
    }

    public String getName() {
      return name;
    }

    public String getGroupName() {
      return groupName;
    }

    public String getDescription() {
      return BeanInjectionInfo.this.getDescription(name);
    }

    public Class<?> getPropertyClass() {
      return path.get(path.size() - 1).leafClass;
    }
  }

  public class Group {
    private final String name;
    protected final List<Property> groupProperties = new ArrayList<>();

    public Group(String name) {
      this.name = name;
    }

    public String getName() {
      return name;
    }

    public List<Property> getGroupProperties() {
      return Collections.unmodifiableList(groupProperties);
    }

    public String getDescription() {
      return BeanInjectionInfo.this.getDescription(name);
    }
  }
}
  public Result execute(Result result, int nr) throws KettleException {
    FileLoggingEventListener loggingEventListener = null;
    LogLevel shellLogLevel = parentJob.getLogLevel();
    if (setLogfile) {
      String realLogFilename = environmentSubstitute(getLogFilename());
      // We need to check here the log filename
      // if we do not have one, we must fail
      if (Const.isEmpty(realLogFilename)) {
        logError(BaseMessages.getString(PKG, "JobEntryShell.Exception.LogFilenameMissing"));
        result.setNrErrors(1);
        result.setResult(false);
        return result;
      }

      try {
        loggingEventListener =
            new FileLoggingEventListener(getLogChannelId(), realLogFilename, setAppendLogfile);
        KettleLogStore.getAppender().addLoggingEventListener(loggingEventListener);
      } catch (KettleException e) {
        logError(
            BaseMessages.getString(
                PKG, "JobEntryShell.Error.UnableopenAppenderFile", getLogFilename(), e.toString()));
        logError(Const.getStackTracker(e));
        result.setNrErrors(1);
        result.setResult(false);
        return result;
      }
      shellLogLevel = logFileLevel;
    }

    log.setLogLevel(shellLogLevel);

    result.setEntryNr(nr);

    // "Translate" the arguments for later
    String[] substArgs = null;
    if (arguments != null) {
      substArgs = new String[arguments.length];
      for (int idx = 0; idx < arguments.length; idx++) {
        substArgs[idx] = environmentSubstitute(arguments[idx]);
      }
    }

    int iteration = 0;
    String[] args = substArgs;
    RowMetaAndData resultRow = null;
    boolean first = true;
    List<RowMetaAndData> rows = result.getRows();

    if (log.isDetailed()) {
      logDetailed(
          BaseMessages.getString(
              PKG, "JobEntryShell.Log.FoundPreviousRows", "" + (rows != null ? rows.size() : 0)));
    }

    while ((first && !execPerRow)
        || (execPerRow && rows != null && iteration < rows.size() && result.getNrErrors() == 0)) {
      first = false;
      if (rows != null && execPerRow) {
        resultRow = rows.get(iteration);
      } else {
        resultRow = null;
      }

      List<RowMetaAndData> cmdRows = null;

      if (execPerRow) {
        // Execute for each input row

        if (argFromPrevious) {
          // Copy the input row to the (command line) arguments

          if (resultRow != null) {
            args = new String[resultRow.size()];
            for (int i = 0; i < resultRow.size(); i++) {
              args[i] = resultRow.getString(i, null);
            }
          }
        } else {
          // Just pass a single row
          List<RowMetaAndData> newList = new ArrayList<RowMetaAndData>();
          newList.add(resultRow);
          cmdRows = newList;
        }
      } else {
        if (argFromPrevious) {
          // Only put the first Row on the arguments
          args = null;
          if (resultRow != null) {
            args = new String[resultRow.size()];
            for (int i = 0; i < resultRow.size(); i++) {
              args[i] = resultRow.getString(i, null);
            }
          } else {
            cmdRows = rows;
          }
        } else {
          // Keep it as it was...
          cmdRows = rows;
        }
      }

      executeShell(result, cmdRows, args);

      iteration++;
    }

    if (setLogfile) {
      if (loggingEventListener != null) {
        KettleLogStore.getAppender().removeLoggingEventListener(loggingEventListener);
        loggingEventListener.close();

        ResultFile resultFile =
            new ResultFile(
                ResultFile.FILE_TYPE_LOG,
                loggingEventListener.getFile(),
                parentJob.getJobname(),
                getName());
        result.getResultFiles().put(resultFile.getFile().toString(), resultFile);
      }
    }

    return result;
  }