/* * Copyright 2016 the original author or authors. * * 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 org.hotswap.agent.versions.matcher; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.jar.Attributes.Name; import org.hotswap.agent.annotation.Manifest; import org.hotswap.agent.logging.AgentLogger; import org.hotswap.agent.util.spring.util.PatternMatchUtils; import org.hotswap.agent.util.spring.util.StringUtils; import org.hotswap.agent.versions.DeploymentInfo; import org.hotswap.agent.versions.ArtifactVersion; import org.hotswap.agent.versions.InvalidVersionSpecificationException; import org.hotswap.agent.versions.ManifestInfo; import org.hotswap.agent.versions.VersionMatchResult; import org.hotswap.agent.versions.VersionMatcher; import org.hotswap.agent.versions.VersionRange; /** * The ManifestMatcher will parse and match a single @Manifest definition * * @author alpapad@gmail.com */ public class ManifestMatcher implements VersionMatcher { private static AgentLogger LOGGER = AgentLogger.getLogger(ManifestMatcher.class); /** The included versions range */ private final VersionRange includes; /** The excluded versions range */ private final VersionRange excludes; /** The properties. */ private final Map<Name, String> properties; /** The includes string. */ private final String includesString; /** The excludes string. */ private final String excludesString; /** The version. */ private final Name[] version; /** * Instantiates a new manifest matcher. * * @param cfg the cfg * @throws InvalidVersionSpecificationException the invalid version specification exception */ public ManifestMatcher(Manifest cfg) throws InvalidVersionSpecificationException { if (StringUtils.hasText(cfg.value())) { this.includesString = cfg.value().trim(); this.includes = VersionRange.createFromVersionSpec(includesString); } else { this.includes = null; this.includesString = null; } if (StringUtils.hasText(cfg.excludeVersion())) { this.excludesString = cfg.excludeVersion().trim(); this.excludes = VersionRange.createFromVersionSpec(excludesString); } else { this.excludes = null; this.excludesString = null; } if(cfg.versionName() == null || cfg.versionName().length == 0) { version = null; } else { List<Name >versions = new ArrayList<>(); for(String versionName: cfg.versionName()) { if (StringUtils.hasText(versionName)) { versions.add(new Name(versionName)); } } version = versions.toArray(new Name[versions.size()]); } if (cfg.names() != null && cfg.names().length > 0) { this.properties = new HashMap<>(); for (org.hotswap.agent.annotation.Name name : cfg.names()) { if(StringUtils.hasText(name.key()) && StringUtils.hasText(name.value())) { this.properties.put(new Name(name.key()), name.value()); } } } else { this.properties = Collections.emptyMap(); } } /** * Gets the included versions range * * @return the included versions range */ public VersionRange getIncludes() { return includes; } /** * Gets the excluded versions range * * @return the excluded versions range */ public VersionRange getExcludes() { return excludes; } /** * Gets the properties. * * @return the properties */ public Map<Name, String> getProperties() { return properties; } /* (non-Javadoc) * @see org.hotswap.agent.config.ArtifactMatcher#matches(org.hotswap.agent.versions.DeploymentInfo) */ public VersionMatchResult matches(DeploymentInfo info) { // Skip if no manifest configuration if(info.getManifest() == null || info.getManifest().size() == 0) { return VersionMatchResult.SKIPPED; } for (ManifestInfo manifest: info.getManifest()) { VersionMatchResult result = match(manifest); if(VersionMatchResult.MATCHED.equals(result)){ LOGGER.debug("Matched {} with {}", this, manifest); return VersionMatchResult.MATCHED; } if(VersionMatchResult.REJECTED.equals(result)){ LOGGER.debug("Rejected {} with {}", this, manifest); return VersionMatchResult.REJECTED; } } // There were no matches (maybe another matcher will pass) return VersionMatchResult.SKIPPED; } /** * Match. * * @param manifest the manifest * @return the version match result */ private VersionMatchResult match(ManifestInfo manifest) { if(manifest == null) { return VersionMatchResult.SKIPPED; } // We need a version... String artifactVersion = manifest.getValue(this.version); if(StringUtils.isEmpty(artifactVersion)){ return VersionMatchResult.SKIPPED; } // if no properties, then skip if(properties.size() == 0) { return VersionMatchResult.SKIPPED; } else { for(Map.Entry<Name,String> e: properties.entrySet()) { String v = manifest.getValue(e.getKey()); // ALL patterns MUST match, else skip if(!StringUtils.hasText(v) || !PatternMatchUtils.regexMatch(e.getValue(), v)) { return VersionMatchResult.SKIPPED; } } } ArtifactVersion version = new ArtifactVersion(artifactVersion); if(excludes != null && excludes.containsVersion(version)) { return VersionMatchResult.REJECTED; } if(includes != null && !includes.containsVersion(version)) { return VersionMatchResult.REJECTED; } else { return VersionMatchResult.MATCHED; } } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "ManifestMatcher [properties=" + properties + ", includes=" + includes + ", excludes=" + excludes + "]"; } /* (non-Javadoc) * @see org.hotswap.agent.config.ArtifactMatcher#isApply() */ @Override public boolean isApply() { return (StringUtils.hasText(includesString) || StringUtils.hasText(excludesString)) && (version != null); } }