/* (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.cluster; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.StyleHandler; import org.geoserver.catalog.StyleInfo; import org.geoserver.catalog.Styles; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.catalog.event.CatalogEvent; import org.geoserver.catalog.event.CatalogModifyEvent; import org.geoserver.cluster.impl.handlers.DocumentFile; import org.geoserver.cluster.impl.handlers.catalog.JMSCatalogAddEventHandlerSPI; import org.geoserver.cluster.impl.handlers.catalog.JMSCatalogModifyEventHandlerSPI; import org.geoserver.cluster.impl.handlers.catalog.JMSCatalogRemoveEventHandlerSPI; import org.geoserver.cluster.impl.handlers.catalog.JMSCatalogStylesFileHandlerSPI; import org.geoserver.cluster.server.events.StyleModifyEvent; import org.geoserver.data.test.MockData; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.test.GeoServerSystemTestSupport; import org.geotools.styling.RasterSymbolizer; import org.geotools.styling.Style; import org.geotools.styling.StyledLayerDescriptor; import org.junit.After; import org.junit.Before; import org.junit.Test; import javax.jms.Message; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.List; import static org.geoserver.cluster.JmsEventsListener.getMessagesForHandler; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; /** * Tests related with styles events. */ public final class JmsStylesTest extends GeoServerSystemTestSupport { private static final String TEST_STYLE_NAME = "test_style"; private static final String TEST_STYLE_FILE = "/test_style.sld"; private static final String TEST_WORKSPACE_NAME = MockData.DEFAULT_PREFIX; private static final String CATALOG_ADD_EVENT_HANDLER_KEY = "JMSCatalogAddEventHandlerSPI"; private static final String CATALOG_MODIFY_EVENT_HANDLER_KEY = "JMSCatalogModifyEventHandlerSPI"; private static final String CATALOG_STYLES_FILE_EVENT_HANDLER_KEY = "JMSCatalogStylesFileHandlerSPI"; private static final String CATALOG_REMOVE_EVENT_HANDLER_KEY = "JMSCatalogRemoveEventHandlerSPI"; private WorkspaceInfo testWorkspace; private JMSEventHandler<String, DocumentFile> styleFileHandler; private JMSEventHandler<String, CatalogEvent> addEventHandler; private JMSEventHandler<String, CatalogEvent> modifyEventHandler; private JMSEventHandler<String, CatalogEvent> removeEventHandler; @Override protected void setUpSpring(List<String> springContextLocations) { super.setUpSpring(springContextLocations); // adding our test spring context springContextLocations.add("classpath:TestContext.xml"); } @Before public void beforeTest() { // get the test workspace from the catalog testWorkspace = getCatalog().getWorkspaceByName(TEST_WORKSPACE_NAME); assertThat(testWorkspace, notNullValue()); // initiate the handlers related to styles styleFileHandler = GeoServerExtensions.bean(JMSCatalogStylesFileHandlerSPI.class).createHandler(); assertThat(styleFileHandler, notNullValue()); addEventHandler = GeoServerExtensions.bean(JMSCatalogAddEventHandlerSPI.class).createHandler(); assertThat(addEventHandler, notNullValue()); modifyEventHandler = GeoServerExtensions.bean(JMSCatalogModifyEventHandlerSPI.class).createHandler(); assertThat(modifyEventHandler, notNullValue()); removeEventHandler = GeoServerExtensions.bean(JMSCatalogRemoveEventHandlerSPI.class).createHandler(); assertThat(removeEventHandler, notNullValue()); } @After public void afterTest() { // search the test style in the catalog Catalog catalog = getCatalog(); StyleInfo style = catalog.getStyleByName(TEST_STYLE_NAME); if (style != null) { // the test style exists so let's remove it catalog.remove(style); } // search the test style in the catalog by workspace style = catalog.getStyleByName(MockData.DEFAULT_PREFIX, TEST_STYLE_NAME); if (style != null) { // the test style exists so let's remove it catalog.remove(style); } // clear all pending events JmsEventsListener.clear(); } @Test public void testAddStyle() throws Exception { // add the test to the style catalog getTestData().addStyle(TEST_STYLE_NAME, TEST_STYLE_FILE, this.getClass(), getCatalog()); // waiting for a catalog add event and a style file event List<Message> messages = JmsEventsListener.getMessagesByHandlerKey(5000, (selected) -> selected.size() >= 2, CATALOG_ADD_EVENT_HANDLER_KEY, CATALOG_STYLES_FILE_EVENT_HANDLER_KEY); // let's check if the new added style was correctly published assertThat(messages.size(), is(2)); // checking that the correct style file was published List<DocumentFile> styleFile = getMessagesForHandler(messages, CATALOG_STYLES_FILE_EVENT_HANDLER_KEY, styleFileHandler); assertThat(styleFile.size(), is(1)); assertThat(styleFile.get(0).getResourceName(), is("test_style.sld")); // checking that the correct style was published List<CatalogEvent> styleAddEvent = getMessagesForHandler(messages, CATALOG_ADD_EVENT_HANDLER_KEY, addEventHandler); assertThat(styleAddEvent.size(), is(1)); assertThat(styleAddEvent.get(0).getSource(), instanceOf(StyleInfo.class)); StyleInfo styleInfo = (StyleInfo) styleAddEvent.get(0).getSource(); assertThat(styleInfo.getName(), is(TEST_STYLE_NAME)); assertThat(styleInfo.getWorkspace(), nullValue()); } @Test public void testAddStyleToWorkspace() throws Exception { // add the test to the style catalog getTestData().addStyle(testWorkspace, TEST_STYLE_NAME, TEST_STYLE_FILE, this.getClass(), getCatalog()); // waiting for a catalog add event and a style file event List<Message> messages = JmsEventsListener.getMessagesByHandlerKey(5000, (selected) -> selected.size() >= 2, CATALOG_ADD_EVENT_HANDLER_KEY, CATALOG_STYLES_FILE_EVENT_HANDLER_KEY); // let's check if the new added style was correctly published assertThat(messages.size(), is(2)); // checking that the correct style file was published List<DocumentFile> styleFile = getMessagesForHandler(messages, CATALOG_STYLES_FILE_EVENT_HANDLER_KEY, styleFileHandler); assertThat(styleFile.size(), is(1)); assertThat(styleFile.get(0).getResourceName(), is("test_style.sld")); // checking that the correct style was published List<CatalogEvent> styleAddEvent = getMessagesForHandler(messages, CATALOG_ADD_EVENT_HANDLER_KEY, addEventHandler); assertThat(styleAddEvent.size(), is(1)); assertThat(styleAddEvent.get(0).getSource(), instanceOf(StyleInfo.class)); StyleInfo styleInfo = (StyleInfo) styleAddEvent.get(0).getSource(); assertThat(styleInfo.getName(), is(TEST_STYLE_NAME)); assertThat(styleInfo.getWorkspace(), is(testWorkspace)); } @Test public void testModifyStyleWorkspace() throws Exception { // add the test to the style catalog addTestStyle(); // modify the style associated file StyleInfo styleInfo = getCatalog().getStyleByName(TEST_STYLE_NAME); assertThat(styleInfo, notNullValue()); getCatalog().getResourcePool().writeStyle(styleInfo, JmsStylesTest.class.getResourceAsStream("/test_style_modified.sld")); // modify the style workspace styleInfo.setWorkspace(testWorkspace); getCatalog().save(styleInfo); // waiting for a catalog modify event and a style file event List<Message> messages = JmsEventsListener.getMessagesByHandlerKey(5000, (selected) -> selected.size() >= 1, CATALOG_MODIFY_EVENT_HANDLER_KEY); assertThat(messages.size(), is(1)); // checking that the correct catalog style was published List<CatalogEvent> styleModifiedEvent = getMessagesForHandler(messages, CATALOG_MODIFY_EVENT_HANDLER_KEY, addEventHandler); assertThat(styleModifiedEvent.size(), is(1)); assertThat(styleModifiedEvent.get(0).getSource(), instanceOf(StyleInfo.class)); StyleInfo modifiedStyle = (StyleInfo) styleModifiedEvent.get(0).getSource(); assertThat(modifiedStyle.getName(), is(TEST_STYLE_NAME)); // check that the catalog modify event contains the correct workspace WorkspaceInfo workspace = searchPropertyNewValue((CatalogModifyEvent) styleModifiedEvent.get(0), "workspace", WorkspaceInfo.class); assertThat(workspace, is(testWorkspace)); // check that the correct file style was published assertThat(styleModifiedEvent.get(0), instanceOf(StyleModifyEvent.class)); byte[] fileContent = ((StyleModifyEvent) styleModifiedEvent.get(0)).getFile(); assertThat(fileContent, notNullValue()); assertThat(fileContent.length, not(0)); // parse the published style file and check the opacity value Style style = parseStyleFile(styleInfo, new ByteArrayInputStream(fileContent)); RasterSymbolizer symbolizer = (RasterSymbolizer) style.featureTypeStyles() .get(0).rules().get(0).symbolizers().get(0); assertThat(symbolizer.getOpacity().evaluate(null), is("0.5")); } @Test public void testRemoveStyle() throws Exception { // add the test to the style catalog addTestStyle(); // remove style from catalog StyleInfo style = getCatalog().getStyleByName(TEST_STYLE_NAME); assertThat(style, notNullValue()); getCatalog().remove(style); // waiting for a catalog remove event List<Message> messages = JmsEventsListener.getMessagesByHandlerKey(5000, (selected) -> selected.size() >= 1, CATALOG_REMOVE_EVENT_HANDLER_KEY); assertThat(messages.size(), is(1)); // checking that the correct style was published List<CatalogEvent> styleRemoveEvent = getMessagesForHandler(messages, CATALOG_REMOVE_EVENT_HANDLER_KEY, addEventHandler); assertThat(styleRemoveEvent.size(), is(1)); assertThat(styleRemoveEvent.get(0).getSource(), instanceOf(StyleInfo.class)); StyleInfo removedStyle = (StyleInfo) styleRemoveEvent.get(0).getSource(); assertThat(removedStyle.getName(), is(TEST_STYLE_NAME)); } /** * Helper method that adds the test style to the catalog * and consume the produced events. */ private void addTestStyle() throws Exception { // add the test to the style catalog getTestData().addStyle(TEST_STYLE_NAME, TEST_STYLE_FILE, this.getClass(), getCatalog()); // waiting for a catalog add event and a style file event // waiting for a catalog add event and a style file event List<Message> messages = JmsEventsListener.getMessagesByHandlerKey(5000, (selected) -> selected.size() >= 2, CATALOG_ADD_EVENT_HANDLER_KEY, CATALOG_STYLES_FILE_EVENT_HANDLER_KEY); assertThat(messages.size(), is(2)); } /** * Helper method that parses the file associated with a style. */ private Style parseStyleFile(StyleInfo styleInfo, InputStream input) throws Exception { StyleHandler styleHandler = Styles.handler(styleInfo.getFormat()); StyledLayerDescriptor styleDescriptor = styleHandler.parse(input, styleInfo.getFormatVersion(), null, null); return Styles.style(styleDescriptor); } /** * Helper method that searches a modified property in a catalog modify event. */ private <T> T searchPropertyNewValue(CatalogModifyEvent event, String propertyName, Class<WorkspaceInfo> propertyType) { // sanity check of the modify event properties and values assertThat(event.getPropertyNames(), notNullValue()); assertThat(event.getPropertyNames().isEmpty(), not(true)); assertThat(event.getNewValues(), notNullValue()); assertThat(event.getNewValues().isEmpty(), not(true)); assertThat(event.getPropertyNames().size(), is(event.getNewValues().size())); // find the property we want Object propertyValue = null; for(int i = 0; i < event.getPropertyNames().size(); i++) { String candidatePropertyName = event.getPropertyNames().get(i); if (candidatePropertyName != null && candidatePropertyName.equalsIgnoreCase(propertyName)) { propertyValue = event.getNewValues().get(i); } } // return the found value assertThat(propertyValue, notNullValue()); assertThat(propertyValue, instanceOf(propertyType)); return (T) propertyValue; } }