/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.syncope.client.console.topology; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.rest.ConnectorRestClient; import org.apache.syncope.client.console.rest.ResourceRestClient; import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.wicket.Application; import org.apache.wicket.Session; import org.apache.wicket.ThreadContext; import org.apache.wicket.protocol.ws.api.WebSocketBehavior; import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler; import org.apache.wicket.protocol.ws.api.message.TextMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TopologyWebSocketBehavior extends WebSocketBehavior { private static final long serialVersionUID = -1653665542635275551L; private static final Logger LOG = LoggerFactory.getLogger(TopologyWebSocketBehavior.class); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private final Map<String, String> resources = new HashMap<>(); private final Set<String> runningResCheck = new HashSet<>(); private final Map<String, String> connectors = new HashMap<>(); private final Set<String> runningConnCheck = new HashSet<>(); private final ConnectorRestClient connectorRestClient = new ConnectorRestClient(); private final ResourceRestClient resourceRestClient = new ResourceRestClient(); @Override protected void onMessage(final WebSocketRequestHandler handler, final TextMessage message) { try { JsonNode obj = OBJECT_MAPPER.readTree(message.getText()); switch (Topology.SupportedOperation.valueOf(obj.get("kind").asText())) { case CHECK_CONNECTOR: final String ckey = obj.get("target").asText(); if (connectors.containsKey(ckey)) { handler.push(connectors.get(ckey)); } else { handler.push(String.format( "{ \"status\": \"%s\", \"target\": \"%s\"}", TopologyNode.Status.UNKNOWN, ckey)); } synchronized (runningConnCheck) { if (runningConnCheck.contains(ckey)) { LOG.debug("Running connection check for connector {}", ckey); } else { runningConnCheck.add(ckey); } } SyncopeConsoleSession.get().execute(new ConnCheck(ckey)); break; case CHECK_RESOURCE: final String rkey = obj.get("target").asText(); if (resources.containsKey(rkey)) { handler.push(resources.get(rkey)); } else { handler.push(String.format( "{ \"status\": \"%s\", \"target\": \"%s\"}", TopologyNode.Status.UNKNOWN, rkey)); } synchronized (runningResCheck) { if (runningResCheck.contains(rkey)) { LOG.debug("Running connection check for resource {}", rkey); } else { runningResCheck.add(rkey); } } SyncopeConsoleSession.get().execute(new ResCheck(rkey)); break; case ADD_ENDPOINT: handler.appendJavaScript(String.format("addEndpoint('%s', '%s', '%s');", obj.get("source").asText(), obj.get("target").asText(), obj.get("scope").asText())); break; default: } } catch (IOException e) { LOG.error("Eror managing websocket message", e); } } class ConnCheck implements Runnable { private final String key; private final Application application; private final Session session; ConnCheck(final String key) { this.key = key; this.application = Application.get(); this.session = Session.exists() ? Session.get() : null; } @Override public void run() { try { ThreadContext.setApplication(application); ThreadContext.setSession(session); String res; try { final ConnInstanceTO connector = connectorRestClient.read(key); res = String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", connectorRestClient.check(connector).getLeft() ? TopologyNode.Status.REACHABLE : TopologyNode.Status.UNREACHABLE, key); } catch (Exception e) { LOG.warn("Error checking connection for {}", key, e); res = String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", TopologyNode.Status.FAILURE, key); } synchronized (runningConnCheck) { connectors.put(key, res); runningConnCheck.remove(key); } } finally { ThreadContext.detach(); } } } class ResCheck implements Runnable { private final String key; private final Application application; private final Session session; ResCheck(final String key) { this.key = key; this.application = Application.get(); this.session = Session.exists() ? Session.get() : null; } @Override public void run() { try { ThreadContext.setApplication(application); ThreadContext.setSession(session); String res; try { final ResourceTO resource = resourceRestClient.read(key); res = String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", resourceRestClient.check(resource).getLeft() ? TopologyNode.Status.REACHABLE : TopologyNode.Status.UNREACHABLE, key); } catch (Exception e) { LOG.warn("Error checking connection for {}", key, e); res = String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", TopologyNode.Status.FAILURE, key); } synchronized (runningResCheck) { resources.put(key, res); runningResCheck.remove(key); } } finally { ThreadContext.detach(); } } } }