/**
* Copyright (C) 2015 Orange
* Licensed 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 com.francetelecom.clara.cloud.deployment.rules;
import static com.francetelecom.clara.cloud.deployment.result.CriticityEnum.WARNING;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.francetelecom.clara.cloud.commons.TechnicalException;
import com.francetelecom.clara.cloud.deployment.result.RuleValidationMessage;
import com.francetelecom.clara.cloud.logicalmodel.LogicalService;
import com.francetelecom.clara.cloud.logicalmodel.LogicalSoapService;
import com.francetelecom.clara.cloud.logicalmodel.LogicalWebGUIService;
import com.francetelecom.clara.cloud.logicalmodel.ProcessingNode;
import com.francetelecom.clara.cloud.services.dto.DeployableMavenArtifactDto;
@Component
public class CheckHttpContextRootRule implements CheckRule {
private static Logger logger = LoggerFactory.getLogger(CheckHttpContextRootRule.class);
private static final String APP_XML = "META-INF/application.xml";
@Override
public Set<RuleValidationMessage> validate(DeployableMavenArtifactDto providedArtifact, ProcessingNode node) throws TechnicalException {
Set<RuleValidationMessage> messages = new HashSet<RuleValidationMessage>();
if ("ear".equals(providedArtifact.getType())) {
Set<String> providedContextRoots;
try {
providedContextRoots = getWebContextRootsFromEAR(new ByteArrayInputStream(providedArtifact.getBinaryContent()));
} catch (Exception e) {
providedContextRoots = new HashSet<String>();
providedContextRoots.add("<no context root>");
}
logger.debug("Context roots from ear : {}", providedContextRoots);
Set<String> modelContextRoots = getWebContextRootsFromLogicalModel(node);
logger.debug("Context roots from logical model : {}", modelContextRoots);
// All defined context root(s) in model should be provided in war(s)
for (String modelContextRoot : modelContextRoots) {
if (!providedContextRoots.contains(modelContextRoot)) {
messages.add(new RuleValidationMessage("Deployment of [" + providedArtifact.getArtifactId() + "] not allowed : model context root [/"
+ modelContextRoot + "] not found in provided war(s)", WARNING));
logger.debug("Deployment of {} not allowed : model context root /{} not found in provided war(s)", providedArtifact.getArtifactId(), modelContextRoot);
}
}
// All provided context roots should exist in logical model
for (String providedContextRoot : providedContextRoots) {
if (!modelContextRoots.contains(providedContextRoot)) {
messages.add(new RuleValidationMessage("Deployment of [" + providedArtifact.getArtifactId() + "] not allowed : provided context root [/"
+ providedContextRoot + "] not found in logical model", WARNING));
logger.debug("Deployment of {} not allowed : provided context root /{} not found in logical model", providedArtifact.getArtifactId(), providedContextRoot);
}
}
}
return messages;
}
private Set<String> getWebContextRootsFromEAR(InputStream in) throws IOException, ParserConfigurationException, SAXException, XPathExpressionException {
Set<String> contextRoots = new HashSet<String>();
JarInputStream ear = new JarInputStream(in);
JarEntry entry;
while ((entry = ear.getNextJarEntry()) != null) {
if (APP_XML.equals(entry.getName())) {
contextRoots = getWebContextRootsFromDeploymentDescriptor(ear);
break;
}
}
return contextRoots;
}
private Set<String> getWebContextRootsFromDeploymentDescriptor(InputStream is) throws ParserConfigurationException, SAXException, IOException,
XPathExpressionException {
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = db.parse(is);
XPath xpath = XPathFactory.newInstance().newXPath();
NodeList nodes = (NodeList) xpath.evaluate("//context-root", doc, XPathConstants.NODESET);
Set<String> contextRoots = new HashSet<String>();
for (int i = 0; i < nodes.getLength(); i++) {
// Strip leading / if any, because "/toto" and "toto" define same context root
contextRoots.add(nodes.item(i).getTextContent().replaceFirst("/", ""));
}
return contextRoots;
}
private Set<String> getWebContextRootsFromLogicalModel(ProcessingNode node) {
Set<String> modelContextRoots = new HashSet<String>();
for (LogicalService service : node.listLogicalServices()) {
// Only WSP and GUI have context root
if (service instanceof LogicalWebGUIService) {
// Strip leading / if any, because "/toto" and "toto" define same context root
modelContextRoots.add(((LogicalWebGUIService) service).getContextRoot().getValue().replaceFirst("/", ""));
} else if (service instanceof LogicalSoapService) {
// Strip leading / if any, because "/toto" and "toto" define same context root
modelContextRoots.add(((LogicalSoapService) service).getContextRoot().getValue().replaceFirst("/", ""));
}
}
return modelContextRoots;
}
}