001/*
002 * #%L
003 * Netarchivesuite - heritrix 3 monitor
004 * %%
005 * Copyright (C) 2005 - 2018 The Royal Danish Library, 
006 *             the National Library of France and the Austrian National Library.
007 * %%
008 * This program is free software: you can redistribute it and/or modify
009 * it under the terms of the GNU Lesser General Public License as
010 * published by the Free Software Foundation, either version 2.1 of the
011 * License, or (at your option) any later version.
012 * 
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Lesser Public License for more details.
017 * 
018 * You should have received a copy of the GNU General Lesser Public
019 * License along with this program.  If not, see
020 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
021 * #L%
022 */
023
024package dk.netarkivet.heritrix3.monitor.resources;
025
026import java.io.IOException;
027import java.net.URLEncoder;
028import java.util.List;
029import java.util.Locale;
030import java.util.regex.Matcher;
031import java.util.regex.Pattern;
032
033import javax.servlet.ServletContext;
034import javax.servlet.ServletOutputStream;
035import javax.servlet.http.HttpServletRequest;
036import javax.servlet.http.HttpServletResponse;
037
038import org.apache.commons.lang.StringEscapeUtils;
039import org.netarchivesuite.heritrix3wrapper.ScriptResult;
040
041import com.antiaction.common.filter.Caching;
042import com.antiaction.common.templateengine.TemplateBuilderFactory;
043
044import dk.netarkivet.heritrix3.monitor.Heritrix3JobMonitor;
045import dk.netarkivet.heritrix3.monitor.NASEnvironment;
046import dk.netarkivet.heritrix3.monitor.NASUser;
047import dk.netarkivet.heritrix3.monitor.Pagination;
048import dk.netarkivet.heritrix3.monitor.ResourceAbstract;
049import dk.netarkivet.heritrix3.monitor.ResourceManagerAbstract;
050import dk.netarkivet.heritrix3.monitor.HttpLocaleHandler.HttpLocale;
051
052public class H3FrontierResource implements ResourceAbstract {
053
054    private NASEnvironment environment;
055
056    protected int R_FRONTIER = -1;
057    
058    @Override
059    public void resources_init(NASEnvironment environment) {
060        this.environment = environment;
061    }
062
063    @Override
064    public void resources_add(ResourceManagerAbstract resourceManager) {
065        R_FRONTIER = resourceManager.resource_add(this, "/job/<numeric>/frontier/", false);
066    }
067
068    @Override
069    public void resource_service(ServletContext servletContext, NASUser nas_user, HttpServletRequest req, HttpServletResponse resp, HttpLocale httpLocale, int resource_id, List<Integer> numerics, String pathInfo) throws IOException {
070        if (NASEnvironment.contextPath == null) {
071            NASEnvironment.contextPath = req.getContextPath();
072        }
073        if (NASEnvironment.servicePath == null) {
074            NASEnvironment.servicePath = req.getContextPath() + req.getServletPath() + "/";
075        }
076        String method = req.getMethod().toUpperCase();
077        if (resource_id == R_FRONTIER) {
078            if ("GET".equals(method) || "POST".equals(method)) {
079                frontier_list(req, resp, httpLocale, numerics);
080            }
081        }
082    }
083
084    public void frontier_list(HttpServletRequest req, HttpServletResponse resp, HttpLocale httpLocale, List<Integer> numerics) throws IOException {
085        Locale locale = httpLocale.locale;
086        resp.setContentType("text/html; charset=UTF-8");
087        ServletOutputStream out = resp.getOutputStream();
088        Caching.caching_disable_headers(resp);
089
090        TemplateBuilderFactory<MasterTemplateBuilder> masterTplBuilderFactory = TemplateBuilderFactory.getInstance(environment.templateMaster, "master.tpl", "UTF-8", MasterTemplateBuilder.class);
091        MasterTemplateBuilder masterTplBuilder = masterTplBuilderFactory.getTemplateBuilder();
092
093        StringBuilder sb = new StringBuilder();
094
095        long lines;
096        long linesPerPage = 100;
097        long page = 1;
098        long pages = 0;
099        String q = null;
100
101        String tmpStr;
102        tmpStr = req.getParameter("page");
103        if (tmpStr != null && tmpStr.length() > 0) {
104            try {
105                page = Long.parseLong(tmpStr);
106            } catch (NumberFormatException e) {
107            }
108        }
109        tmpStr = req.getParameter("itemsperpage");
110        if (tmpStr != null && tmpStr.length() > 0) {
111            try {
112                linesPerPage = Long.parseLong(tmpStr);
113            } catch (NumberFormatException e) {
114            }
115        }
116
117        if (linesPerPage < 25) {
118            linesPerPage = 25;
119        }
120
121        String initials = req.getParameter("initials");
122        if (initials == null) {
123            initials = "";
124        }
125
126        tmpStr = req.getParameter("q");
127        if (tmpStr != null && tmpStr.length() > 0 && !tmpStr.equalsIgnoreCase(".*")) {
128            q = tmpStr;
129        } else {
130                q = "";
131        }
132
133        String additionalParams = "";
134        if (q.length() > 0) {
135            additionalParams += "&q=" + URLEncoder.encode(q, "UTF-8");
136        }
137        if (initials.length() > 0) {
138            additionalParams += "&initials=" + URLEncoder.encode(initials, "UTF-8");
139        }
140
141        String script = environment.NAS_GROOVY_SCRIPT;
142
143        String deleteStr = req.getParameter("delete");
144        if (deleteStr != null && "1".equals(deleteStr) && initials != null && initials.length() > 0) {
145            script += "\n";
146            script += "\ninitials = \"" + initials + "\"";
147            script += "\ndeleteFromFrontier '" + q + "'\n";
148        } else {
149            script += "\n";
150            script += "\nlistFrontier '" + q + "', " + linesPerPage + ", " + (page - 1) + "\n";
151        }
152
153        long jobId = numerics.get(0);
154        Heritrix3JobMonitor h3Job = environment.h3JobMonitorThread.getRunningH3Job(jobId);
155
156        if (h3Job != null && h3Job.isReady()) {
157            if (deleteStr != null && "1".equals(deleteStr) && (initials == null || initials.length() == 0)) {
158                //sb.append("<span style=\"text-color: red;\">Initials required to delete from the frontier queue!</span><br />\n");
159                sb.append("<div class=\"notify notify-red\"><span class=\"symbol icon-error\"></span> Initials required to delete from the frontier queue!</div>");
160            }
161
162            sb.append("<form class=\"form-horizontal\" action=\"?\" name=\"insert_form\" method=\"post\" enctype=\"application/x-www-form-urlencoded\" accept-charset=\"utf-8\">\n");
163            sb.append("<label for=\"limit\">Lines per page:</label>");
164            sb.append("<input type=\"text\" id=\"itemsperpage\" name=\"itemsperpage\" value=\"" + linesPerPage + "\" placeholder=\"return limit\">\n");
165            sb.append("<label for=\"q\">Filter regex:</label>");
166            sb.append("<input type=\"text\" id=\"q\" name=\"q\" value=\"" + q + "\" placeholder=\"regex\" style=\"display:inline;width:350px;\">\n");
167            sb.append("<button type=\"submit\" name=\"show\" value=\"1\" class=\"btn btn-success\"><i class=\"icon-white icon-thumbs-up\"></i> Show</button>\n");
168            sb.append("&nbsp;");
169            sb.append("<label for=\"initials\">User initials:</label>");
170            sb.append("<input type=\"text\" id=\"initials\" name=\"initials\" value=\"" + initials  + "\" placeholder=\"initials\">\n");
171            sb.append("<button type=\"submit\" name=\"delete\" value=\"1\" class=\"btn btn-danger\"><i class=\"icon-white icon-trash\"></i> Delete</button>\n");
172            sb.append("</form>\n");
173
174            ScriptResult scriptResult = h3Job.h3wrapper.ExecuteShellScriptInJob(h3Job.jobResult.job.shortName, "groovy", script);
175            lines = extractLinesAmount(scriptResult);
176
177            if (lines > 0) {
178                pages = (lines + linesPerPage - 1) / linesPerPage;
179                if (pages == 0) {
180                    pages = 1;
181                }
182            }
183
184            if (page > pages) {
185                page = pages;
186            }
187
188            if (scriptResult != null && scriptResult.script != null) {
189                if (scriptResult.script.failure) {
190                        if (scriptResult.script.stackTrace != null) {
191                        sb.append("<h5>Script failed with the following stacktrace:</h5>\n");
192                        sb.append("<pre>\n");
193                        sb.append(StringEscapeUtils.escapeHtml(scriptResult.script.stackTrace));
194                        sb.append("</pre>\n");
195                        } else if (scriptResult.script.exception != null) {
196                        sb.append("<h5>Script failed with the following message:</h5>\n");
197                        sb.append("<pre>\n");
198                        sb.append(StringEscapeUtils.escapeHtml(scriptResult.script.exception));
199                        sb.append("</pre>\n");
200                        } else {
201                        sb.append("<b>Unknown script failure!</b></br>\n");
202                        }
203                        sb.append("<h5>Raw script result Xml:</h5>\n");
204                    sb.append("<pre>");
205                    sb.append(StringEscapeUtils.escapeHtml(new String(scriptResult.response, "UTF-8")));
206                    sb.append("</pre>");
207                } else {
208                    sb.append("<div style=\"float:left;margin: 20px 0px;\">\n");
209                    sb.append("<span>Matching lines: ");
210                    sb.append(lines);
211                    sb.append(" URIs</span>\n");
212                    sb.append("</div>\n");
213                    sb.append(Pagination.getPagination(page, linesPerPage, pages, false, additionalParams));
214                    sb.append("<div style=\"clear:both;\"></div>");
215                    sb.append("<div>\n");
216                    sb.append("<pre>\n");
217                    //System.out.println(new String(scriptResult.response, "UTF-8"));
218                    if (scriptResult != null && scriptResult.script != null) {
219                        if (scriptResult.script.htmlOutput != null) {
220                            sb.append("<fieldset><!--<legend>htmlOut</legend>-->");
221                            sb.append(scriptResult.script.htmlOutput);
222                            sb.append("</fieldset><br />\n");
223                        }
224                        if (scriptResult.script.rawOutput != null) {
225                            sb.append("<fieldset><!--<legend>rawOut</legend>-->");
226                            sb.append("<pre>");
227                            sb.append(scriptResult.script.rawOutput);
228                            sb.append("</pre>");
229                            sb.append("</fieldset><br />\n");
230                        }
231                    }
232                    sb.append("</pre>\n");
233                    sb.append("</div>\n");
234                    sb.append(Pagination.getPagination(page, linesPerPage, pages, false, additionalParams));
235                    sb.append("</form>");
236                }
237            } else {
238                sb.append("<b>Script did not return any response!</b><br/>\n");
239            }
240        } else {
241            sb.append("Job ");
242            sb.append(jobId);
243            sb.append(" is not running.");
244        }
245
246        StringBuilder menuSb = masterTplBuilder.buildMenu(new StringBuilder(), req, locale, h3Job);
247
248        masterTplBuilder.insertContent("Job " + jobId + " Frontier", menuSb.toString(), httpLocale.generateLanguageLinks(),
249                        "Job " + jobId + " Frontier", sb.toString(), "").write(out);
250
251        out.flush();
252        out.close();
253    }
254    
255    private Long extractLinesAmount(ScriptResult scriptResult) {
256        Pattern pattern = Pattern.compile("\\d+");
257        if (scriptResult != null && scriptResult.script != null) {
258            try {
259                if (scriptResult.script.htmlOutput != null) {
260                    Matcher matcher = pattern.matcher(scriptResult.script.htmlOutput);
261                    matcher.find();
262                    String str = scriptResult.script.htmlOutput.substring(matcher.start(), matcher.end());
263                    scriptResult.script.htmlOutput = scriptResult.script.htmlOutput.substring(matcher.end());
264                    return Long.parseLong(str);
265                } else {
266                    if (scriptResult.script.rawOutput != null) {
267                        Matcher matcher = pattern.matcher(scriptResult.script.rawOutput);
268                        matcher.find();
269                        String str = scriptResult.script.rawOutput.substring(matcher.start(), matcher.end());
270                        scriptResult.script.rawOutput = scriptResult.script.rawOutput.substring(matcher.end());
271                        return Long.parseLong(str);
272                    }
273                }
274            }
275            catch (Exception ex) {
276                return 1L;
277            }
278        }
279        return 1L;
280    }
281
282}