/* * Copyright 2008-2017 by Emeric Vernat * * This file is part of Java Melody. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.bull.javamelody; // NOPMD import static net.bull.javamelody.HttpParameters.ACTION_PARAMETER; import static net.bull.javamelody.HttpParameters.APPLICATIONS_PART; import static net.bull.javamelody.HttpParameters.CACHE_ID_PARAMETER; import static net.bull.javamelody.HttpParameters.CACHE_KEYS_PART; import static net.bull.javamelody.HttpParameters.CACHE_KEY_PARAMETER; import static net.bull.javamelody.HttpParameters.CLASS_PARAMETER; import static net.bull.javamelody.HttpParameters.CONNECTIONS_PART; import static net.bull.javamelody.HttpParameters.COUNTER_PARAMETER; import static net.bull.javamelody.HttpParameters.COUNTER_SUMMARY_PER_CLASS_PART; import static net.bull.javamelody.HttpParameters.CURRENT_REQUESTS_PART; import static net.bull.javamelody.HttpParameters.DATABASE_PART; import static net.bull.javamelody.HttpParameters.DEPENDENCIES_PART; import static net.bull.javamelody.HttpParameters.EXPLAIN_PLAN_PART; import static net.bull.javamelody.HttpParameters.FORMAT_PARAMETER; import static net.bull.javamelody.HttpParameters.GRAPH_PARAMETER; import static net.bull.javamelody.HttpParameters.HEAP_HISTO_PART; import static net.bull.javamelody.HttpParameters.HOTSPOTS_PART; import static net.bull.javamelody.HttpParameters.HTML_BODY_FORMAT; import static net.bull.javamelody.HttpParameters.HTML_CONTENT_TYPE; import static net.bull.javamelody.HttpParameters.JMX_VALUE; import static net.bull.javamelody.HttpParameters.JNDI_PART; import static net.bull.javamelody.HttpParameters.JOB_ID_PARAMETER; import static net.bull.javamelody.HttpParameters.JROBINS_PART; import static net.bull.javamelody.HttpParameters.JVM_PART; import static net.bull.javamelody.HttpParameters.MBEANS_PART; import static net.bull.javamelody.HttpParameters.OTHER_JROBINS_PART; import static net.bull.javamelody.HttpParameters.PART_PARAMETER; import static net.bull.javamelody.HttpParameters.PATH_PARAMETER; import static net.bull.javamelody.HttpParameters.POM_XML_PART; import static net.bull.javamelody.HttpParameters.PROCESSES_PART; import static net.bull.javamelody.HttpParameters.REQUEST_PARAMETER; import static net.bull.javamelody.HttpParameters.SESSIONS_PART; import static net.bull.javamelody.HttpParameters.SESSION_ID_PARAMETER; import static net.bull.javamelody.HttpParameters.SOURCE_PART; import static net.bull.javamelody.HttpParameters.SPRING_BEANS_PART; import static net.bull.javamelody.HttpParameters.THREADS_PART; import static net.bull.javamelody.HttpParameters.THREAD_ID_PARAMETER; import static net.bull.javamelody.HttpParameters.WEB_XML_PART; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.Serializable; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import net.bull.javamelody.SamplingProfiler.SampledMethod; /** * Contrôleur au sens MVC de l'ihm de monitoring dans le serveur collecte. * @author Emeric Vernat */ class CollectorController { // NOPMD private static final Logger LOGGER = Logger.getLogger("javamelody"); private static final String COOKIE_NAME = "javamelody.application"; private static final boolean CSRF_PROTECTION_ENABLED = Boolean .parseBoolean(Parameters.getParameter(Parameter.CSRF_PROTECTION_ENABLED)); private final HttpCookieManager httpCookieManager = new HttpCookieManager(); private final CollectorServer collectorServer; CollectorController(CollectorServer collectorServer) { super(); assert collectorServer != null; this.collectorServer = collectorServer; } void addCollectorApplication(String appName, String appUrls) throws IOException { final File file = Parameters.getCollectorApplicationsFile(); if (file.exists() && !file.canWrite()) { throw new IllegalStateException( "applications should be added or removed in the applications.properties file, because the user is not allowed to write: " + file); } final List<URL> urls = Parameters.parseUrl(appUrls); collectorServer.addCollectorApplication(appName, urls); } void doMonitoring(HttpServletRequest req, HttpServletResponse resp, String application) throws IOException { try { final String actionParameter = req.getParameter(ACTION_PARAMETER); if (actionParameter != null) { if (CSRF_PROTECTION_ENABLED) { MonitoringController.checkCsrfToken(req); } final String messageForReport; if ("remove_application".equalsIgnoreCase(actionParameter)) { collectorServer.removeCollectorApplication(application); LOGGER.info("monitored application removed: " + application); messageForReport = I18N.getFormattedString("application_enlevee", application); showAlertAndRedirectTo(resp, messageForReport, "?"); return; } final Collector collector = getCollectorByApplication(application); final MonitoringController monitoringController = new MonitoringController( collector, collectorServer); final Action action = Action.valueOfIgnoreCase(actionParameter); if (action != Action.CLEAR_COUNTER && action != Action.MAIL_TEST && action != Action.PURGE_OBSOLETE_FILES) { // on forwarde l'action (gc, invalidate session(s) ou heap dump) sur l'application monitorée // et on récupère les informations à jour (notamment mémoire et nb de sessions) messageForReport = forwardActionAndUpdateData(req, application); } else { // nécessaire si action clear_counter messageForReport = monitoringController.executeActionIfNeeded(req); } if (TransportFormat.isATransportFormat(req.getParameter(FORMAT_PARAMETER))) { final SerializableController serializableController = new SerializableController( collector); final Range range = serializableController.getRangeForSerializable(req); final List<Object> serializable = new ArrayList<Object>(); final List<JavaInformations> javaInformationsList = getJavaInformationsByApplication( application); serializable.addAll((List<?>) serializableController.createDefaultSerializable( javaInformationsList, range, messageForReport)); monitoringController.doCompressedSerializable(req, resp, (Serializable) serializable); } else { writeMessage(req, resp, application, messageForReport); } return; } doReport(req, resp, application); } catch (final RuntimeException e) { // catch RuntimeException pour éviter warning exception writeMessage(req, resp, application, e.getMessage()); } catch (final Exception e) { writeMessage(req, resp, application, e.getMessage()); } } private void doReport(HttpServletRequest req, HttpServletResponse resp, String application) throws IOException, ServletException { final Collector collector = getCollectorByApplication(application); final MonitoringController monitoringController = new MonitoringController(collector, collectorServer); final String partParameter = req.getParameter(PART_PARAMETER); final String formatParameter = req.getParameter(FORMAT_PARAMETER); if (req.getParameter(JMX_VALUE) != null) { doJmxValue(req, resp, application, req.getParameter(JMX_VALUE)); } else if (TransportFormat.isATransportFormat(formatParameter)) { doCompressedSerializable(req, resp, application, monitoringController); } else if (partParameter == null || "pdf".equalsIgnoreCase(formatParameter)) { // la récupération de javaInformationsList doit être après forwardActionAndUpdateData // pour être à jour final List<JavaInformations> javaInformationsList = getJavaInformationsByApplication( application); monitoringController.doReport(req, resp, javaInformationsList); } else { doCompressedPart(req, resp, application, monitoringController, partParameter); } } private void doCompressedPart(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String application, MonitoringController monitoringController, String partParameter) throws IOException, ServletException { if (MonitoringController.isCompressionSupported(httpRequest)) { // comme la page html peut être volumineuse // on compresse le flux de réponse en gzip à partir de 4 Ko // (à moins que la compression http ne soit pas supportée // comme par ex s'il y a un proxy squid qui ne supporte que http 1.0) final CompressionServletResponseWrapper wrappedResponse = new CompressionServletResponseWrapper( httpResponse, 4096); try { doPart(httpRequest, wrappedResponse, application, monitoringController, partParameter); } finally { wrappedResponse.finishResponse(); } } else { doPart(httpRequest, httpResponse, application, monitoringController, partParameter); } } private void doPart(HttpServletRequest req, HttpServletResponse resp, String application, MonitoringController monitoringController, String partParameter) throws IOException, ServletException { if (WEB_XML_PART.equalsIgnoreCase(partParameter) || POM_XML_PART.equalsIgnoreCase(partParameter) || DEPENDENCIES_PART.equalsIgnoreCase(partParameter) || SPRING_BEANS_PART.equalsIgnoreCase(partParameter)) { noCache(resp); doProxy(req, resp, application, partParameter); } else if (SOURCE_PART.equalsIgnoreCase(partParameter)) { noCache(resp); doProxy(req, resp, application, partParameter + '&' + CLASS_PARAMETER + '=' + req.getParameter(CLASS_PARAMETER)); } else if (CONNECTIONS_PART.equalsIgnoreCase(partParameter)) { doMultiHtmlProxy(req, resp, application, CONNECTIONS_PART, I18N.getString("Connexions_jdbc_ouvertes"), I18N.getString("connexions_intro"), "db.png"); } else if (CACHE_KEYS_PART.equalsIgnoreCase(partParameter)) { // note: cache keys may not be serializable, so we do not try to serialize them final String cacheId = req.getParameter(CACHE_ID_PARAMETER); doMultiHtmlProxy(req, resp, application, CACHE_KEYS_PART + '&' + CACHE_ID_PARAMETER + '=' + cacheId, I18N.getFormattedString("Keys_cache", cacheId), null, "caches.png"); } else { final List<JavaInformations> javaInformationsList = getJavaInformationsByApplication( application); monitoringController.doReport(req, resp, javaInformationsList); } } private void doJmxValue(HttpServletRequest req, HttpServletResponse resp, String application, String jmxValueParameter) throws IOException { noCache(resp); resp.setContentType("text/plain"); boolean first = true; for (final URL url : getUrlsByApplication(application)) { if (first) { first = false; } else { resp.getOutputStream().write('|'); resp.getOutputStream().write('|'); } final URL proxyUrl = new URL( url.toString().replace(TransportFormat.SERIALIZED.getCode(), "") .replace(TransportFormat.XML.getCode(), "") + '&' + JMX_VALUE + '=' + jmxValueParameter); new LabradorRetriever(proxyUrl).copyTo(req, resp); } resp.getOutputStream().close(); } private void doProxy(HttpServletRequest req, HttpServletResponse resp, String application, String partParameter) throws IOException { // récupération à la demande du contenu du web.xml de la webapp monitorée // (et non celui du serveur de collecte), // on prend la 1ère url puisque le contenu de web.xml est censé être le même // dans tout l'éventuel cluster final URL url = getUrlsByApplication(application).get(0); // on récupère le contenu du web.xml sur la webapp et on transfert ce contenu final URL proxyUrl = new URL( url.toString().replace(TransportFormat.SERIALIZED.getCode(), HTML_BODY_FORMAT) .replace(TransportFormat.XML.getCode(), HTML_BODY_FORMAT) + '&' + PART_PARAMETER + '=' + partParameter); new LabradorRetriever(proxyUrl).copyTo(req, resp); } private void doMultiHtmlProxy(HttpServletRequest req, HttpServletResponse resp, String application, String partParameter, String title, String introduction, String iconName) throws IOException { final PrintWriter writer = createWriterFromOutputStream(resp); final HtmlReport htmlReport = createHtmlReport(req, resp, writer, application); htmlReport.writeHtmlHeader(); writer.write("<div class='noPrint'>"); I18N.writelnTo( "<a href='javascript:history.back()'><img src='?resource=action_back.png' alt='#Retour#'/> #Retour#</a>      ", writer); writer.write("<a href='?part="); writer.write(partParameter); writer.write("'>"); I18N.writelnTo("<img src='?resource=action_refresh.png' alt='#Actualiser#'/> #Actualiser#", writer); writer.write("</a></div>"); if (introduction != null) { writer.write("<br/>"); writer.write(I18N.htmlEncode(introduction, false)); } for (final URL url : getUrlsByApplication(application)) { final String htmlTitle = "<h3 class='chapterTitle'><img src='?resource=" + iconName + "' alt='" + I18N.urlEncode(title) + "'/> " + I18N.htmlEncode(title, false) + " (" + getHostAndPort(url) + ")</h3>"; writer.write(htmlTitle); writer.flush(); // flush du buffer de writer, sinon le copyTo passera avant dans l'outputStream final URL proxyUrl = new URL( url.toString().replace(TransportFormat.SERIALIZED.getCode(), HTML_BODY_FORMAT) .replace(TransportFormat.XML.getCode(), HTML_BODY_FORMAT) + '&' + PART_PARAMETER + '=' + partParameter); new LabradorRetriever(proxyUrl).copyTo(req, resp); } htmlReport.writeHtmlFooter(); writer.close(); } private void doCompressedSerializable(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String application, MonitoringController monitoringController) throws IOException { Serializable serializable; try { serializable = createSerializable(httpRequest, application); } catch (final Exception e) { serializable = e; } monitoringController.doCompressedSerializable(httpRequest, httpResponse, serializable); } private Serializable createSerializable(HttpServletRequest httpRequest, String application) throws Exception { // NOPMD final Serializable resultForSystemActions = createSerializableForSystemActions(httpRequest, application); if (resultForSystemActions != null) { return resultForSystemActions; } final Collector collector = getCollectorByApplication(application); final SerializableController serializableController = new SerializableController(collector); final Range range = serializableController.getRangeForSerializable(httpRequest); final String part = httpRequest.getParameter(PART_PARAMETER); if (THREADS_PART.equalsIgnoreCase(part)) { return new ArrayList<List<ThreadInformations>>( collectorServer.getThreadInformationsLists(application)); } else if (CURRENT_REQUESTS_PART.equalsIgnoreCase(part)) { return new LinkedHashMap<JavaInformations, List<CounterRequestContext>>( collectorServer.collectCurrentRequests(application)); } else if (EXPLAIN_PLAN_PART.equalsIgnoreCase(part)) { final String sqlRequest = httpRequest.getHeader(REQUEST_PARAMETER); return collectorServer.collectSqlRequestExplainPlan(application, sqlRequest); } else if (COUNTER_SUMMARY_PER_CLASS_PART.equalsIgnoreCase(part)) { final String counterName = httpRequest.getParameter(COUNTER_PARAMETER); final String requestId = httpRequest.getParameter(GRAPH_PARAMETER); final Counter counter = collector.getRangeCounter(range, counterName); final List<CounterRequest> requestList = new CounterRequestAggregation(counter) .getRequestsAggregatedOrFilteredByClassName(requestId); return new ArrayList<CounterRequest>(requestList); } else if (APPLICATIONS_PART.equalsIgnoreCase(part)) { // list all applications, with last exceptions if not available, // use ?part=applications&format=json for example final Map<String, Throwable> applications = new HashMap<String, Throwable>(); for (final String app : Parameters.getCollectorUrlsByApplications().keySet()) { applications.put(app, null); } applications.putAll(collectorServer.getLastCollectExceptionsByApplication()); return new HashMap<String, Throwable>(applications); } else if (JROBINS_PART.equalsIgnoreCase(part) || OTHER_JROBINS_PART.equalsIgnoreCase(part)) { // pour UI Swing return serializableController.createSerializable(httpRequest, null, null); } final List<JavaInformations> javaInformationsList = getJavaInformationsByApplication( application); return serializableController.createDefaultSerializable(javaInformationsList, range, null); } // CHECKSTYLE:OFF private Serializable createSerializableForSystemActions(HttpServletRequest httpRequest, String application) throws IOException { // CHECKSTYLE:ON final String part = httpRequest.getParameter(PART_PARAMETER); if (JVM_PART.equalsIgnoreCase(part)) { final List<JavaInformations> javaInformationsList = getJavaInformationsByApplication( application); return new ArrayList<JavaInformations>(javaInformationsList); } else if (HEAP_HISTO_PART.equalsIgnoreCase(part)) { // par sécurité Action.checkSystemActionsEnabled(); return collectorServer.collectHeapHistogram(application); } else if (SESSIONS_PART.equalsIgnoreCase(part)) { // par sécurité Action.checkSystemActionsEnabled(); final String sessionId = httpRequest.getParameter(SESSION_ID_PARAMETER); final List<SessionInformations> sessionInformations = collectorServer .collectSessionInformations(application, sessionId); if (sessionId != null && !sessionInformations.isEmpty()) { return sessionInformations.get(0); } return new ArrayList<SessionInformations>(sessionInformations); } else if (HOTSPOTS_PART.equalsIgnoreCase(part)) { // par sécurité Action.checkSystemActionsEnabled(); return new ArrayList<SampledMethod>(collectorServer.collectHotspots(application)); } else if (PROCESSES_PART.equalsIgnoreCase(part)) { // par sécurité Action.checkSystemActionsEnabled(); return new LinkedHashMap<String, List<ProcessInformations>>( collectorServer.collectProcessInformations(application)); } else if (JNDI_PART.equalsIgnoreCase(part)) { // par sécurité Action.checkSystemActionsEnabled(); final String path = httpRequest.getParameter(PATH_PARAMETER); return new ArrayList<JndiBinding>( collectorServer.collectJndiBindings(application, path)); } else if (MBEANS_PART.equalsIgnoreCase(part)) { // par sécurité Action.checkSystemActionsEnabled(); return new LinkedHashMap<String, List<MBeanNode>>( collectorServer.collectMBeans(application)); } else if (DATABASE_PART.equalsIgnoreCase(part)) { // par sécurité Action.checkSystemActionsEnabled(); final int requestIndex = DatabaseInformations .parseRequestIndex(httpRequest.getParameter(REQUEST_PARAMETER)); return collectorServer.collectDatabaseInformations(application, requestIndex); } else if (CONNECTIONS_PART.equalsIgnoreCase(part)) { // par sécurité Action.checkSystemActionsEnabled(); return new ArrayList<List<ConnectionInformations>>( collectorServer.collectConnectionInformations(application)); } return null; } private HtmlReport createHtmlReport(HttpServletRequest req, HttpServletResponse resp, PrintWriter writer, String application) { final Range range = httpCookieManager.getRange(req, resp); final Collector collector = getCollectorByApplication(application); final List<JavaInformations> javaInformationsList = getJavaInformationsByApplication( application); return new HtmlReport(collector, collectorServer, javaInformationsList, range, writer); } private static String getHostAndPort(URL url) { return RemoteCollector.getHostAndPort(url); } void writeMessage(HttpServletRequest req, HttpServletResponse resp, String application, String message) throws IOException { noCache(resp); final Collector collector = getCollectorByApplication(application); final List<JavaInformations> javaInformationsList = getJavaInformationsByApplication( application); if (application == null || collector == null || javaInformationsList == null) { showAlertAndRedirectTo(resp, message, "?"); } else { final PrintWriter writer = createWriterFromOutputStream(resp); final String partToRedirectTo; if (req.getParameter(CACHE_ID_PARAMETER) == null) { partToRedirectTo = req.getParameter(PART_PARAMETER); } else { partToRedirectTo = req.getParameter(PART_PARAMETER) + '&' + CACHE_ID_PARAMETER + '=' + req.getParameter(CACHE_ID_PARAMETER); } // la période n'a pas d'importance pour writeMessageIfNotNull new HtmlReport(collector, collectorServer, javaInformationsList, Period.TOUT, writer) .writeMessageIfNotNull(message, partToRedirectTo); writer.close(); } } private static PrintWriter createWriterFromOutputStream(HttpServletResponse httpResponse) throws IOException { noCache(httpResponse); httpResponse.setContentType(HTML_CONTENT_TYPE); return new PrintWriter(MonitoringController.getWriter(httpResponse)); } static void writeOnlyAddApplication(HttpServletResponse resp) throws IOException { noCache(resp); resp.setContentType(HTML_CONTENT_TYPE); final PrintWriter writer = createWriterFromOutputStream(resp); writer.write("<html lang='" + I18N.getCurrentLocale().getLanguage() + "'><head><title>Monitoring</title></head><body>"); HtmlReport.writeAddAndRemoveApplicationLinks(null, writer); writer.write("</body></html>"); writer.close(); } static void writeDataUnavailableForApplication(String application, HttpServletResponse resp) throws IOException { noCache(resp); resp.setContentType(HTML_CONTENT_TYPE); final PrintWriter writer = createWriterFromOutputStream(resp); writer.write("<html lang='" + I18N.getCurrentLocale().getLanguage() + "'><head><title>Monitoring</title></head><body>"); writer.write( I18N.htmlEncode(I18N.getFormattedString("data_unavailable", application), false)); writer.write("<br/><br/>"); writer.write("<a href='?'><img src='?resource=action_back.png' alt=\"" + I18N.getString("Retour") + "\"/> " + I18N.getString("Retour") + "</a>"); if (Parameters.getCollectorApplicationsFile().canWrite()) { writer.write("     "); writer.write("<a href='?action=remove_application&application=" + I18N.urlEncode(application) + HtmlAbstractReport.getCsrfTokenUrlPart() + "' "); final String messageConfirmation = I18N.getFormattedString("confirm_remove_application", application); writer.write("onclick=\"javascript:return confirm('" + I18N.javascriptEncode(messageConfirmation) + "');\">"); final String removeApplicationLabel = I18N.getFormattedString("remove_application", application); writer.write("<img src='?resource=action_delete.png' alt=\"" + removeApplicationLabel + "\"/> " + removeApplicationLabel + "</a>"); } writer.write("</body></html>"); writer.close(); } static void showAlertAndRedirectTo(HttpServletResponse resp, String message, String redirectTo) throws IOException { resp.setContentType(HTML_CONTENT_TYPE); final PrintWriter writer = createWriterFromOutputStream(resp); writer.write("<script type='text/javascript'>alert('"); writer.write(I18N.javascriptEncode(message)); writer.write("');location.href='"); writer.write(redirectTo); writer.write("';</script>"); writer.close(); } private static void noCache(HttpServletResponse httpResponse) { MonitoringController.noCache(httpResponse); } private String forwardActionAndUpdateData(HttpServletRequest req, String application) throws IOException { final String actionParameter = req.getParameter(ACTION_PARAMETER); final String sessionIdParameter = req.getParameter(SESSION_ID_PARAMETER); final String threadIdParameter = req.getParameter(THREAD_ID_PARAMETER); final String jobIdParameter = req.getParameter(JOB_ID_PARAMETER); final String cacheIdParameter = req.getParameter(CACHE_ID_PARAMETER); final String cacheKeyParameter = req.getParameter(CACHE_KEY_PARAMETER); final List<URL> urls = getUrlsByApplication(application); final List<URL> actionUrls = new ArrayList<URL>(urls.size()); for (final URL url : urls) { final StringBuilder actionUrl = new StringBuilder(url.toString()); actionUrl.append("&action=").append(actionParameter); if (sessionIdParameter != null) { actionUrl.append("&sessionId=").append(sessionIdParameter); } if (threadIdParameter != null) { actionUrl.append("&threadId=").append(threadIdParameter); } if (jobIdParameter != null) { actionUrl.append("&jobId=").append(jobIdParameter); } if (cacheIdParameter != null) { actionUrl.append("&cacheId=").append(cacheIdParameter); } if (cacheKeyParameter != null) { actionUrl.append("&cacheKey=").append(cacheKeyParameter); } actionUrls.add(new URL(actionUrl.toString())); } return collectorServer.collectForApplicationForAction(application, actionUrls); } String getApplication(HttpServletRequest req, HttpServletResponse resp) { // on utilise un cookie client pour stocker l'application // car la page html est faite pour une seule application sans passer son nom en paramètre des requêtes // et pour ne pas perdre l'application choisie entre les reconnexions String application = req.getParameter("application"); if (application == null) { // pas de paramètre application dans la requête, on cherche le cookie final Cookie cookie = httpCookieManager.getCookieByName(req, COOKIE_NAME); if (cookie != null) { application = cookie.getValue(); if (!collectorServer.isApplicationDataAvailable(application)) { cookie.setMaxAge(-1); resp.addCookie(cookie); application = null; } } if (application == null) { // pas de cookie, on prend la première application si elle existe application = collectorServer.getFirstApplication(); } } else if (collectorServer.isApplicationDataAvailable(application)) { // un paramètre application est présent dans la requête: l'utilisateur a choisi une application, // donc on fixe le cookie httpCookieManager.addCookie(req, resp, COOKIE_NAME, String.valueOf(application)); } return application; } private Collector getCollectorByApplication(String application) { return collectorServer.getCollectorByApplication(application); } private List<JavaInformations> getJavaInformationsByApplication(String application) { return collectorServer.getJavaInformationsByApplication(application); } private static List<URL> getUrlsByApplication(String application) throws IOException { return CollectorServer.getUrlsByApplication(application); } }