001/*
002 * #%L
003 * Netarchivesuite - harvester
004 * %%
005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University 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.viewerproxy.distribute;
025
026import java.io.IOException;
027import java.net.URI;
028import java.util.Locale;
029import java.util.Set;
030
031import javax.servlet.http.HttpServletResponse;
032import javax.servlet.jsp.JspWriter;
033
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037import dk.netarkivet.common.exceptions.ArgumentNotValid;
038import dk.netarkivet.common.exceptions.IOFailure;
039import dk.netarkivet.common.utils.I18n;
040import dk.netarkivet.common.utils.StringUtils;
041import dk.netarkivet.common.webinterface.HTMLUtils;
042import dk.netarkivet.viewerproxy.CommandResolver;
043import dk.netarkivet.viewerproxy.Constants;
044import dk.netarkivet.viewerproxy.Controller;
045
046/**
047 * Client side communication with http controller server. This class works on a specific response object, simply
048 * forwarding it to the given url. Thus an instance of this class is a use-once-object.
049 * <p>
050 * The class is supposed to be used in JSP pages in order to make sure that the remote URI is requested through the
051 * browser, so communication with the HTTPControllerServer is done to the one currently set as viewer proxy.
052 */
053public class HTTPControllerClient implements Controller {
054    /** The response we are working on. */
055    private HttpServletResponse response;
056    /** The JspWriter that the page gave us. */
057    private JspWriter out;
058    /** The url to return to, for commands using this. */
059    private String returnURL;
060
061    /** The I18n object associated with this class. */
062    private static final I18n I18N = new I18n(Constants.TRANSLATIONS_BUNDLE);
063
064    /** The log. */
065    private static final Logger log = LoggerFactory.getLogger(HTTPControllerClient.class);
066    
067    
068    /**
069     * Make an HTTP controller client. Commands are sent using redirect on the given http response object. For commands
070     * with no output, the page is then forwarded to the response url.
071     *
072     * @param response The response object to use for redirect.
073     * @param out The JspWriter used to communicate with the users.
074     * @param returnURL The URL to return to afterwards if no output is given. This must not be null or not empty if
075     * either startRecordingURIs, stopRecordingURIs, changeIndex or clearRecordedURIs are called.
076     */
077    public HTTPControllerClient(HttpServletResponse response, JspWriter out, String returnURL) {
078        ArgumentNotValid.checkNotNull(response, "HttpServletResponse response");
079        ArgumentNotValid.checkNotNull(out, "JspWriter out");
080
081        this.response = response;
082        this.out = out;
083        this.returnURL = returnURL;
084    }
085
086    /** Start recording URIs and return to return URL. */
087    public void startRecordingURIs() {
088        redirectForSimpleCommand(HTTPControllerServer.START_COMMAND, true);
089    }
090
091    /** Stop recording URIs and return to return URL. */
092    public void stopRecordingURIs() {
093        String command = HTTPControllerServer.STOP_COMMAND;
094        redirectForSimpleCommand(command, true);
095    }
096
097    /** Clear recorded URIs and return to return URL. */
098    public void clearRecordedURIs() {
099        redirectForSimpleCommand(HTTPControllerServer.CLEAR_COMMAND, true);
100    }
101
102    /**
103     * Perform the necessary redirection to execute a simple (parameterless) command. Checks, that the returnURL is
104     * neither null or not empty.
105     *
106     * @param command One of the three parameterless commands START_COMMAND,
107     * @param useReturnURL Whether to append the returnURL parameter
108     */
109    protected void redirectForSimpleCommand(String command, boolean useReturnURL) {
110        try {
111            String url = "http://" + CommandResolver.VIEWERPROXY_COMMAND_NAME + command;
112            if (useReturnURL) {
113                ArgumentNotValid.checkNotNullOrEmpty(returnURL, "String returnURL");
114                url += '?' + HTTPControllerServer.RETURN_URL_PARAMETER + '=' + HTMLUtils.encode(returnURL);
115            }
116            response.sendRedirect(url);
117        } catch (IOException e) {
118            throw new IOFailure("Unable to redirect to controller server", e);
119        }
120    }
121
122    /**
123     * Write recorded URIs to response. NOTE! This does not respect the Controller! The URIs are *not* returned!
124     *
125     * @return null in all cases. The URIs are written in response by the forwarded call instead.
126     */
127    public Set<URI> getRecordedURIs() {
128        redirectForSimpleCommand(HTTPControllerServer.GET_RECORDED_URIS_COMMAND, false);
129        return null;
130    }
131
132    /**
133     * Change current index to work on these jobs. Then return to returnURL.
134     * <p>
135     * Since post data cannot be transferred through a regular redirect, we instead build a page that uses javascript to
136     * immediately repost the data to the url.
137     *
138     * @param jobList The list of jobs.
139     * @param label An arbitrary label that will be used to indicate this index
140     */
141    public void changeIndex(Set<Long> jobList, String label) {
142        ArgumentNotValid.checkNotNull(jobList, "Set jobList");
143        ArgumentNotValid.checkNotNullOrEmpty(label, "label");
144        ArgumentNotValid.checkNotNullOrEmpty(returnURL, "String returnURL");
145        StringBuffer url = new StringBuffer("http://" + CommandResolver.VIEWERPROXY_COMMAND_NAME
146                + HTTPControllerServer.CHANGE_INDEX_COMMAND);
147        log.info("Changing index to index for jobs {}, label {}, returnurl {}, url {}", StringUtils.conjoin(",", jobList), label, returnURL, url.toString());
148        try {
149            out.println("<html><head><title>");
150            out.println(I18N.getString(response.getLocale(), "redirecting"));
151            out.println("</title></head>");
152            out.println("<body onload='document" + ".getElementById(\"form\").submit();'>");
153            out.println("<form action=\"" + HTMLUtils.escapeHtmlValues(url.toString())
154                    + "\" method=\"POST\" id=\"form\">");
155            out.println("<input type='hidden' name='" + HTTPControllerServer.RETURN_URL_PARAMETER + "' value='"
156                    + HTMLUtils.escapeHtmlValues(returnURL) + "'/>");
157            for (Long jobId : jobList) {
158                out.println("<input type='hidden' name='" + HTTPControllerServer.JOB_ID_PARAMETER + "' value='" + jobId
159                        + "'/>");
160            }
161            out.println("<input type='hidden' name='" + HTTPControllerServer.INDEX_LABEL_PARAMETER + "' value='"
162                    + HTMLUtils.escapeHtmlValues(label) + "'/>");
163            out.println("</form>");
164            out.println("<p>");
165            out.println(I18N.getString(response.getLocale(), "generating.index.0.for.jobs.1",
166                    HTMLUtils.escapeHtmlValues(label), StringUtils.conjoin(", ", jobList)));
167            out.println("</body></html>");
168        } catch (IOException e) {
169            throw new IOFailure("Unable to redirect to controller server", e);
170        }
171    }
172
173    /**
174     * Write the current status of viewerproxy to response. NOTE! This does not respect the Controller API! The URIs are
175     * *not* returned!
176     *
177     * @param locale The locale (da, en, ...) that the response should be written using.
178     * @return null. The status is written in response by the forwarded call instead.
179     */
180    public String getStatus(Locale locale) {
181        ArgumentNotValid.checkNotNull(locale, "locale");
182        try {
183            response.sendRedirect("http://" + CommandResolver.VIEWERPROXY_COMMAND_NAME
184                    + HTTPControllerServer.GET_STATUS_COMMAND + "?" + HTTPControllerServer.LOCALE_PARAMETER + "="
185                    + HTMLUtils.encode(locale.toString()));
186        } catch (IOException e) {
187            throw new IOFailure("Unable to redirect to controller server", e);
188        }
189        return null;
190    }
191}