001/**
002 * Copyright (C) 2014  Universidade de Aveiro, DETI/IEETA, Bioinformatics Group - http://bioinformatics.ua.pt/
003 *
004 * This file is part of Dicoogle/dicoogle.
005 *
006 * Dicoogle/dicoogle is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * Dicoogle/dicoogle is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with Dicoogle.  If not, see <http://www.gnu.org/licenses/>.
018 */
019package pt.ua.dicoogle.server.web.management;
020
021import java.io.UnsupportedEncodingException;
022import java.util.Enumeration;
023import java.util.Map;
024import java.util.HashMap;
025
026import pt.ua.dicoogle.core.XMLSupport;
027import pt.ua.dicoogle.core.ServerSettings;
028
029import java.io.IOException;
030
031import javax.servlet.http.HttpServletRequest;
032import javax.servlet.http.HttpServletResponse;
033
034import pt.ua.dicoogle.server.ControlServices;
035import pt.ua.dicoogle.plugins.PluginController;
036import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;
037import pt.ua.dicoogle.sdk.settings.Utils;
038
039// FIXME do the procedures of this class need to be synchronized?!?
040/**
041 * A wrapper used to manage the services and plugins remotely through the web
042 * environment/app.
043 *
044 * @author António Novo <antonio.novo@ua.pt>
045 */
046public class Services {
047
048    /**
049     * The current instance of this class.
050     */
051    private static Services instance;
052    /**
053     * Pointers to the objects that maintain the services and plugins
054     * information.
055     */
056    private static ControlServices svcs;
057    private static PluginController plgs;
058    /**
059     * Types of actions to perform.
060     */
061    public static final int SVC_NO_ACTION = 0;
062    public static final int SVC_START = 1;
063    public static final int SVC_STOP = 2;
064    public static final int SVC_INIT_START = 3;
065    public static final int SVC_SET_PORT = 4;
066    public static final int SVC_SET_SETTINGS = 5;
067    /**
068     * Textual representation of the actions available.
069     */
070    public static final String ACTION_START = "start";
071    public static final String ACTION_STOP = "stop";
072    public static final String ACTION_INIT_START = "init-start";
073    public static final String ACTION_SET_PORT = "set-port";
074    public static final String ACTION_SET_ADVANCED_SETTINGS = "set-settings";
075    /**
076     * Textural representation of the params available.
077     */
078    public static final String PARAM_ACTION = "action";
079    public static final String PARAM_SERVICE = "service";
080    public static final String PARAM_INIT_START = "init-start";
081    public static final String PARAM_PORT = "port";
082    /**
083     * The embedded services name.
084     */
085    public static final String storageName = "Storage";
086    public static final String queryRetrieveName = "Query Retrieve";
087    public static final String webServicesName = "Web Services";
088    public static final String webServerName = "Web Server";
089    public static final String remoteGUIName = "Remote GUI";
090    /**
091     * The results indicating what was wrong with a performAction call.
092     */
093    public static final int RES_NO_ACTION = 0;
094    public static final int RES_INVALID_ACTION = 1;
095    public static final int RES_NO_SERVICE_NAME = 2;
096    public static final int RES_INVALID_SERVICE_NAME = 3;
097    public static final int RES_INVALID_ACTION_PARAMETER = 4;
098    public static final int RES_WARNING = 5;
099    public static final int RES_OK = 6;
100    /**
101     * The object responsible for saving the settings between server inits.
102     */
103    private XMLSupport xmlSupport;
104    /**
105     * Server settings container.
106     */
107    private ServerSettings cfgs;
108
109    private Services() {
110        svcs = ControlServices.getInstance();
111        plgs = PluginController.getInstance();
112
113        cfgs = ServerSettings.getInstance();
114        xmlSupport = new XMLSupport();
115    }
116
117    /**
118     * Returns the current instance of this class.
119     *
120     * @return the current instance of this class.
121     */
122    public synchronized static Services getInstance() {
123        if (instance == null) {
124            instance = new Services();
125        }
126
127        return instance;
128    }
129
130    /**
131     * This converts and applies the new textual values into the
132     * originalSettings HashMap, taking into account their original class/type.
133     * Only the settings that exist on both Maps are updated, this avoid invalid
134     * settings and Class type injections from happening.
135     *
136     * @param originalSettings the original advanced/internal settings of a
137     * plugin or service.
138     * @param newSettings the new settings to apply.
139     * @return the originalSettings HashMap with the new values in place (you
140     * can use the reference passed as originalSettings instead if you want,
141     * they will allways be the same).
142     */
143    public HashMap<String, Object> processAdvancedSettings(HashMap<String, Object> originalSettings, HashMap<String, String[]> newSettings) {
144        for (Map.Entry<String, Object> setting : originalSettings.entrySet()) {
145            String name = Utils.getHTMLElementIDFromString(setting.getKey()); // NOTE remmember that the setting name is "kinda encoded" (and not in the URLEncode way, that's handled automagically)
146            Object value = setting.getValue();
147
148            if (newSettings.containsKey(name)) // if the setting is found on the request try to update its value (maintaining the same type ofc)
149            {
150                String[] newValue = newSettings.get(name);
151
152                if (value != null) {
153                    // parse the value depending on its class
154                    if (value.getClass().equals(Integer.class)) {
155                        value = Integer.valueOf(newValue[0]);
156                    } else if (value.getClass().equals(Float.class)) {
157                        value = Float.valueOf(newValue[0]);
158                    } else if (value.getClass().equals(Boolean.class)) {
159                        value = Boolean.valueOf(Utils.parseCheckBoxValue(newValue[0]));
160                    } /*else if (value.getClass().equals(ServerDirectoryPath.class))
161                     value = ServerDirectoryPath.fromHTTPParams((ServerDirectoryPath) value, newSettings, name);*/ 
162                    //else if (value instanceof GenericSetting) {
163                       // value = ((GenericSetting) value).fromHTTPParams(newSettings, name);
164                //    } 
165                    else // String or unrecognized class
166                    {
167                        value = newValue[0];
168                    }
169
170                    setting.setValue(value);
171                } else // null is treated as String
172                {
173                    value = newValue[0];
174
175                    setting.setValue(value);
176                }
177
178            } else // if the setting was not found check if it's a Boolean one, this is a FIX because browsers omit unchecked checkboxes on form action
179            {
180                if (value.getClass().equals(Boolean.class)) {
181                    setting.setValue(false);
182                }
183            }
184        }
185
186        return originalSettings; // just for easier use
187    }
188
189    /**
190     * Performs a certain action to a service or plugin.
191     *
192     * @param action the action to perform.
193     * @param svcName the name of the service or plugin to perform the action
194     * at.
195     * @param initStart the init-start param value.
196     * @param port the set-port param value.
197     * @param advSettings a HashMap with all the advanced settings and their
198     * values.
199     * @return an int value indicating what was wrong with the call (see
200     * Services.RES_* for values/name pairs)
201     */
202    @Deprecated
203    public int performAction(int action, String svcName, boolean initStart, int port, HashMap<String, String[]> advSettings) {
204        if ((svcName == null) || svcName.trim().isEmpty()) {
205            return RES_NO_SERVICE_NAME;
206        }
207
208        try {
209            switch (action) {
210                case SVC_START:
211                    if (svcName.equalsIgnoreCase(webServerName)) {
212                        svcs.startWebServer();
213                        return RES_OK;
214                    } else {
215                        if (svcName.equalsIgnoreCase(webServicesName)) {
216
217                            svcs.startWebServices();
218                            return RES_OK;
219                        } else {
220                            if (svcName.equalsIgnoreCase(storageName)) {
221                                svcs.startStorage();
222                                return RES_OK;
223                            } else {
224                                if (svcName.equalsIgnoreCase(queryRetrieveName)) {
225                                    svcs.startQueryRetrieve();
226                                    return RES_OK;
227                                } else {
228                                        //TODO: DELETED
229                                    /*for (String plug : plgs.getPluginsNames()) {
230                                        if (plug.equalsIgnoreCase(svcName)) {
231                                            svcs.startPlugin(svcName);
232                                            return RES_OK;
233                                        }
234                                    }*/
235
236                                    return RES_INVALID_SERVICE_NAME;
237                                }
238                            }
239                        }
240                    }
241
242                case SVC_STOP:
243                    if (svcName.equalsIgnoreCase(webServerName)) {
244                        svcs.stopWebServer();
245                        return RES_OK;
246                    } else {
247                        if (svcName.equalsIgnoreCase(webServicesName)) {
248
249                            svcs.stopWebServices();
250                            return RES_OK;
251                        } else {
252                            if (svcName.equalsIgnoreCase(storageName)) {
253                                svcs.stopStorage();
254                                return RES_OK;
255                            } else {
256                                if (svcName.equalsIgnoreCase(queryRetrieveName)) {
257                                    svcs.stopQueryRetrieve();
258                                    return RES_OK;
259                                } else {
260                                        //TODO: DELETED
261                                    /*for (String plug : plgs.getPluginsNames()) {
262                                        if (svcName.equalsIgnoreCase(plug)) {
263                                            svcs.stopPlugin(svcName);
264                                            return RES_OK;
265                                        }
266                                    }*/
267
268                                    return RES_INVALID_SERVICE_NAME;
269                                }
270                            }
271                        }
272                    }
273
274                case SVC_INIT_START:
275                    if (svcName.equalsIgnoreCase(webServerName)) {
276                        setWebServerStart(initStart);
277                        return RES_OK;
278                    } else {
279                        if (svcName.equalsIgnoreCase(webServicesName)) {
280
281                            setWebServicesStart(initStart);
282                            return RES_OK;
283                        } else {
284                            if (svcName.equalsIgnoreCase(storageName)) {
285                                setStorageStart(initStart);
286                                return RES_OK;
287                            } else {
288                                if (svcName.equalsIgnoreCase(queryRetrieveName)) {
289                                    setQueryRetrieveStart(initStart);
290                                    return RES_OK;
291                                } else {
292                                        //TODO: DELETED
293                                    /*for (String plug : plgs.getPluginsNames()) {
294                                        if (svcName.equalsIgnoreCase(plug)) {
295                                            cfgs.setAutoStartPlugin(svcName, initStart);
296                                            return RES_OK;
297                                        }
298                                    }*/
299
300                                    return RES_INVALID_SERVICE_NAME;
301                                }
302                            }
303                        }
304                    }
305
306                case SVC_SET_PORT: {
307                    if ((port < 0) || (port > 65535)) {
308                        return RES_INVALID_ACTION_PARAMETER;
309                    }
310
311                    if (svcName.equalsIgnoreCase(webServerName)) {
312                        setWebServerPort(port);
313                        return RES_OK;
314                    } else {
315                        if (svcName.equalsIgnoreCase(webServicesName)) {
316
317                            setWebServicesPort(port);
318                            return RES_OK;
319                        } else {
320                            if (svcName.equalsIgnoreCase(storageName)) {
321                                setStoragePort(port);
322                                return RES_OK;
323                            } else {
324                                if (svcName.equalsIgnoreCase(queryRetrieveName)) {
325                                    setQueryRetrievePort(port);
326                                    return RES_OK;
327                                } else {
328                                    if (svcName.equalsIgnoreCase(remoteGUIName)) {
329                                        setRemoteGUIPort(port);
330                                        return RES_OK;
331                                    } else {
332                                        // TODO for plugins
333
334                                        return RES_INVALID_SERVICE_NAME;
335                                    }
336                                }
337                            }
338                        }
339                    }
340                }
341
342                case SVC_SET_SETTINGS:
343                    if (advSettings == null || advSettings.isEmpty()) {
344                        return RES_INVALID_ACTION_PARAMETER;
345                    } else {
346                        if (svcName.equalsIgnoreCase(storageName)) {
347                            HashMap<String, Object> settings = cfgs.getStorageSettings();
348
349                            processAdvancedSettings(settings, advSettings);
350
351                            // try to apply the settings
352                            if (cfgs.tryStorageSettings(settings)) {
353                                cfgs.setStorageSettings(settings);
354
355                                saveSettings();
356
357                                return RES_OK;
358                            } else {
359                                return RES_INVALID_ACTION_PARAMETER;
360                            }
361                        } else {
362                            if (svcName.equalsIgnoreCase(queryRetrieveName)) {
363                                HashMap<String, Object> settings = cfgs.getQueryRetrieveSettings();
364
365                                processAdvancedSettings(settings, advSettings);
366
367                                // try to apply the settings
368                                if (cfgs.tryQueryRetrieveSettings(settings)) {
369                                    cfgs.setQueryRetrieveSettings(settings);
370
371                                    saveSettings();
372
373                                    return RES_OK;
374                                } else {
375                                    return RES_INVALID_ACTION_PARAMETER;
376                                }
377                            } else {
378                                if (svcName.equalsIgnoreCase(remoteGUIName)) {
379                                    HashMap<String, Object> settings = cfgs.getRGUISettings();
380
381                                    processAdvancedSettings(settings, advSettings);
382
383                                    // try to apply the settings
384                                    if (cfgs.tryRGUISettings(settings)) {
385                                        cfgs.setRGUISettings(settings);
386
387                                        saveSettings();
388
389                                        return RES_OK;
390                                    } else {
391                                        return RES_INVALID_ACTION_PARAMETER;
392                                    }
393                                } else {
394                                    if (plgs.hasAdvancedSettings(svcName)) {
395                                        //TODO: DELETED
396                                        //HashMap<String, Object> settings = plgs.getAdvancedSettings(svcName);
397
398                                        //processAdvancedSettings(settings, advSettings);
399
400
401                                        // try to apply the settings
402                                        if (/*plgs.trySettings(svcName, settings)*/true) {
403                                            //plgs.setSettings(svcName, settings);
404
405                                            //plgs.saveSettings();
406
407                                            return RES_OK;
408                                        } else {
409                                            return RES_INVALID_ACTION_PARAMETER;
410                                        }
411                                    }
412
413                                    return RES_INVALID_SERVICE_NAME;
414                                }
415                            }
416                        }
417                    }
418
419                case SVC_NO_ACTION:
420                default:
421                    return RES_INVALID_ACTION;
422            }
423        } catch (IOException ex) {
424            return RES_WARNING;
425        }
426    }
427
428    /**
429     * Performs a certain action to a service or plugin, and save the settings
430     * if needed.
431     *
432     * @param request the request object.
433     * @return an int value indicating what was wrong with the call (see
434     * Services.RES_* for values/name pairs).
435     */
436    public int performAction(HttpServletRequest request) throws UnsupportedEncodingException {
437        // set proper encoding on the request
438        request.setCharacterEncoding("UTF-8");
439
440        // get the action to take
441        String action = request.getParameter(PARAM_ACTION);
442
443        // check if it is a valid action
444        if (action == null) {
445            return RES_NO_ACTION;
446        }
447
448        // get the name of the service/plugin to execute the action at
449        String svcName = request.getParameter(PARAM_SERVICE);
450
451        int portNumber = 0;
452        boolean initOnStart = false;
453        HashMap<String, String[]> advSettings = new HashMap<>();
454
455        int act = SVC_NO_ACTION;
456        if (action.equalsIgnoreCase(ACTION_START)) {
457            act = SVC_START;
458        } else if (action.equalsIgnoreCase(ACTION_STOP)) {
459            act = SVC_STOP;
460        } else if (action.equalsIgnoreCase(ACTION_INIT_START)) {
461            initOnStart = Utils.parseCheckBoxValue(request.getParameter(PARAM_INIT_START));
462            act = SVC_INIT_START;
463        } else if (action.equalsIgnoreCase(ACTION_SET_PORT)) {
464            String port = request.getParameter(PARAM_PORT);
465
466            if (port == null || port.trim().isEmpty()) {
467                return RES_INVALID_ACTION_PARAMETER;
468            }
469
470            portNumber = Integer.parseInt(port);
471            act = SVC_SET_PORT;
472        } else if (action.equalsIgnoreCase(ACTION_SET_ADVANCED_SETTINGS)) {
473            // get all the settings and their values
474            Enumeration<String> params = request.getParameterNames();
475            while (params.hasMoreElements()) {
476                String name = params.nextElement();
477
478                // ignore the main params (the ones that go us here)
479                if (name.equalsIgnoreCase(PARAM_ACTION) || name.equalsIgnoreCase(PARAM_SERVICE)) {
480                    continue;
481                }
482
483                String[] value = request.getParameterValues(name);
484
485                advSettings.put(name, value);
486            }
487
488            act = SVC_SET_SETTINGS;
489        } else {
490            return RES_INVALID_ACTION;
491        }
492
493        // execute the action requested
494        int result = performAction(act, svcName, initOnStart, portNumber, advSettings);
495
496        // save the settings if needed
497        if ((result == RES_OK) && ((act == SVC_INIT_START) || (act == SVC_SET_PORT))) {
498            saveSettings();
499        }
500
501        // return the performAction result
502        return result;
503    }
504
505    /**
506     * Processes a request from a servlet to perform an action on a service or
507     * plugin.
508     *
509     * @param request the servlet request object.
510     * @param response the servlet response object.
511     * @throws IOException if there was an error while attempting to perform the
512     * requested action.
513     */
514    public void processServletRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
515        // try to perform the action
516        int result = performAction(request);
517
518        // check the result and repond accordingly
519        switch (result) {
520            case RES_OK:
521                response.setStatus(HttpServletResponse.SC_OK);
522                break;
523
524            case RES_NO_ACTION:
525                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No action defined!");
526                break;
527
528            case RES_INVALID_ACTION:
529                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid action!");
530                break;
531
532            case RES_NO_SERVICE_NAME:
533                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No service/plugin name defined!");
534                break;
535
536            case RES_INVALID_SERVICE_NAME:
537                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid service/plugin name!");
538                break;
539
540            case RES_WARNING:
541            default:
542                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unexpected action result!");
543                break;
544        }
545    }
546
547    /**
548     * Processes a request from a webapp to perform an action on a service or
549     * plugin.
550     *
551     * @param request the webapp request object.
552     * @param response the webapp response object.
553     * @throws IOException if there was an error while attempting to perform the
554     * requested action.
555     * @return an int value that represents theresult of the operation performed
556     * (see Services.RES_* for values/name pairs).
557     */
558    public int processWebappRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
559        // try to perform the action
560        int result = performAction(request);
561
562        // check the result and repond accordingly
563        switch (result) {
564            case RES_OK:
565                response.setStatus(HttpServletResponse.SC_OK);
566                break;
567
568            case RES_NO_ACTION:
569                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
570                break;
571
572            case RES_INVALID_ACTION:
573                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
574                break;
575
576            case RES_NO_SERVICE_NAME:
577                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
578                break;
579
580            case RES_INVALID_SERVICE_NAME:
581                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
582                break;
583
584            case RES_WARNING:
585            default:
586                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
587                break;
588        }
589
590        return result;
591    }
592
593    /**
594     * Returns an HTML String containing the form element to perform some action
595     * an a plugin name by the brokerURL.
596     *
597     * @param brokerURL the URL where the form will post its data.
598     * @param action the action to take.
599     * @param name the name of the service or plugin where the action will be
600     * performed.
601     * @return an HTML String containing the form element to perform some action
602     * an a plugin name by the brokerURL.
603     */
604    private String getHTMLActionForm(String brokerURL, String action, String name) {
605        String result = "<form action=\"" + brokerURL + "\" method=\"get\"><input type=\"hidden\" name=\"" + PARAM_ACTION + "\" value=\"" + action.toLowerCase() + "\" /><input type=\"hidden\" name=\"" + PARAM_SERVICE + "\" value=\"" + name + "\" />";
606
607        String buttonStyle = "btn";
608        if (action.equalsIgnoreCase("start")) {
609            buttonStyle += " btn-success";
610        }
611        if (action.equalsIgnoreCase("stop")) {
612            buttonStyle += " btn-danger";
613        }
614
615        result += "<input type=\"submit\" class=\"" + buttonStyle + "\" value=\"" + action + "\" />";
616
617        // add a little warning for stopping the web server, making it unable to be used remotely again
618                /*if (name.equalsIgnoreCase(webServerName))
619         {
620         result += "<div class=\"warning\"><span class=\"notice\">!</span></div>";
621         }*/
622
623        result += "</form>";
624
625        return result;
626    }
627
628    /**
629     * Returns an HTML String containing the form element to change the start on
630     * init flag of a plugin name by the brokerURL.
631     *
632     * @param brokerURL the URL where the form will post its data.
633     * @param name the name of the service or plugin where the action will be
634     * performed.
635     * @param startInitially if the plugin or service is currently set to start
636     * initially or not.
637     * @param canBeDisabled if this plugin or service can be disabled.
638     * @return an HTML String containing the form element to change the start on
639     * init flag of a plugin name by the brokerURL.
640     */
641    private String getHTMLStartOnInitForm(String brokerURL, String name, boolean startInitially, boolean canBeDisabled) {
642        String result = "<form action=\"" + brokerURL + "\" method=\"get\"><input type=\"hidden\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_INIT_START + "\" /><input type=\"hidden\" name=\"" + PARAM_SERVICE + "\" value=\"" + name + "\" />";
643
644        result += "<label for=\"" + PARAM_INIT_START + "-" + Utils.getHTMLElementIDFromString(name) + "\" class=\"checkbox inline\">";
645        result += "<input type=\"checkbox\" id=\"" + PARAM_INIT_START + "-" + Utils.getHTMLElementIDFromString(name) + "\" name=\"" + PARAM_INIT_START + "\" " + (startInitially ? "checked=\"checked\"" : "") + " onclick=\"this.parent.submit();\" " + (canBeDisabled ? "" : "disabled=\"disabled\"") + " /> Auto Start";
646        result += "</label>";
647        result += "<input type=\"submit\" hidden=\"hidden\" />";
648
649        // add a little warning for disabling the web server, making it unable to be used remotely again
650                /*if (name.equalsIgnoreCase(webServerName))
651         {
652         result += "<div class=\"warning\"><span class=\"notice\">!</span></div>";
653         }*/
654
655        result += "</form>";
656
657        return result;
658    }
659
660    /**
661     * Returns an HTML String containing the form element to change the port
662     * number of a plugin name by the brokerURL.
663     *
664     * @param brokerURL the URL where the form will post its data.
665     * @param name the name of the service or plugin where the action will be
666     * performed.
667     * @param port the current plugin or service port number.
668     * @return an HTML String containing the form element to change the port
669     * number of a plugin name by the brokerURL.
670     */
671    private String getHTMLPortForm(String brokerURL, String name, int port) {
672        String result = "<form action=\"" + brokerURL + "\" method=\"get\"><input type=\"hidden\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_SET_PORT + "\" /><input type=\"hidden\" name=\"" + PARAM_SERVICE + "\" value=\"" + name + "\" />";
673
674        result += "<label for=\"" + PARAM_PORT + "-" + Utils.getHTMLElementIDFromString(name) + "\">";
675        result += "Port: " + "<input id=\"" + PARAM_PORT + "-" + Utils.getHTMLElementIDFromString(name) + "\" type=\"number\" name=\"" + PARAM_PORT + "\" min=\"0\" max=\"65535\" maxlength=\"5\" size=\"5\" value=\"" + port + "\" class=\"input-small\" style=\"margin-right: 10px;\"/>";
676        result += "<input type=\"submit\" class=\"btn\" value=\"Set Port\" class=\"btn\" />";
677        result += "</label>";
678
679        // add a little warning for stopping the web server, making it unable to be used remotely again
680                /*if (name.equalsIgnoreCase(webServerName))
681         {
682         result += "<div class=\"warning\"><span class=\"notice\">!</span></div>";
683         }*/
684
685        result += "</form>";
686
687        return result;
688    }
689
690    /**
691     * Returns an HTML String containing the form element to see and modify the
692     * advanced/internal options of a plugin or service name by the brokerURL.
693     *
694     * @param brokerURL the URL where the form will post its data.
695     * @param name the name of the service or plugin where the action will be
696     * performed.
697     * @return an HTML String containing the form element to see and modify the
698     * advanced/internal options of a plugin or service name by the brokerURL.
699     */
700    @Deprecated
701    private String getHTMLAdvancedOptionForm(String brokerURL, String name) {
702        String result = "<form action=\"" + brokerURL + "\" method=\"get\"><input type=\"hidden\" name=\"" + PARAM_SERVICE + "\" value=\"" + name + "\" />";
703
704        result += "<input type=\"submit\" class=\"btn\" value=\"Advanced Options\" class=\"btn btn-info\" />";
705
706        result += "</form>";
707
708        return result;
709    }
710
711    /**
712     * Returns a String containing the HTML code for a row to the HTML service
713     * management table, based o the parameters passed to it.
714     *
715     * @param isRunning if the target service is currently running.
716     * @param brokerURL the url of the service broker.
717     * @param name the name of the target service.
718     * @param canBeDisabled if the plugins or service can be disabled from
719     * starting/stoping.
720     * @param startInitially if the plugin or service is currently set to be
721     * start on server initialization.
722     * @param hasPort if the plugin or service has a port number that can be
723     * changed.
724     * @param port the current port number of the plugin or service.
725     * @param hasAdvancedOptions if the pugin or service has advanced (internal)
726     * options.
727     * @param advancedOptionsManagerURL the URL of the pugin or service advanced
728     * options manager.
729     * @return a String containing the HTML code for a row to the HTML service
730     * management table, based o the parameters passed to it.
731     */
732    @Deprecated
733    private String getHTMLServiceManagementTableRow(boolean isRunning, String brokerURL, String name, boolean canBeStopped, boolean canBeDisabled, boolean startInitially, boolean hasPort, int port, boolean hasAdvancedOptions, String advancedOptionsManagerURL) {
734        String result = "<tr " + (name.equalsIgnoreCase(webServerName) ? "class=\"warning\"" : "") + ">"; // add a little warning for stopping the web server, making it unable to be used remotely again
735
736        result += "<td>" + escapeHtml4(name) + "</td>";
737        if (isRunning) {
738            result += "<td class=\"running\">Running</td>";
739            if (canBeStopped) {
740                result += "<td>" + getHTMLActionForm(brokerURL, "Stop", name) + "</td>";
741            } else {
742                result += "<td></td>";
743            }
744        } else {
745            result += "<td class=\"stopped\">Stopped</td>";
746            if (canBeStopped) {
747                result += "<td>" + getHTMLActionForm(brokerURL, "Start", name) + "</td>";
748            } else {
749                result += "<td>" + "<div class=\"error\"></div>" + getHTMLActionForm(brokerURL, "Start", name) + "</td>"; // add a little error notification, so that the admin can see that the plugin failed to start
750            }
751        }
752        result += "<td>" + getHTMLStartOnInitForm(brokerURL, name, startInitially, canBeDisabled) + "</td>";
753        if (hasPort) {
754            result += "<td>" + getHTMLPortForm(brokerURL, name, port) + "</td>";
755        } else {
756            result += "<td></td>";
757        }
758        if (hasAdvancedOptions) {
759            result += "<td>" + getHTMLAdvancedOptionForm(advancedOptionsManagerURL, name) + "</td>";
760        } else {
761            result += "<td></td>";
762        }
763
764        result += "</tr>";
765
766        return result;
767    }
768
769    /**
770     * Returns an HTML String containing the list of all plugins and services
771     * available, their status and action that can be taken.
772     *
773     * @param brokerURL the URL of the service manager page (where the form will
774     * post data to).
775     * @param advancedOptionsManagerURL the URL of the plugin/service
776     * advanced/internal options manager.
777     * @param id the ID of this HTML element, can be null if not needed.
778     * @return an HTML String containing the list of all plugins and services
779     * available, their status and action that can be taken.
780     */
781    @Deprecated
782    public String getHTMLServiceManagementTable(String brokerURL, String advancedOptionsManagerURL, String id) {
783        String result = "";
784        if ((id == null) || id.trim().isEmpty()) {
785            result += "<table class=\"table table-striped\">";
786        } else {
787            result += "<table id=\"" + id + "\" class=\"table table-striped\">";
788        }
789        result += "<thead>";
790        result += "<tr>";
791        result += "<th>Name</th>";
792        result += "<th>Status</th>";
793        result += "<th>Actions</th>";
794        result += "<th>Start on Init</th>";
795        result += "<th>Options</th>";
796        result += "<th>Advanced Options</th>";
797        result += "</tr>";
798        result += "</thead>";
799        result += "<tbody>";
800
801        // list all the plugins
802        //TODO: DELETED
803        /*for (String plug : plgs.getPluginsNames()) {
804            result += getHTMLServiceManagementTableRow(plgs.isPluginRunning(plug), brokerURL, plug, true, true, cfgs.getAutoStartPlugin(plug), false, 0, plgs.hasAdvancedSettings(plug), advancedOptionsManagerURL);
805        }*/
806        // list the storage service
807        result += getHTMLServiceManagementTableRow(svcs.storageIsRunning(), brokerURL, storageName, true, true, storageStart(), true, storagePort(), false, /*advancedOptionsManagerURL*/ null);
808        // list the query retrieve service
809        result += getHTMLServiceManagementTableRow(svcs.queryRetrieveIsRunning(), brokerURL, queryRetrieveName, true, true, queryRetrieveStart(), true, queryRetrievePort(), true, advancedOptionsManagerURL);
810        // list the web services
811        //result += getHTMLServiceManagementTableRow(false, brokerURL, webServicesName, false, false, false, true, webServicesPort(), false, null);
812        // list the web server
813        result += getHTMLServiceManagementTableRow(svcs.webServerIsRunning(), brokerURL, webServerName, true, true, webServerStart(), true, webServerPort(), false, null);
814        // list the RMI service
815        result += getHTMLServiceManagementTableRow(true, brokerURL, remoteGUIName, false, false, true, true, remoteGUIPort(), true, advancedOptionsManagerURL); // FIXME actually get if the RMI is running or not
816
817        result += "</tbody>";
818        result += "</table>";
819
820        return result;
821    }
822
823    /**
824     * Returns an HTML String containing the list of all plugins and services
825     * available, their status and action that can be taken.
826     *
827     * @param brokerURL the URL of the service manager page (where the form will
828     * post data to).
829     * @param advancedOptionsManagerURL the URL of the plugin/service
830     * advanced/internal options manager.
831     * @return an HTML String containing the list of all plugins and services
832     * available, their status and action that can be taken.
833     */
834    @Deprecated
835    public String getHTMLServiceManagementTable(String brokerURL, String advancedOptionsManagerURL) {
836        return getHTMLServiceManagementTable(brokerURL, advancedOptionsManagerURL, null);
837    }
838
839    /**
840     * Return if the Storage service is started initially.
841     *
842     * @return if the Storage service is started initially.
843     */
844    public boolean storageStart() {
845        return cfgs.isStorage();
846    }
847
848    /**
849     * Return if the QueryRetrieve service is started initially.
850     *
851     * @return if the QueryRetrieve service is started initially.
852     */
853    public boolean queryRetrieveStart() {
854        return cfgs.isQueryRetrive();
855    }
856
857    /**
858     * Return if the WebServices service is started initially.
859     *
860     * @return if the WebServices service is started initially.
861     */
862    public boolean webServicesStart() {
863        return cfgs.getWeb().isWebServices();
864    }
865
866    /**
867     * Return if the WebServer service is started initially.
868     *
869     * @return if the WebServer service is started initially.
870     */
871    public boolean webServerStart() {
872        return cfgs.getWeb().isWebServer();
873    }
874
875    /**
876     * Sets if the Storage service is started initially.
877     *
878     * @param start if the Storage service is started initially.
879     */
880    public void setStorageStart(boolean start) {
881        cfgs.setStorage(start);
882    }
883
884    /**
885     * Sets if the QueryRetrieve service is started initially.
886     *
887     * @param start if the QueryRetrieve service is started initially.
888     */
889    public void setQueryRetrieveStart(boolean start) {
890        cfgs.setQueryRetrive(start);
891    }
892
893    /**
894     * Sets if the WebServices service is started initially.
895     *
896     * @param start if the WebServices service is started initially.
897     */
898    public void setWebServicesStart(boolean start) {
899        cfgs.getWeb().setWebServices(start);
900    }
901
902    /**
903     * Sets if the WebServer service is started initially.
904     *
905     * @param start if the WebServer service is started initially.
906     */
907    public void setWebServerStart(boolean start) {
908        cfgs.getWeb().setWebServer(start);
909    }
910
911    /**
912     * Returns the Storage service port.
913     *
914     * @return the Storage service port.
915     */
916    public int storagePort() {
917        return cfgs.getStoragePort();
918    }
919
920    /**
921     * Returns the QueryRetrieve service port.
922     *
923     * @return the QueryRetrieve service port.
924     */
925    public int queryRetrievePort() {
926        return cfgs.getWlsPort();
927    }
928
929    /**
930     * Returns the WebServices service port.
931     *
932     * @return the WebServices service port.
933     */
934    public int webServicesPort() {
935        return cfgs.getWeb().getServicePort();
936    }
937
938    /**
939     * Returns the WebServer service port.
940     *
941     * @return the WebServer service port.
942     */
943    public int webServerPort() {
944        return cfgs.getWeb().getServerPort();
945    }
946
947    /**
948     * Returns the RemoteGUI service port.
949     *
950     * @return the RemoteGUI service port.
951     */
952    public int remoteGUIPort() {
953        return cfgs.getRemoteGUIPort();
954    }
955
956    /**
957     * Returns the RemoteGUI service external IP.
958     *
959     * @return the RemoteGUI service external IP.
960     */
961    public String remoteGUIExtIP() {
962        return cfgs.getRGUIExternalIP();
963    }
964
965    /**
966     * Sets the Storage service port.
967     *
968     * @param port the Storage service port.
969     */
970    public void setStoragePort(int port) {
971        cfgs.setStoragePort(port);
972    }
973
974    /**
975     * Sets the QueryRetrieve service port.
976     *
977     * @param port the QueryRetrieve service port.
978     */
979    public void setQueryRetrievePort(int port) {
980        cfgs.setWlsPort(port);
981    }
982
983    /**
984     * Sets the WebServices service port.
985     *
986     * @param port the WebServices service port.
987     */
988    public void setWebServicesPort(int port) {
989        cfgs.getWeb().setServicePort(port);
990    }
991
992    /**
993     * Sets the WebServer service port.
994     *
995     * @param port the WebServer service port.
996     */
997    public void setWebServerPort(int port) {
998        cfgs.getWeb().setServerPort(port);
999    }
1000
1001    /**
1002     * Sets the RemoteGUI service port.
1003     *
1004     * @param port the RemoteGUI service port.
1005     */
1006    public void setRemoteGUIPort(int port) {
1007        cfgs.setRemoteGUIPort(port);
1008    }
1009
1010    /**
1011     * Sets the RemoteGUI service external IP.
1012     *
1013     * @param extIP the RemoteGUI service external IP.
1014     */
1015    public void setRemoteGUIExtIP(String extIP) {
1016        cfgs.setRGUIExternalIP(extIP);
1017    }
1018
1019    /**
1020     * Saves the settings for use between server inits.
1021     */
1022    public void saveSettings() {
1023        
1024        xmlSupport.printXML();
1025    }
1026
1027    /**
1028     * For a request, returns the name of the service/plugin required.
1029     *
1030     * @param request the servlet request object.
1031     * @return the name of the service/plugin required.
1032     * @throws IOException
1033     */
1034    public String getRequestServiceName(HttpServletRequest request) throws IOException {
1035        // set proper encoding on the request
1036        request.setCharacterEncoding("UTF-8");
1037
1038        // get the name of the service/plugin
1039        return request.getParameter(PARAM_SERVICE);
1040    }
1041
1042    public String getHTMLSettingHelp(String fieldTitle, String help) {
1043        String result = "";
1044
1045        // make sure that there is a valid help notice
1046        if (help == null) {
1047            return result;
1048        }
1049        help = help.trim();
1050        if (help.isEmpty()) {
1051            return result;
1052        }
1053
1054        // replace the "\n" (#13/#10) with "\\n" so that JS can interpret that correctly instead of ending up with a new line on the document
1055        //help = help.replaceAll("\n", "\\\\n");
1056        // also the "\t" ones
1057        //help = help.replaceAll("\t", "\\\\t");
1058
1059        // add a button that will show the help (button because of touch interfaces, instead of popup for desktop)
1060        System.out.println("HELP: "+help);
1061        String msg = escapeHtml4(help.replaceAll("\n", "<br>"));
1062        result += buildInfoButton(fieldTitle, msg);
1063                        System.out.println("MSG: "+msg);
1064        return result;
1065    }
1066    
1067   private String buildInfoButton(String title, String msg){
1068        StringBuilder builder = new StringBuilder();
1069        
1070        builder.append("<a ");
1071        //builder.append(id);
1072        builder.append(" data-original-title=\"");
1073        builder.append(title);
1074        builder.append("\" href=\"#\" class=\"btn btn-mini btn-info\" data-toggle=\"popover\" data-html=\"true\" data-content=\"");
1075        builder.append(msg);
1076        builder.append("\" onclick=\"return false;\">Info</a>\n");
1077        
1078        return builder.toString();
1079    }
1080    
1081
1082    /**
1083     * Returns a row for the advanced settings form.
1084     *
1085     * @param name the name of the setting.
1086     * @param value the type/value of the setting.
1087     * @param help s String containing a help notice for this setting, can be
1088     * null if not needed.
1089     * @return a String containing a HTML code to a row for the advanced
1090     * settings form.
1091     */
1092    public String getHTMLAdvancedSettingsFormRow(String name, Object value, String help) {
1093        
1094        String result = "";
1095
1096        String id = Utils.getHTMLElementIDFromString(name);
1097
1098        result += "<tr>";
1099
1100        result += "<td><label for=\"" + id + "\">" + escapeHtml4(name) + ":</label></td>";
1101        result += "<td>" + Utils.getHTMLInputFromType(id, value) + getHTMLSettingHelp(escapeHtml4(name) , help) + "</td>"; // TODO add a button with help, like in the desktop application [getHelpForParam(pluginName, paramName) != null]
1102
1103        result += "</tr>";
1104
1105        return result;
1106    }
1107
1108    /**
1109     * Based on the request, returns a String containing a form with all the
1110     * settings inputs boxes. These boxes are rendered/specified in accordance
1111     * with the each setting value type reported by the plugin/service.
1112     *
1113     * @param request the servlet request object.
1114     * @param brokerURL the URL of the broker that will apply the settings after
1115     * receiving this forms post.
1116     * @param elementID the ID of this HTML form, can be null.
1117     * @return a String containing the HTML structure for the form with all the
1118     * plugin advanced setting.
1119     */
1120    public String getHTMLServiceAdvancedSettingsForm(HttpServletRequest request, String brokerURL, String elementID) throws IOException {
1121        String result = "";
1122
1123        String name = getRequestServiceName(request);
1124
1125        // test if this is a embeded service and has advanced settings
1126        boolean isStorage = storageName.equalsIgnoreCase(name);
1127        boolean isQueryRetrieve = queryRetrieveName.equalsIgnoreCase(name);
1128        boolean isRemoteGUI = remoteGUIName.equalsIgnoreCase(name);
1129        boolean isEmbededService = isStorage || isQueryRetrieve || isRemoteGUI;
1130
1131        // if it's not a embeded service
1132        if (!isEmbededService) {
1133            // test if it's a plugin and has advanced settings
1134            if (!plgs.hasAdvancedSettings(name)) {
1135                result += "<h3>The required plugin/service is either invalid or has no advanced settings!</h3>";
1136                return result;
1137            }
1138        }
1139
1140        if ((elementID == null) || elementID.trim().isEmpty()) {
1141            result += "<form ";
1142        } else {
1143            result += "<form id=\"" + elementID + "\" ";
1144        }
1145        result += "action=\"" + brokerURL + "\" method=\"get\">";
1146
1147        result += "<input type=\"hidden\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_SET_ADVANCED_SETTINGS + "\" />";
1148        result += "<input type=\"hidden\" name=\"" + PARAM_SERVICE + "\" value=\"" + escapeHtml4(name) + "\" />";
1149
1150        result += "<table class=\"table table-hover\"><tbody>";
1151
1152        HashMap<String, Object> settings = null;
1153        HashMap<String, String> settingsHelp = null;
1154        if (!isEmbededService) // if it's a plugin
1155        {
1156                //TODO: DELETED
1157            //settings = plgs.getAdvancedSettings(name);
1158            settingsHelp = plgs.getAdvancedSettingsHelp(name);
1159        } else {
1160            if (isStorage) {
1161                settings = cfgs.getStorageSettings();
1162                settingsHelp = cfgs.getStorageSettingsHelp();
1163            } else if (isQueryRetrieve) {
1164                settings = cfgs.getQueryRetrieveSettings();
1165                settingsHelp = cfgs.getQueryRetrieveSettingsHelp();
1166            } else // Remote GUI
1167            {
1168                settings = cfgs.getRGUISettings();
1169                settingsHelp = cfgs.getRGUISettingsHelp();
1170            }
1171        }
1172        // just to avoid any unwanted exceptions, in case a plugin misbehaves
1173        if (settings == null) {
1174            settings = new HashMap<String, Object>();
1175        }
1176        if (settingsHelp == null) {
1177            settingsHelp = new HashMap<String, String>();
1178        }
1179
1180        // create a table row for each setting (includes name, value/type and help, if available)
1181        for (Map.Entry<String, Object> setting : settings.entrySet()) {
1182            String key = setting.getKey();
1183            Object value = setting.getValue();
1184            String help = settingsHelp.get(key);
1185
1186            result += getHTMLAdvancedSettingsFormRow(key, value, help);
1187        }
1188
1189        result += "</tbody></table><br />";
1190
1191        result += "<input type=\"submit\" value=\"Apply Settings\" class=\"btn btn-primary\"/>";
1192        result += "</form>";
1193
1194        return result;
1195    }
1196}