/*
* JBoss, Home of Professional Open Source
* Copyright 2010-2016, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt 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.richfaces.tests.metamer.ftest;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.text.MessageFormat;
import java.util.List;
import java.util.Locale;
import org.eu.ingwar.tools.arquillian.extension.suite.annotations.ArquillianSuiteDeployment;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.OverProtocol;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.arquillian.testng.Arquillian;
import org.jboss.as.cli.CommandContext;
import org.jboss.as.cli.CommandContextFactory;
import org.jboss.as.cli.CommandLineException;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.importer.ArchiveImportException;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.shrinkwrap.descriptor.api.Descriptors;
import org.jboss.shrinkwrap.descriptor.api.javaee6.ParamValueType;
import org.jboss.shrinkwrap.descriptor.api.webapp30.WebAppDescriptor;
import org.richfaces.tests.metamer.TemplatesList;
import org.richfaces.tests.metamer.ftest.extension.configurator.templates.annotation.Templates;
import org.richfaces.tests.metamer.ftest.utils.files.LineIdentifiers;
import org.richfaces.tests.metamer.ftest.utils.files.SimpleFileManipulator;
import com.google.common.io.Files;
/**
* Abstract test case used as a basis for majority of test cases.
*
* @author <a href="mailto:ppitonak@redhat.com">Pavol Pitonak</a>
* @version $Revision: 22749 $
*/
@RunAsClient
@ArquillianSuiteDeployment
public abstract class AbstractMetamerTest extends Arquillian {
private static final String EAP_63_AND_UP_REGEX = ".*jbosseap-(managed|remote)-(6-[3-9]).*";
private static final String EAP_70_AND_UP_REGEX = ".*jbosseap-(managed|remote)-(7-[0-9]).*";
// Key to enable WebSockets on EAP
public static final String EAP_WS_ENABLED = "eap.ws.enabled";
/**
* Keys to manage resources optimization (in previous releases named mapping), compression and packaging, used in web.xml
*/
public static final String RESOURCE_OPTIMIZATION_COMPRESSION_STAGES = "org.richfaces.resourceOptimization.compressionStages";
public static final String RESOURCE_OPTIMIZATION_ENABLED = "org.richfaces.resourceOptimization.enabled";
public static final String RESOURCE_OPTIMIZATION_PACKAGING_STAGES = "org.richfaces.resourceOptimization.packagingStages";
public static final String RESOURCE_OPTIMIZATION_PARAM_ALL = "All";
public static final String RESOURCE_OPTIMIZATION_PARAM_NONE = "None";
private static final String activatedMavenProfiles = System.getProperty("activated.maven.profiles", "");
private static WebArchive deployedWar;
protected static final Boolean runInPortalEnv = Boolean.getBoolean("runInPortalEnv");
@ArquillianResource
protected URL contextPath;
@Templates(value = { "plain", "richAccordion", "richCollapsibleSubTable", "richExtendedDataTable", "richDataGrid",
"richCollapsiblePanel", "richTabPanel", "richPopupPanel", "a4jRegion", "a4jRepeat", "uiRepeat" })
protected TemplatesList template;
public AbstractMetamerTest() {
Locale.setDefault(Locale.US);
}
private static void checkValueIsValidForResourceOptimizationParam(String value) {
assertNotNull(value, "The parameter for resource optimization can only be <None> or <All>, not null!");
assertTrue(value.equals(RESOURCE_OPTIMIZATION_PARAM_NONE) || value.equals(RESOURCE_OPTIMIZATION_PARAM_ALL),
MessageFormat.format("The parameter for resource optimization can only be <{0}> or <{1}>. Now it is set to <{2}>.",
RESOURCE_OPTIMIZATION_PARAM_ALL, RESOURCE_OPTIMIZATION_PARAM_NONE, value));
}
private static File copyJBossWebXMLToTarget(WebArchive war) throws IOException {
File file = new File("target/jboss-web.xml");
FileChannel outChannel = new FileOutputStream(file).getChannel();
ReadableByteChannel fromChannel = Channels.newChannel(war.get(ArchivePaths.create("WEB-INF/jboss-web.xml")).getAsset().openStream());
outChannel.transferFrom(fromChannel, 0, Integer.MAX_VALUE);
return file;
}
@Deployment(testable = false, name = "updated")
@OverProtocol("Servlet 3.0")
public static WebArchive createTestArchive() throws IOException, URISyntaxException {
WebArchive war = createWarFromZipFile();
/*
* If value on system property "org.richfaces.resourceOptimization.enabled" is set to true, modify context-params in
* web.xml. For more info see https://issues.jboss.org/browse/RFPL-1682
*/
if (Boolean.getBoolean(RESOURCE_OPTIMIZATION_ENABLED)) {
enableResourceOptimization(war);
}
// advanced features, tested only with browser profile
if (isUsingBrowserProfile()) {
File temporaryJBossWebXML = copyJBossWebXMLToTarget(war);
if (isUsingEAP()) {
// workaround xml parse error of 'default encoding' in jboss-web.xml in EAP version lesser than 7
if (!isUsingEAP70AndUp()) {
removeDefaultEncodingFromJbossWebXML(war, temporaryJBossWebXML);
}
}
// workaround to enable running commands through JBoss CLI in EAP 6.3 or 6.4
workaroundCLIVersionInEAP63_or_64();
// undeploy all metamer WARs if using a JBoss container
if (isUsingJBossContainer()) {
try {
undeployMetamerWars();
} catch (CommandLineException ignored) {// no metamer war was deployed
}
}
if (isUsingEAP63_or_64()) {
// enable WebSockets in EAP 6.3 or 6.4
if (Boolean.getBoolean(EAP_WS_ENABLED)) {
try {
System.out.println("### Enabling WebSockets in EAP ###");
enableWebSocketsInEAP63_or_64(war, temporaryJBossWebXML);
System.out.println("### Enabling of WebSockets in EAP was successful ###");
} catch (CommandLineException t) {
t.printStackTrace(System.err);
System.out.println("### Enabling of WebSockets in EAP was NOT successful ###");
}
}
}
temporaryJBossWebXML.deleteOnExit();
}
// save actual war to target/metamer-UPDATED.war
File updatedWar = new File("target/metamer-UPDATED.war");
war.as(ZipExporter.class).exportTo(updatedWar);
deployedWar = war;
return war;
}
private static WebArchive createWarFromZipFile() throws IOException, IllegalArgumentException, ArchiveImportException {
return ShrinkWrap.createFromZipFile(WebArchive.class, runInPortalEnv ? new File("target/metamer-portlet.war")
: new File("target/metamer.war"));
}
/**
* Deploys target/metamer-UPDATED.war file on current JBoss container.
*/
public static void deployMetamerWar() throws CommandLineException {
if (isUsingJBossContainer()) {
runCLICommandWithWait("deploy target/metamer-UPDATED.war");
} else {
System.err.println("Not using any JBoss container. War cannot be deployed.");
}
}
/**
* Sets javax.faces.PARTIAL_STATE_SAVING to false in web.xml and redeploys the current war. Can only be used with JBoss
* containers.
*/
public static void disablePartialStateSavingAndRedeploy() {
modifyAndDeployWar(new WARModifyAction() {
@Override
public WebArchive modify(WebArchive war) {
WebArchive newWar = (WebArchive) war.shallowCopy();
// 1. load existing web.xml from WebArchive
WebAppDescriptor webXML = Descriptors.importAs(WebAppDescriptor.class).fromStream(
newWar.get("WEB-INF/web.xml").getAsset().openStream());
webXML.getOrCreateContextParam().paramName("javax.faces.PARTIAL_STATE_SAVING").paramValue("false");
// 3. save the params to web.xml
newWar.setWebXML(new StringAsset(webXML.exportAsString()));
return newWar;
}
});
}
/*
* This will update web.xml with resource optimization values.
*/
private static void enableResourceOptimization(WebArchive war) {
System.out.println("### Enabling resource optimization ###");
String compressedParam = System.getProperty(RESOURCE_OPTIMIZATION_COMPRESSION_STAGES, RESOURCE_OPTIMIZATION_PARAM_NONE);
checkValueIsValidForResourceOptimizationParam(compressedParam);
String packedParam = System.getProperty(RESOURCE_OPTIMIZATION_PACKAGING_STAGES, RESOURCE_OPTIMIZATION_PARAM_NONE);
checkValueIsValidForResourceOptimizationParam(packedParam);
System.out.println(MessageFormat.format(" {0} = {1}", RESOURCE_OPTIMIZATION_COMPRESSION_STAGES, compressedParam));
System.out.println(MessageFormat.format(" {0} = {1}", RESOURCE_OPTIMIZATION_PACKAGING_STAGES, packedParam));
// 1. load existing web.xml from metamer.war
WebAppDescriptor webXmlDefault = Descriptors.importAs(WebAppDescriptor.class).fromStream(
war.get("WEB-INF/web.xml").getAsset().openStream());
List<ParamValueType<WebAppDescriptor>> allContextParams = webXmlDefault.getAllContextParam();
// 2. Iterate over all context params and alter the particular ones
for (ParamValueType<WebAppDescriptor> param : allContextParams) {
String paramName = param.getParamName();
if (paramName.equals(RESOURCE_OPTIMIZATION_ENABLED)) {
param.paramValue("true");
} else if (paramName.equals(RESOURCE_OPTIMIZATION_COMPRESSION_STAGES)) {
param.paramValue(compressedParam);
} else if (paramName.equals(RESOURCE_OPTIMIZATION_PACKAGING_STAGES)) {
param.paramValue(packedParam);
}
}
// 3. save the params to web.xml
war.setWebXML(new StringAsset(webXmlDefault.exportAsString()));
}
/**
* Runs a command through JBoss CLI for enabling WebSockets and restarting the server.
*/
private static void enableWSInJBossCLI() throws CommandLineException {
runCLICommandWithWait(
"/subsystem=web/connector=http/:write-attribute(name=protocol,value=org.apache.coyote.http11.Http11NioProtocol)",
":reload");
}
private static File enableWSInJbossWebXML(File jbossWebXMLFile) {
SimpleFileManipulator.inFile(jbossWebXMLFile)
.appendLine("<enable-websockets>true</enable-websockets>")
.beforeLine(LineIdentifiers.lineContains("</jboss-web>"))
.perform();
return jbossWebXMLFile;
}
private static void enableWebSocketsInEAP63_or_64(WebArchive war, File jBossWebXML) throws CommandLineException {
System.out.println(" * adding <enable-websockets>true</enable-websockets> to jboss-web.xml");
replaceJBossWebXMLInWar(war, enableWSInJbossWebXML(jBossWebXML));
enableWSInJBossCLI();
}
private static boolean isUsingBrowserProfile() {
return activatedMavenProfiles.contains("browser");
}
private static boolean isUsingEAP() {
return activatedMavenProfiles.contains("jbosseap");
}
private static boolean isUsingEAP63_or_64() {
return activatedMavenProfiles.matches(EAP_63_AND_UP_REGEX);
}
private static boolean isUsingEAP70AndUp() {
return activatedMavenProfiles.matches(EAP_70_AND_UP_REGEX);
}
private static boolean isUsingJBossContainer() {
return isUsingEAP() || isUsingWildFly();
}
private static boolean isUsingWildFly() {
return activatedMavenProfiles.contains("wildfly");
}
/**
* Creates a copy of target/metamer-UPDATED.war, updates it according to WARModifyAction and saves it to
* target/metamerModfied.war. Undeploys all *metamer* wars, deploys the modified one and deletes it in the target directory.
* Can be used only with JBoss containers,
*/
public static void modifyAndDeployWar(WARModifyAction a) {
if (isUsingJBossContainer()) {
try {
undeployMetamerWars();
} catch (CommandLineException t) {
System.err.println(t);
System.err.println("Undeployment of war was not successful. Exiting.");
System.exit(1);
}
WebArchive modifiedWar = a.modify(deployedWar);
File modifiedWarFile = new File("target/metamerModified.war");
modifiedWarFile.delete();
modifiedWar.as(ZipExporter.class).exportTo(modifiedWarFile);
try {
runCLICommandWithWait("deploy target/metamerModified.war");
} catch (CommandLineException t) {
System.err.println(t);
System.err.println("Modification of war and redeployment was not successful. Exiting.");
System.exit(1);
}
modifiedWarFile.delete();
} else {
System.err.println("Not using any JBoss container. War will not be modified.");
}
}
/**
* If not removed and using EAP, you get: <code>
* Caused by: java.lang.Exception: {"JBAS014671: Failed services" => {"jboss.deployment.unit.\"metamer.war\".PARSE" => "org.jboss.msc.service.StartException in service jboss.deployment.unit.\"metamer.war\".PARSE: JBAS018733: Failed to process phase PARSE of deployment \"metamer.war\"
* Caused by: org.jboss.as.server.deployment.DeploymentUnitProcessingException: JBAS018014: Failed to parse XML descriptor \"/content/metamer.war/WEB-INF/jboss-web.xml\" at [4,3]
* Caused by: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[4,3]
* Message: Unexpected element 'default-encoding' encountered"}}</code>
*/
private static void removeDefaultEncodingFromJbossWebXML(WebArchive war, File jBossWebXML) {
System.out.println(" * removing line containing <default-encoding> from jboss-web.xml");
replaceJBossWebXMLInWar(war, removeDefaultEncodingInJbossWebXML(jBossWebXML));
}
private static File removeDefaultEncodingInJbossWebXML(File jbossWebXMLFile) {
SimpleFileManipulator.inFile(jbossWebXMLFile)
.deleteLine(LineIdentifiers.lineContains("<default-encoding>"))
.perform();
return jbossWebXMLFile;
}
/**
* Replaces jboss-web.xml in the final WAR with a version with enabled WebSockets.
*/
private static WebArchive replaceJBossWebXMLInWar(WebArchive war, File jbossWebXML) throws IllegalArgumentException {
war.delete(ArchivePaths.create("WEB-INF/jboss-web.xml"));
war.addAsWebInfResource(jbossWebXML);
return war;
}
/**
* Runs a command through JBoss CLI.
*/
private static void runCLICommand(String... commands) throws CommandLineException {
if (commands == null || commands.length == 0) {
return;
}
final CommandContext ctx;
ctx = CommandContextFactory.getInstance().newCommandContext();
int MAX_RETRY_COUNT = 20;
try {
String cmd;
int i = 0;
int retryCount = 0;
while (i < commands.length) {
cmd = commands[i];
try {
// connect to the server controller
ctx.connectController();
// execute the command
ctx.handle(cmd);
retryCount = 0;
i++;
} catch (CommandLineException e) {
if (e.getMessage().contains("The controller is not available")) {
retryCount++;
if (retryCount >= MAX_RETRY_COUNT) {
throw e;
}
// wait and repeat
waiting(1000);
} else {
throw e;
}
}
}
} finally {
// terminate the session and
// close the connection to the controller
ctx.terminateSession();
}
}
/**
* Runs all CLI commands and then runs an empty command to wait until the container is ready again
*/
protected static void runCLICommandWithWait(String... commands) throws CommandLineException {
runCLICommand(commands);
runCLICommand("");// wait for EAP to be ready, e.g. after restart
}
/**
* Undeploys all metamer wars from current JBoss container.
*/
public static void undeployMetamerWars() throws CommandLineException {
if (isUsingJBossContainer()) {
runCLICommandWithWait("undeploy *metamer*");
} else {
System.err.println("Not using any JBoss container. War cannot be undeployed.");
}
}
private static void waiting(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException ex) {
}
}
/**
* Workaround works only for container placed in project's target directory. Workaround the exception during parsing the
* jboss-cli.xml. Change the urn:jboss:cli:1.3 to *1.2
*/
private static void workaroundCLIVersionInEAP63_or_64() throws URISyntaxException, IOException {
if (isUsingEAP63_or_64()) {
String jbossHome = System.getProperty("JBOSS_HOME");
if (jbossHome == null || jbossHome.isEmpty()) {
System.err.println("JBOSS_HOME not detected. Exiting.");
System.exit(1);
}
File jbossCliFile = new File(new File(jbossHome), "bin/jboss-cli.xml");
File workaroundedJBossCliFile = new File(AbstractMetamerTest.class
.getResource("eap/jboss-cli.xml").toURI());
jbossCliFile.delete();
Files.copy(workaroundedJBossCliFile, jbossCliFile);
}
}
/**
* Returns the url to test page to be opened by Selenium
*
* @return absolute url to the test page to be opened by Selenium
*/
public abstract URL
getTestUrl();
public interface WARModifyAction {
WebArchive modify(WebArchive war);
}
}