/* (c) 2016 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wps.remote; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import org.apache.vysper.xml.fragment.XMLSemanticError; import org.apache.vysper.xmpp.addressing.Entity; import org.apache.vysper.xmpp.addressing.EntityImpl; import org.apache.vysper.xmpp.server.XMPPServer; import org.apache.vysper.xmpp.stanza.PresenceStanza; import org.apache.vysper.xmpp.stanza.PresenceStanzaType; import org.apache.vysper.xmpp.stanza.StanzaBuilder; import org.apache.vysper.xmpp.state.presence.LatestPresenceCache; import org.apache.vysper.xmpp.state.presence.PresenceCachingException; import org.geoserver.data.test.SystemTestData; import org.geoserver.wps.WPSTestSupport; import org.geoserver.wps.remote.plugin.MockRemoteClient; import org.geoserver.wps.remote.plugin.XMPPClient; import org.geoserver.wps.remote.plugin.XMPPLoadAverageMessage; import org.geoserver.wps.remote.plugin.XMPPMessage; import org.geoserver.wps.remote.plugin.XMPPRegisterMessage; import org.geoserver.wps.remote.plugin.server.XMPPServerEmbedded; import org.geotools.factory.FactoryIteratorProvider; import org.geotools.factory.GeoTools; import org.geotools.feature.NameImpl; import org.geotools.process.ProcessFactory; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Presence.Type; import org.junit.Test; import org.opengis.feature.type.Name; /** * This class tests checks if the RemoteProcess class behaves correctly. * * @author "Alessio Fabiani - alessio.fabiani@geo-solutions.it" */ public class RemoteProcessTest extends WPSTestSupport { private static final boolean DISABLE = "false" .equalsIgnoreCase(System.getProperty("disableTest", "true")); private RemoteProcessFactory factory; @Override protected void setUpSpring(List<String> springContextLocations) { super.setUpSpring(springContextLocations); springContextLocations.add("testRemoteAppContext.xml"); } @Override protected void onSetUp(SystemTestData testData) throws Exception { super.onSetUp(testData); } @Override protected void setUpTestData(SystemTestData testData) throws Exception { super.setUpTestData(testData); // add limits properties file testData.copyTo(RemoteProcessTest.class.getClassLoader().getResourceAsStream( "remote-process/remoteProcess.properties"), "remoteProcess.properties"); } @Test public void testNames() { setupFactory(); assertNotNull(factory); Set<Name> names = factory.getNames(); assertNotNull(names); assertTrue(names.size() == 0); final NameImpl name = new NameImpl("default", "Service"); factory.registerProcess( new RemoteServiceDescriptor(name, "Service", "A test service", null, null, null)); assertTrue(names.size() == 1); assertTrue(names.contains(name)); factory.deregisterProcess(name); assertTrue(names.size() == 0); } @Test public void testListeners() { setupFactory(); assertNotNull(factory); RemoteProcessClient remoteClient = factory.getRemoteClient(); assertNotNull(remoteClient); assertTrue(remoteClient instanceof MockRemoteClient); Set<Name> names = factory.getNames(); assertNotNull(names); assertTrue(names.size() == 0); final NameImpl name = new NameImpl("default", "Service"); try { remoteClient.execute(name, null, null, null); assertTrue(names.size() == 1); assertTrue(names.contains(name)); factory.deregisterProcess(name); } catch (Exception e) { e.printStackTrace(); fail(e.getLocalizedMessage()); } finally { assertTrue(names.size() == 0); } } @Test public void testXMPPClient() { if (DISABLE) { return; } setupFactory(); XMPPServerEmbedded server = null; XMPPClient xmppRemoteClient = null; try { final RemoteProcessFactoryConfiguration configuration = factory.getRemoteClient().getConfiguration(); final String xmppDomain = configuration.get("xmpp_domain"); final String xmppUserName = configuration.get("xmpp_manager_username"); final String[] serviceChannels = configuration.get("xmpp_service_channels").split(","); final Entity adminJID = EntityImpl.parseUnchecked(xmppUserName + "@" + xmppDomain); // / server = (XMPPServerEmbedded) applicationContext.getBean("xmppServerEmbedded"); assertNotNull(server); Thread.sleep(3000); LOGGER.info("vysper server is running..."); // / xmppRemoteClient = (XMPPClient) applicationContext.getBean("xmppRemoteProcessClient"); assertNotNull(xmppRemoteClient); xmppRemoteClient.init(); // / Thread.sleep(1000); Presence presence = getPresence(server, adminJID); assertNotNull(presence); assertTrue(presence.isAvailable()); assertTrue("Orchestrator Active".equals(presence.getStatus())); List<RemoteMachineDescriptor> remoteMachines = xmppRemoteClient.getRegisteredProcessingMachines(); assertNotNull(remoteMachines); assertTrue(remoteMachines.size() > 0); for (RemoteMachineDescriptor machine : remoteMachines) { if(!"test".equals(machine.getServiceName().getNamespaceURI())) { assertTrue(xmppUserName.equals(machine.getServiceName().getLocalPart())); assertTrue(Arrays.asList(serviceChannels).contains(machine.getServiceName().getNamespaceURI())); assertTrue(machine.getNodeJID().equals(machine.getServiceName().getNamespaceURI() + "@" + configuration.get("xmpp_bus") + "." + xmppDomain + "/" + xmppUserName)); } } } catch (Exception e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); fail(e.getLocalizedMessage()); } finally { if (server != null) { try { xmppRemoteClient.destroy(); server.stop(); } catch (Exception e) { LOGGER.log(Level.WARNING, e.getMessage(), e); } } } } private static Type type(PresenceStanza stanza) { if (PresenceStanzaType.isAvailable(stanza.getPresenceType())) { return Type.available; } else { return Type.unavailable; } } private Presence getPresence(XMPPServerEmbedded xmpp, Entity userJID) { LatestPresenceCache presenceCache = xmpp.getServerRuntimeContext().getPresenceCache(); PresenceStanza presence = presenceCache.getForBareJID(userJID); if (presence == null) { return new Presence(Type.unavailable, "AWAY", 0, null); } else { try { Presence p = new Presence(type(presence), presence.getStatus(null), 0, null); return p; } catch (XMLSemanticError e) { LOGGER.log(Level.SEVERE, "Exception getting presence status", e); return new Presence(Type.unavailable, "AWAY", 0, null); } } } private PresenceStanza presenceStanza(Entity userJID, Presence presence, Entity to) throws XMLSemanticError { Entity from = userJID; String lang = null; String show = "ONLINE"; String status = presence.getStatus(); Type presenceType = presence.getType(); PresenceStanzaType type = presenceType == Type.unavailable ? PresenceStanzaType.UNAVAILABLE : null; StanzaBuilder b = StanzaBuilder.createPresenceStanza(from, to, lang, type, show, status); return new PresenceStanza(b.build()); } private void updatePresenceCache(XMPPServer xmpp, Entity userJID, Presence presence) throws PresenceCachingException, XMLSemanticError { LatestPresenceCache presenceCache = xmpp.getServerRuntimeContext().getPresenceCache(); presenceCache.put(userJID, presenceStanza(userJID, presence, null)); } @Test public void testRegisterMessage() { // / XMPPClient xmppRemoteClient = (XMPPClient) applicationContext .getBean("xmppRemoteProcessClient"); assertNotNull(xmppRemoteClient); XMPPMessage msg = new XMPPRegisterMessage(); // build register body Map<String, String> signalArgs = new HashMap<String, String>(); signalArgs.put("topic", "register"); signalArgs.put("service", "test.Service"); /** * JSON URL Encoded Body * * { "title": "test.Service", "description": "This is a test Service!", "input": [ ["simpleType", * "{\"type\": \"string\", \"description\": \"A simple string parameter\", \"max\": 1}"], ["complexType", * "{\"type\": \"complex\", \"description\": \"A complex parameter\", \"min\": 1, \"max\": 10}"] ] } */ signalArgs.put("message", "%7B%0A%20%20%22title%22%3A%20%22test.Service%22%2C%0A%20%20%22description%22%3A%20%22This%20is%20a%20test%20Service!%22%2C%0A%20%20%22input%22%3A%20%5B%0A%20%20%20%20%5B%22simpleType%22%2C%20%22%7B%5C%22type%5C%22%3A%20%5C%22string%5C%22%2C%20%5C%22description%5C%22%3A%20%5C%22A%20simple%20string%20parameter%5C%22%2C%20%5C%22max%5C%22%3A%201%7D%22%5D%2C%0A%20%20%20%20%5B%22complexType%22%2C%20%22%7B%5C%22type%5C%22%3A%20%5C%22complex%5C%22%2C%20%5C%22description%5C%22%3A%20%5C%22A%20complex%20parameter%5C%22%2C%20%5C%22min%5C%22%3A%201%2C%20%5C%22max%5C%22%3A%2010%7D%22%5D%0A%20%20%5D%0A%7D"); // handle signal Packet packet = new Packet() { @Override public String getFrom() { return "test@geoserver.org"; } @Override public CharSequence toXML() { return null; } }; msg.handleSignal(xmppRemoteClient, packet, null, signalArgs); } @Test public void testLoadAverageMessage() { // / XMPPClient xmppRemoteClient = (XMPPClient) applicationContext .getBean("xmppRemoteProcessClient"); assertNotNull(xmppRemoteClient); List<RemoteMachineDescriptor> registeredProcessingMachines = new ArrayList<RemoteMachineDescriptor>(); registeredProcessingMachines.add(new RemoteMachineDescriptor("test@geoserver.org", new NameImpl("test", "Service"), true, 90.0, 90.0)); xmppRemoteClient.setRegisteredProcessingMachines(registeredProcessingMachines); XMPPMessage msg = new XMPPLoadAverageMessage(); // build register body Map<String, String> signalArgs = new HashMap<String, String>(); signalArgs.put("topic", "loadavg"); signalArgs.put("message", "loadavg"); signalArgs.put("service", "test.Service"); signalArgs.put("id", "master"); /** * JSON URL Encoded Body * * { "title": "test.Service", "description": "This is a test Service!", "input": [ ["simpleType", * "{\"type\": \"string\", \"description\": \"A simple string parameter\", \"max\": 1}"], ["complexType", * "{\"type\": \"complex\", \"description\": \"A complex parameter\", \"min\": 1, \"max\": 10}"] ] } */ signalArgs.put("result_vmem", "%7B%22vmem_value%22%3A%2089.3%2C%20%22vmem_description%22%3A%20%22Percentage%20of%20Memory%20used%20by%20the%20server.%22%7D"); signalArgs.put("result_loadavg", "%7B%22loadavg_description%22%3A%20%22Average%20Load%20on%20CPUs%20during%20the%20last%2015%20minutes.%22%2C%20%22loadavg_value%22%3A%2014.6%7D"); // handle signal Packet packet = new Packet() { @Override public String getFrom() { return "test@geoserver.org"; } @Override public CharSequence toXML() { return null; } }; msg.handleSignal(xmppRemoteClient, packet, null, signalArgs); assertTrue("LoadAverage does not match!", registeredProcessingMachines.get(0).getLoadAverage().equals(14.6)); assertTrue("MemoryPerc does not match!", registeredProcessingMachines.get(0).getMemPercUsed().equals(89.3)); } /** * */ protected void setupFactory() { if (factory == null) { factory = new RemoteProcessFactory(); // check SPI will see the factory if we register it using an iterator // provider GeoTools.addFactoryIteratorProvider(new FactoryIteratorProvider() { public <T> Iterator<T> iterator(Class<T> category) { if (ProcessFactory.class.isAssignableFrom(category)) { return (Iterator<T>) Collections.singletonList(factory).iterator(); } else { return null; } } }); } } /** * * @param fname * * @throws IOException */ private static InputStream fullStream(File fname) throws IOException { FileInputStream fis = new FileInputStream(fname); DataInputStream dis = new DataInputStream(fis); byte[] bytes = new byte[dis.available()]; dis.readFully(bytes); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); return bais; } }