All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.galasa.cicsts.resource.internal.CicsBundleImpl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright contributors to the Galasa project
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package dev.galasa.cicsts.resource.internal;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.attribute.PosixFilePermissions;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import dev.galasa.artifact.IArtifactManager;
import dev.galasa.artifact.IBundleResources;
import dev.galasa.artifact.ISkeletonProcessor.SkeletonType;
import dev.galasa.artifact.TestBundleResourceException;
import dev.galasa.cicsts.CicstsHashMap;
import dev.galasa.cicsts.CicstsManagerException;
import dev.galasa.cicsts.ICicsRegion;
import dev.galasa.cicsts.ICicsTerminal;
import dev.galasa.cicsts.cicsresource.CicsBundleResourceException;
import dev.galasa.cicsts.cicsresource.CicsJvmserverResourceException;
import dev.galasa.cicsts.cicsresource.CicsResourceManagerException;
import dev.galasa.cicsts.cicsresource.CicsResourceStatus;
import dev.galasa.cicsts.cicsresource.ICicsBundle;
import dev.galasa.cicsts.resource.internal.properties.DefaultResourceTimeout;
import dev.galasa.zos.IZosImage;
import dev.galasa.zosfile.IZosFileHandler;
import dev.galasa.zosfile.IZosUNIXFile;
import dev.galasa.zosfile.IZosUNIXFile.UNIXFileDataType;
import dev.galasa.zosfile.ZosUNIXFileException;

public class CicsBundleImpl implements ICicsBundle {
    
    private static final Log logger = LogFactory.getLog(CicsBundleImpl.class);

	private IBundleResources testBundleResources;
    private CicsResourceManagerImpl cicsResourceManager;
	private IArtifactManager artifactManager;
    private ICicsRegion cicsRegion;
    private ICicsTerminal cicsTerminal;
    private Class testClass;
    private IZosImage cicsZosImage;
    private IZosUNIXFile runTemporaryUNIXPath;
    private IZosFileHandler zosFileHandler;
	private String localBundlePath;
	private Map parameters = new HashMap<>();
	private boolean shouldDeploy;
	private List cicsBundleComponents = new ArrayList<>();
    
    private String resourceDefinitionName;
    private String resourceDefinitionGroup;
    private String resourceDefinitionDescription;
    private CicsResourceStatus resourceDefinitionStatus = CicsResourceStatus.ENABLED;
    private String resourceDefinitionBundledir;

	private int defaultTimeout;

    private static final String SLASH_SYBMOL = "/";

	private static final String RESOURCE_TYPE_BUNDLE = "BUNDLE";

	public CicsBundleImpl(CicsResourceManagerImpl cicsResourceManager, ICicsRegion cicsRegion, ICicsTerminal cicsTerminal, Class testClass, String name, String group, String bundlePath, String bunndledir, Map parameters) throws CicsBundleResourceException {
        this.cicsResourceManager = cicsResourceManager;
        this.cicsResourceManager.registerCicsBundle(this);
        this.artifactManager = this.cicsResourceManager.getArtifactManager();
        try {
            this.zosFileHandler = this.cicsResourceManager.getZosFileHandler();
        } catch (CicsResourceManagerException e) {
            throw new CicsBundleResourceException("Unable to get zOS File Handler", e);
        }
        this.cicsRegion = cicsRegion;
        this.cicsZosImage = cicsRegion.getZosImage();
        this.cicsTerminal = cicsTerminal;
        this.testClass = testClass;
        this.resourceDefinitionName = name;
        this.resourceDefinitionGroup = group;
        try {        	                          
			this.runTemporaryUNIXPath = this.zosFileHandler.newUNIXFile(cicsRegion.getRunTemporaryUNIXDirectory().getUnixPath() + "CICSBundles" + SLASH_SYBMOL + getName() + SLASH_SYBMOL, this.cicsZosImage);
		} catch (CicstsManagerException | ZosUNIXFileException e) {
			throw new CicsBundleResourceException("Unable to get run temporary UNIX path", e);
		}
        // CICS bundle source already stored on file system 
        if (bundlePath == null) {
            this.shouldDeploy = false;
            this.resourceDefinitionBundledir = bunndledir;
        } else {
            this.shouldDeploy = true;
	        if (bundlePath.endsWith(SLASH_SYBMOL)) {
	        	this.localBundlePath = bundlePath;
	        } else {
	        	this.localBundlePath = bundlePath + SLASH_SYBMOL;
	    	}
	    	String root = new File(this.localBundlePath).getName();
	    	this.resourceDefinitionBundledir = this.runTemporaryUNIXPath.getUnixPath() + root + SLASH_SYBMOL;
	        if (parameters != null && !parameters.isEmpty()) {
	        	this.parameters.putAll(parameters);
	        }
    		this.testBundleResources = this.artifactManager.getBundleResources(this.testClass);
        }
	}

	@Override
	public void setDefinitionDescriptionAttribute(String value) {
		this.resourceDefinitionDescription = value;
	}

	@Override
	public void setDefinitionStatusAttribute(CicsResourceStatus value) {
		this.resourceDefinitionStatus = value;
	}

    @Override
    public String getResourceDefinitionNameAttribute() {
        return this.resourceDefinitionName;
    }

    @Override
    public String getResourceDefinitionGroupAttribute() {
        return this.resourceDefinitionGroup;
    }

    @Override
    public String getResourceDefinitionDescriptionAttribute() {
        return this.resourceDefinitionDescription;
    }

    @Override
    public CicsResourceStatus getResourceDefinitionStatusAttribute() {
        return this.resourceDefinitionStatus;
    }

    @Override
    public String getResourceDefinitionBundledirAttribute() {
        return this.resourceDefinitionBundledir;
    }	

	@Override
	public void buildResourceDefinition() throws CicsBundleResourceException {
        try {
            if (resourceDefined()) {
                throw new CicsBundleResourceException(RESOURCE_TYPE_BUNDLE + " " + getName() + " already exists");
            }
            boolean setUcctran = false;
            if (this.cicsTerminal.isUppercaseTranslation() == true) {
            	this.cicsTerminal.setUppercaseTranslation(false);
            	setUcctran = true;
            }
            this.cicsRegion.ceda().createResource(this.cicsTerminal, RESOURCE_TYPE_BUNDLE, getName(), getResourceDefinitionGroupAttribute(), buildResourceParameters());
            if (setUcctran) {
            	this.cicsTerminal.setUppercaseTranslation(true);
            }

            if (!resourceDefined()) {
                throw new CicsBundleResourceException("Failed to define " + RESOURCE_TYPE_BUNDLE + " resource definition");
            } 
        } catch (CicstsManagerException e) {
            throw new CicsBundleResourceException("Unable to build " + RESOURCE_TYPE_BUNDLE + " resource definition", e);
        }
	}
	
	@Override
	public void deploy() throws CicsBundleResourceException {
		if (!this.shouldDeploy) {
			throw new CicsBundleResourceException("The CICS bundle content was not supplied when the ICicsBundle was created");
		}
        try {
        	findComponents();
        	for (CicsBundleComponent bundleComponent : this.cicsBundleComponents) {
    			logger.debug("Copying file " + bundleComponent.localPath + " to " + bundleComponent.targetPath);

                IZosUNIXFile bundleFile = this.zosFileHandler.newUNIXFile(bundleComponent.targetPath, cicsZosImage);
                if (bundleComponent.type.isBinaryBundleResource()) {
                	bundleFile.setDataType(UNIXFileDataType.BINARY);
                } else {
                	bundleFile.setDataType(UNIXFileDataType.TEXT);
                }
                if (!bundleFile.exists()) {
                    bundleFile.create(PosixFilePermissions.fromString("rwxrwxrwx"));
                }
                bundleFile.storeBinary(bundleComponent.content);
    		}
        } catch (ZosUNIXFileException e) {
            throw new CicsBundleResourceException("Problem deploying CICS bundle to zOS UNIX file system", e);
        }
	}

	private void findComponents() throws CicsBundleResourceException {
    	try {
    		String localPath = this.localBundlePath + "META-INF/cics.xml";
    		String targetPath = this.getResourceDefinitionBundledirAttribute() + "META-INF/cics.xml";
    		byte[] content = IOUtils.toByteArray(this.testBundleResources.retrieveSkeletonFile(localPath, this.parameters, SkeletonType.VELOCITY));
    		CicsBundleComponent cicsBundleComponent = new CicsBundleComponent(localPath, targetPath, content, CicsBundleResourceType.CICSXML);
			this.cicsBundleComponents.add(cicsBundleComponent);
    		NodeList nodes = getDocument(cicsBundleComponent).getElementsByTagName("define");
    		for (int i = 0; i < nodes.getLength(); i++) {
    			NamedNodeMap attributes = nodes.item(i).getAttributes();
    			String type = attributes.getNamedItem("type").getNodeValue();
    			String path = attributes.getNamedItem("path").getNodeValue();
    			localPath = this.localBundlePath + path;
    			targetPath = getResourceDefinitionBundledirAttribute() + path;
    			content = IOUtils.toByteArray(this.testBundleResources.retrieveSkeletonFile(localPath, this.parameters, SkeletonType.VELOCITY));
    			CicsBundleResourceType componentType = CicsBundleResourceType.valueOf(new File(type).getName());
    			cicsBundleComponent = new CicsBundleComponent(localPath, targetPath, content, componentType);
    			this.cicsBundleComponents.add(cicsBundleComponent);
    			if (componentType.getSubComponentType() != null) {
    				parseCicsBundleComponent(cicsBundleComponent);
    			}
            }
    	} catch (CicsBundleResourceException | TestBundleResourceException | IOException e) {
    		throw new CicsBundleResourceException("Problem retrieving the CICS bundle files from the test bundle", e);
		}
	}

	private void parseCicsBundleComponent(CicsBundleComponent cicsBundleComponent) throws CicsBundleResourceException {
		try {
			NodeList nodes = getDocument(cicsBundleComponent).getChildNodes();
			for (int i = 0; i < nodes.getLength(); i++) {
	    		String localPath;
	    		String targetPath;
	    		byte[] content;
				
				Element element = (Element) nodes.item(i);
				String nodeName = element.getTagName();
				String symbolicName = element.getAttribute("symbolicname");
				if (nodeName.equals("osgibundle")) {
					String version = element.getAttribute("version");
					String fileName = symbolicName + "_" + version + ".jar";
					localPath = this.localBundlePath + fileName;
					targetPath = getResourceDefinitionBundledirAttribute() + fileName;
					content = IOUtils.toByteArray(this.testBundleResources.retrieveJar(symbolicName, version, this.localBundlePath));
				} else if (nodeName.equals("nodejsapp")) {
					//TODO !!??
					throw new CicsBundleResourceException("nodejsapp not yet implemented");
				} else {
					String fileName = symbolicName + "." + cicsBundleComponent.type.getSubComponentType().toString().toLowerCase();
					localPath = this.localBundlePath + fileName;
					targetPath = getResourceDefinitionBundledirAttribute() + fileName;
					content = IOUtils.toByteArray(this.testBundleResources.retrieveFile(localPath));
				}
				this.cicsBundleComponents.add(new CicsBundleComponent(localPath, targetPath , content, cicsBundleComponent.type.getSubComponentType()));
			}
		} catch (CicsBundleResourceException | TestBundleResourceException | IOException e) {
			throw new CicsBundleResourceException("Problem parsing bundle component", e);
		}
	}

	private Document getDocument(CicsBundleComponent cicsBundleComponent) throws CicsBundleResourceException {
		try {
			logger.trace("Parsing " + cicsBundleComponent.localPath);
			DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
	        Document document = documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(cicsBundleComponent.content));
    		logger.debug("Content:" + "\n" + documentToString(document));
	        return document;
		} catch (SAXException | IOException | ParserConfigurationException e) {
			throw new CicsBundleResourceException("Problem retrieving content of \"" + cicsBundleComponent.localPath + "\" from the test bundle", e);
		}
	}
	
    protected String documentToString(Document document) throws CicsBundleResourceException {
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        try {
            // Remove blank lines
            document.normalize();
            XPath xPath = XPathFactory.newInstance().newXPath();
            NodeList nodeList = (NodeList) xPath.evaluate("//text()[normalize-space()='']", document, XPathConstants.NODESET);
            for (int i = 0; i < nodeList.getLength(); ++i) {
                Node node = nodeList.item(i);
                node.getParentNode().removeChild(node);
            }
            
            Transformer transformer = transformerFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
            Source source = new DOMSource(document);            
            StringWriter stringWriter = new StringWriter();
            StreamResult result = new StreamResult(stringWriter);
            transformer.transform(source, result);
            return stringWriter.toString();
        } catch (XPathExpressionException | TransformerException e) {
            throw new CicsBundleResourceException("Unable to convert server.xml org.w3c.dom.Document to java.lang.String");
        }
    }

	@Override
	public void buildInstallResourceDefinition() throws CicsBundleResourceException {
        buildResourceDefinition();
        if (this.shouldDeploy) {
        	deploy();
        }
        installResourceDefinition();
	}

	@Override
	public void installResourceDefinition() throws CicsBundleResourceException {
        try {
            if (resourceInstalled()) {
                throw new CicsBundleResourceException(RESOURCE_TYPE_BUNDLE + " " + getName() + " already installed");
            }
            this.cicsRegion.ceda().installResource(this.cicsTerminal, RESOURCE_TYPE_BUNDLE, getName(), this.resourceDefinitionGroup);
            //TODO: should return messages????
            if (!resourceInstalled()) {
                throw new CicsBundleResourceException("Failed to install " + RESOURCE_TYPE_BUNDLE + " resource definition");
            }
        } catch (CicstsManagerException e) {
            throw new CicsBundleResourceException("Unable to install " + RESOURCE_TYPE_BUNDLE + " resource definition", e);
        }
	}

	@Override
	public boolean resourceDefined() throws CicsBundleResourceException {
        try {
            return this.cicsRegion.ceda().resourceExists(this.cicsTerminal, RESOURCE_TYPE_BUNDLE, getName(), resourceDefinitionGroup);
        } catch (CicstsManagerException e) {
            throw new CicsBundleResourceException("Unable to display " + RESOURCE_TYPE_BUNDLE + " resource definition", e);
        }
	}

	@Override
	public boolean resourceInstalled() throws CicsBundleResourceException {
        try {
            return this.cicsRegion.cemt().inquireResource(this.cicsTerminal, RESOURCE_TYPE_BUNDLE, getName()) != null;
        } catch (CicstsManagerException e) {
            throw new CicsBundleResourceException("Unable to inquire " + RESOURCE_TYPE_BUNDLE + "", e);
        }
	}

	@Override
	public void enable() throws CicsBundleResourceException {
        try {
            if (!resourceInstalled()) {
                throw new CicsBundleResourceException(RESOURCE_TYPE_BUNDLE + " " + getName() + " does not exist");
            }
            this.cicsRegion.cemt().setResource(this.cicsTerminal, RESOURCE_TYPE_BUNDLE, getName(), "ENABLED");
        } catch (CicstsManagerException e) {
            throw new CicsBundleResourceException("Problem enabling " + RESOURCE_TYPE_BUNDLE + " " + getName(), e);
        }
	}

	@Override
	public boolean waitForEnable() throws CicsBundleResourceException {
        return waitForEnable(getDefaultTimeout());
	}

	@Override
	public boolean waitForEnable(int timeout) throws CicsBundleResourceException {
        logger.trace("Waiting " + timeout + " second(s) for " + RESOURCE_TYPE_BUNDLE + " " +  getName() + " to be enabled");
	    LocalDateTime timeoutTime = LocalDateTime.now().plusSeconds(timeout);
	    while (LocalDateTime.now().isBefore(timeoutTime)) {
            if (isEnabled()) {
                return true;
            }
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new CicsBundleResourceException("Interrupted during wait", e);
            }
        }
        return isEnabled();
	}

	@Override
	public boolean isEnabled() throws CicsBundleResourceException {
        if (!resourceInstalled()) {
            return false;
        }
        boolean enabled = cemtInquire().isParameterEquals("enablestatus", CicsResourceStatus.ENABLED.toString());
        if (enabled) {
            logger.trace(RESOURCE_TYPE_BUNDLE + " " +  getName() + " is enabled");
        } else {
            logger.trace(RESOURCE_TYPE_BUNDLE + " " +  getName() + " is NOT enabled");
        }
        return enabled;
	}

	@Override
	public boolean disable() throws CicsBundleResourceException {
        try {
            if (!resourceInstalled()) {
                throw new CicsJvmserverResourceException(RESOURCE_TYPE_BUNDLE + " " + getName() + " does not exist");
            }
            this.cicsRegion.cemt().setResource(this.cicsTerminal, RESOURCE_TYPE_BUNDLE, getName(), "DISABLED");
        } catch (CicstsManagerException e) {
            throw new CicsBundleResourceException("Problem disabling " + RESOURCE_TYPE_BUNDLE + " " + getName(), e);
        }
        return isEnabled();
	}

	@Override
	public boolean waitForDisable() throws CicsBundleResourceException {
        return waitForDisable(getDefaultTimeout());
	}

	@Override
	public boolean waitForDisable(int timeout) throws CicsBundleResourceException {
        logger.trace("Waiting " + timeout + " second(s) for " + RESOURCE_TYPE_BUNDLE + " " +  getName() + " to be disabled");
	    LocalDateTime timeoutTime = LocalDateTime.now().plusSeconds(timeout);
	    while (LocalDateTime.now().isBefore(timeoutTime)) {
            if (!isEnabled()) {
                return true;
            }
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new CicsBundleResourceException("Interrupted during wait", e);
            }
        }
        if (isEnabled()) {
            throw new CicsBundleResourceException(RESOURCE_TYPE_BUNDLE + " " + getName() + " not disabled in " + timeout + " second(s)");
        }
        return true;
	}

	@Override
	public boolean disableDiscardInstall() throws CicsBundleResourceException {
		return disableDiscardInstall(getDefaultTimeout());
	}

	@Override
	public boolean disableDiscardInstall(int timeout) throws CicsBundleResourceException {
		disable();
		waitForDisable(timeout);
		discard();
		installResourceDefinition();
		return waitForEnable();
	}

	@Override
	public void delete() throws CicsBundleResourceException {
        try {
            if (resourceDefined()) {
                this.cicsRegion.ceda().deleteResource(this.cicsTerminal, RESOURCE_TYPE_BUNDLE, getName(), resourceDefinitionGroup);
            }
        } catch (CicstsManagerException e) {
        	throw new CicsBundleResourceException("Problem deleteing " + RESOURCE_TYPE_BUNDLE + " " + getName(), e);
        }
	}

	@Override
	public void discard() throws CicsBundleResourceException {
        try {
            if (resourceInstalled()) {
                this.cicsRegion.cemt().discardResource(cicsTerminal, RESOURCE_TYPE_BUNDLE, getName());
	            if (resourceInstalled()) {
	            	throw new CicsBundleResourceException(RESOURCE_TYPE_BUNDLE + " was not discarded" + getName());
	            }
            }
        } catch (CicstsManagerException e) {
            throw new CicsBundleResourceException("Problem discarding " + RESOURCE_TYPE_BUNDLE + " " + getName(), e);
        }
	}

	@Override
	public void disableDiscardDelete() throws CicsBundleResourceException {
        disable();
        waitForDisable();
        discard();
        delete();
	}

	@Override
	public void build() throws CicsBundleResourceException {
        try {
            if (this.shouldDeploy) {
            	deploy();
            }
            buildResourceDefinition();
            installResourceDefinition();
        } catch (CicsBundleResourceException e) {
            throw new CicsBundleResourceException("Problem building " + RESOURCE_TYPE_BUNDLE + " " + getName(), e);
        }
	}	

	@Override
	public String getName() {
		return this.resourceDefinitionName;
	}

	@Override
    public String toString() {
        return "[CICS Bundle] " + getName();
    }

    protected int getDefaultTimeout() throws CicsBundleResourceException {
        if (this.defaultTimeout == -1) {
            try {
                this.defaultTimeout = DefaultResourceTimeout.get(this.cicsZosImage);
            } catch (CicsResourceManagerException e) {
                throw new CicsBundleResourceException("Problem creating getting default resource timeout", e);
            }
        }
        return this.defaultTimeout;
    }

    protected String buildResourceParameters() {
        StringBuilder resourceParameters = new StringBuilder();
        appendNotNull(resourceParameters, "DESCRIPTION", getResourceDefinitionDescriptionAttribute());
        appendNotNull(resourceParameters, "STATUS", getResourceDefinitionStatusAttribute().toString());
        appendNotNull(resourceParameters, "BUNDLEDIR", getResourceDefinitionBundledirAttribute());
        return resourceParameters.toString();
    }

    protected StringBuilder appendNotNull(StringBuilder resourceParameters, String attribute, String value) {
        if (value != null && !value.isEmpty()) {
            resourceParameters.append(attribute);
            resourceParameters.append(" (");
            resourceParameters.append(value);
            resourceParameters.append(") ");
        }
        return resourceParameters;
    }

    protected CicstsHashMap cemtInquire() throws CicsBundleResourceException {
        if (!resourceInstalled()) {
            throw new CicsBundleResourceException("JVMSERVER " + getName() + " does not exist");
        }
        CicstsHashMap cemtMap;
        try {
            cemtMap = this.cicsRegion.cemt().inquireResource(this.cicsTerminal, RESOURCE_TYPE_BUNDLE, getName());
        } catch (CicstsManagerException e) {
            throw new CicsBundleResourceException("Problem inquiring JVMSERVER " + getName(), e);
        }
        return cemtMap;
    }
	
	protected void cleanup() {
        try {
            if (!resourceInstalled()) {
                logger.info(RESOURCE_TYPE_BUNDLE + " " + getName() + " has not been installed");
            } else {
                try {
                    disable();
                    waitForDisable();
                } catch (CicsBundleResourceException e) {
                    logger.error("Problem in cleanup phase", e);
                }
                try {
                    discard();
                } catch (CicsBundleResourceException e) {
                    logger.error("Problem in cleanup phase", e);
                }
            }
        } catch (CicstsManagerException e) {
            logger.error("Problem in cleanup phase", e);
        }
        try {
            delete();
        } catch (CicsBundleResourceException e) {
            logger.error("Problem in cleanup phase", e);
        }
    }
	
	private class CicsBundleComponent {

		private String localPath;
		private String targetPath;
		private byte[] content;
		private CicsBundleResourceType type;

		private CicsBundleComponent(String localPath, String targetPath, byte[] content, CicsBundleResourceType type) {
			this.localPath = localPath;
			this.targetPath = targetPath;
			this.content = content;
			this.type = type;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy