package org.openiot.ide.core; /** * Copyright (c) 2011-2014, OpenIoT * * This file is part of OpenIoT. * * OpenIoT is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, version 3 of the License. * * OpenIoT is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with OpenIoT. If not, see <http://www.gnu.org/licenses/>. * * Contact: OpenIoT mailto: info@openiot.eu */ import org.apache.commons.collections.map.MultiValueMap; import org.apache.commons.lang3.StringUtils; import org.omnifaces.util.Faces; import org.openiot.commons.util.PropertyManagement; import org.primefaces.model.menu.DefaultMenuItem; import org.primefaces.model.menu.DefaultMenuModel; import org.primefaces.model.menu.DefaultSubMenu; import javax.annotation.PostConstruct; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import java.io.IOException; import java.io.Serializable; import java.net.HttpURLConnection; import java.net.URL; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author Chris Georgoulis e-mail: cgeo@ait.edu.gr */ @RequestScoped public class MenuFactory implements Serializable { private static final String IDE_CORE_GROUP = "ide.core.navigation."; private static final String IDE_CORE_RELATIVE_URL = "ide.core"; private static final String FIELD_TITLE = "title"; private static final String FIELD_MONITORING = "monitoring"; private static final String FIELD_URL = "url"; private static final String IDE_CORE_MONITORING_URL = Faces.getRequestDomainURL() + "/" + IDE_CORE_RELATIVE_URL + "/" + FIELD_MONITORING; private static final String SUBMENU_MAIN = "Main Menu"; private static final String SUBMENU_MONITORS = "Monitors"; private static final String MENU_ICON = "ui-icon-triangle-1-e"; private static final String CENTER_PANEL = ":centerPanel"; @Inject private Resources resources; private HashMap<String, HashMap<String, String>> propertyMap; @PostConstruct private void init() { propertyMap = createPropertyMap(); } /** * Creates the main menu according to the Injected Navigation map from Resources */ public DefaultMenuModel createMainMenu() { DefaultMenuModel menu = new DefaultMenuModel(); // Main SubMenu DefaultSubMenu mainMenu = new DefaultSubMenu(); mainMenu.setLabel(SUBMENU_MAIN); menu.addElement(mainMenu); // Monitor Submenu DefaultSubMenu monitorMenu = new DefaultSubMenu(); monitorMenu.setLabel(SUBMENU_MONITORS); menu.addElement(monitorMenu); // validateUrls(); // build submenus for (HashMap<String, String> map : propertyMap.values()) { // build main menu item try { DefaultMenuItem item = createMenuItem(map.get(FIELD_TITLE), map.get(FIELD_URL)); mainMenu.addElement(item); // build monitor item if (map.get(FIELD_MONITORING).equals("true")) { String monitoringUrl = createMonitorURL(map.get(FIELD_URL)); DefaultMenuItem monitorItem = createMenuItem(map.get(FIELD_TITLE), monitoringUrl); monitorMenu.addElement(monitorItem); } } catch (Exception e) { e.printStackTrace(); } } // add Ide Core Monitor DefaultMenuItem ideCoreMonitor = createMenuItem("IDE", IDE_CORE_MONITORING_URL); monitorMenu.addElement(ideCoreMonitor); return menu; } /** * Creates a single menu item and sets it's ActionListener to update the current navigation Url * * @param value * @param url * @return */ private DefaultMenuItem createMenuItem(String value, String url) { DefaultMenuItem item = new DefaultMenuItem(); item.setValue(value); item.setIcon(MENU_ICON); item.setAjax(true); item.setParam("navigation", url); String command = String.format("#{layout.setNavigation('%s')}", url); item.setCommand(command); item.setUpdate(CENTER_PANEL); return item; } /** * Groups the navigation properties in a Linked Hashmap where Key = the navigation group name * e.g. Request Presentation Values = a Hashmap with the group fields e.g. Url, Title, Active * Monitoring * * @return */ private HashMap<String, HashMap<String, String>> createPropertyMap() { HashMap<String, HashMap<String, String>> itemMap = null; HashMap<String, String> navigationMap = resources.getNavigationMap(); MultiValueMap groupMap = new MultiValueMap(); for (String key : navigationMap.keySet()) { String newKey = key.split(IDE_CORE_GROUP)[1]; Scanner sc = new Scanner(newKey).useDelimiter("\\."); String group = sc.next(); groupMap.put(group, sc.next()); sc.close(); } itemMap = new LinkedHashMap<>(); for (Object parentKey : groupMap.keySet()) { HashMap<String, String> childMap = new HashMap<>(); for (Object childKey : groupMap.getCollection(parentKey)) { String fullKey = IDE_CORE_GROUP + parentKey + "." + childKey; childMap.put((String) childKey, navigationMap.get(fullKey)); } itemMap.put((String) parentKey, childMap); } System.out.println("ItemMap size: " + itemMap.size()); return itemMap; } /** * checks if the base Url ends with a '/' character and appends accordingly * * @param baseUrl * @return */ private String createMonitorURL(String baseUrl) { // if (!StringUtils.endsWith(baseUrl, "/")) { // baseUrl += "/"; // } // int indexDomainUrl = StringUtils.indexOf(baseUrl, ); String suffix = StringUtils.substring(baseUrl, Faces.getRequestDomainURL().length(), baseUrl.length() - 1); String moduleName = StringUtils.split(suffix, "/")[0]; return Faces.getRequestDomainURL() + "/" + moduleName + "/" + FIELD_MONITORING; } /** * Creates */ private void validateUrls() { // TODO Remove comments for old way // Spawn a new Thread for each url Validation // List<Thread> threads = new ArrayList<>(propertyMap.size()); if (propertyMap.isEmpty()) return; ExecutorService es = Executors.newFixedThreadPool(propertyMap.entrySet().size()); for (Map.Entry<String, HashMap<String, String>> entry : propertyMap.entrySet()) { es.execute(new ValidatorRunnable(entry)); // threads.add(new Thread(new ValidatorRunnable(entry))); } es.shutdown(); try { es.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { e.printStackTrace(); } // alternative way to do the above with threads // for (Thread t : threads) { // t.start(); // } // // for (Thread t : threads) { // try { // t.join(); // } catch (InterruptedException e) { // e.printStackTrace(); // } // } } /** * This private class validates the URLs in the property map and accordingly removes the invalid * ones */ private class ValidatorRunnable implements Runnable { Map.Entry<String, HashMap<String, String>> entry; private ValidatorRunnable(Map.Entry<String, HashMap<String, String>> entry) { ValidatorRunnable.this.entry = entry; } @Override public void run() { String url = entry.getValue().get(FIELD_URL); boolean isUrlValid = isValid(url); if (!isUrlValid) { propertyMap.remove(entry.getKey()); } } public boolean isValid(String url) { boolean isValid = true; try { URL u = new URL(url); HttpURLConnection huc = (HttpURLConnection) u.openConnection(); huc.setRequestMethod("HEAD"); huc.connect(); isValid = huc.getResponseCode() == HttpURLConnection.HTTP_OK; } catch (IOException e) { e.printStackTrace(); } return isValid; } } public String generateLogoutScript(String iframeId, String defaultNavigation) { Pattern pattern = Pattern.compile("(http|https)://(.*?)/(.*?)/.*"); StringBuilder script = new StringBuilder("function logoutApps() { statusDialog.show(); document.getElementById('navigation').src='"); script.append(defaultNavigation).append("'; var ifrmWn = document.getElementById('").append(iframeId).append("');\n"); int i = 0; int time = 1000; for (HashMap<String, String> map : propertyMap.values()) { String url = map.get(FIELD_URL); Matcher matcher = pattern.matcher(url); if (matcher.matches()) { String logoutURL = url.substring(0, matcher.end(3)) + "/logout"; if (i > 0) script.append("setTimeout(function() {"); script.append("ifrmWn.src = '").append(logoutURL).append("'"); if (i > 0) script.append(";}, ").append(time * i).append(")"); script.append(";\n"); i += 1; } } PropertyManagement props = new PropertyManagement(); script.append("setTimeout(function() {statusDialog.hide(); var link = document.getElementById('logout-link'); link.href='").append(props.getCASLogoutURL()).append("'; link.click();},").append(time * i).append(");\n"); script.append("};"); return script.toString(); } }