/* * JBoss, Home of Professional Open Source. * Copyright 2015, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.test.integration.logging.operations; import java.io.BufferedReader; import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.http.HttpStatus; import org.jboss.as.controller.client.helpers.Operations; import org.jboss.as.controller.client.helpers.Operations.CompositeOperationBuilder; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.test.integration.logging.LoggingServiceActivator; import org.jboss.as.test.integration.management.util.ServerReload; import org.jboss.as.test.shared.TestSuiteEnvironment; import org.jboss.dmr.ModelNode; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.wildfly.core.testrunner.ServerSetup; import org.wildfly.core.testrunner.WildflyTestRunner; /** * @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a> */ @ServerSetup(ServerReload.SetupTask.class) @RunWith(WildflyTestRunner.class) public class CustomFormattersTestCase extends AbstractLoggingOperationsTestCase { private static final String CUSTOM_FORMATTER_NAME = "customFormatter"; private static final String FILE_NAME = "cf-log.xml"; private static final String HANDLER_NAME = "xmlFile"; private static final ModelNode CUSTOM_FORMATTER_ADDRESS = createAddress("custom-formatter", CUSTOM_FORMATTER_NAME); private static final ModelNode HANDLER_ADDRESS = createAddress("file-handler", HANDLER_NAME); private static final ModelNode ROOT_LOGGER_ADDRESS = createAddress("root-logger", "ROOT"); @Test public void testOperations() throws Exception { // Create the custom formatter ModelNode op = Operations.createAddOperation(CUSTOM_FORMATTER_ADDRESS); op.get("class").set("org.jboss.logmanager.formatters.PatternFormatter"); op.get("module").set("org.jboss.logmanager"); executeOperation(op); // Write some properties final ModelNode properties = new ModelNode().setEmptyObject(); properties.get("pattern").set("%s%E%n"); testWrite(CUSTOM_FORMATTER_ADDRESS, "properties", properties); // Undefine the properties testUndefine(CUSTOM_FORMATTER_ADDRESS, "properties"); // Write a new class attribute, should leave in restart state ModelNode result = testWrite(CUSTOM_FORMATTER_ADDRESS, "class", "java.util.logging.XMLFormatter"); // Check the state ModelNode step1 = Operations.readResult(result).get("step-1"); Assert.assertTrue("Should be in reload-required state: " + result, step1.get("response-headers").get("operation-requires-reload").asBoolean()); // Undefining the class should fail testUndefine(CUSTOM_FORMATTER_ADDRESS, "class", true); // Change the module which should require a restart result = testWrite(CUSTOM_FORMATTER_ADDRESS, "module", "sun.jdk"); // Check the state step1 = Operations.readResult(result).get("step-1"); Assert.assertTrue("Should be in reload-required state: " + result, step1.get("response-headers").get("operation-requires-reload").asBoolean()); // Undefining the module should fail testUndefine(CUSTOM_FORMATTER_ADDRESS, "module", true); // Remove the custom formatter op = Operations.createRemoveOperation(CUSTOM_FORMATTER_ADDRESS); executeOperation(op); // Verify it's been removed verifyRemoved(CUSTOM_FORMATTER_ADDRESS); } @Test public void testUsage() throws Exception { // Create the custom formatter CompositeOperationBuilder builder = CompositeOperationBuilder.create(); ModelNode op = Operations.createAddOperation(CUSTOM_FORMATTER_ADDRESS); op.get("class").set("java.util.logging.XMLFormatter"); // the module doesn't really matter since it's a JDK, so we'll just use the jboss-logmanager. op.get("module").set("org.jboss.logmanager"); builder.addStep(op); // Create the handler op = Operations.createAddOperation(HANDLER_ADDRESS); final ModelNode file = op.get("file"); file.get("relative-to").set("jboss.server.log.dir"); file.get("path").set(FILE_NAME); op.get("append").set(false); op.get("autoflush").set(true); op.get("named-formatter").set(CUSTOM_FORMATTER_NAME); builder.addStep(op); // Add the handler to the root logger op = Operations.createOperation("add-handler", ROOT_LOGGER_ADDRESS); op.get(ModelDescriptionConstants.NAME).set(HANDLER_NAME); builder.addStep(op); executeOperation(builder.build()); // Get the log file op = Operations.createOperation("resolve-path", HANDLER_ADDRESS); ModelNode result = executeOperation(op); final Path logFile = Paths.get(Operations.readResult(result).asString()); // The file should exist Assert.assertTrue("The log file was not created.", Files.exists(logFile)); // Log 5 records doLog("Test message: ", 5); // Read the log file try (BufferedReader reader = Files.newBufferedReader(logFile, StandardCharsets.UTF_8)) { final Pattern pattern = Pattern.compile("^(<message>)+(Test message: \\d)+(</message>)$"); final List<String> messages = new ArrayList<>(5); String line; while ((line = reader.readLine()) != null) { final String trimmedLine = line.trim(); final Matcher m = pattern.matcher(trimmedLine); // Very simple xml parsing if (m.matches()) { messages.add(m.group(2)); } } // Should be 5 messages Assert.assertEquals(5, messages.size()); // Check each message int count = 0; for (String msg : messages) { Assert.assertEquals("Test message: " + count++, msg); } } builder = CompositeOperationBuilder.create(); // Remove the handler from the root-logger op = Operations.createOperation("remove-handler", ROOT_LOGGER_ADDRESS); op.get(ModelDescriptionConstants.NAME).set(HANDLER_NAME); builder.addStep(op); // Remove the custom formatter op = Operations.createRemoveOperation(CUSTOM_FORMATTER_ADDRESS); builder.addStep(op); // Remove the handler op = Operations.createRemoveOperation(HANDLER_ADDRESS); builder.addStep(op); executeOperation(builder.build()); // So we don't pollute other, verify the formatter and handler have been removed op = Operations.createReadAttributeOperation(ROOT_LOGGER_ADDRESS, "handlers"); result = executeOperation(op); // Should be a list type final List<ModelNode> handlers = Operations.readResult(result).asList(); for (ModelNode handler : handlers) { Assert.assertNotEquals(CUSTOM_FORMATTER_NAME, handler.asString()); } verifyRemoved(CUSTOM_FORMATTER_ADDRESS); verifyRemoved(HANDLER_ADDRESS); // Delete the log file Files.delete(logFile); // Ensure it's been deleted Assert.assertFalse(Files.exists(logFile)); } private void doLog(final String msg, final int count) throws Exception { final URL url = TestSuiteEnvironment.getHttpUrl(); for (int i = 0; i < count; i++) { final String s = msg + i; final int statusCode = getResponse(s, Collections.singletonMap(LoggingServiceActivator.LOG_INFO_ONLY_KEY, "true")); Assert.assertTrue("Invalid response statusCode: " + statusCode + " URL: " + url, statusCode == HttpStatus.SC_OK); } } }