package org.jboss.windup.rules.apps.java.scan.operation.packagemapping;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.commons.lang3.StringUtils;
import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.config.GraphRule;
import org.jboss.windup.config.PreRulesetEvaluation;
import org.jboss.windup.rules.apps.java.model.WindupJavaConfigurationModel;
import org.jboss.windup.rules.apps.java.service.WindupJavaConfigurationService;
import org.jboss.windup.util.PathUtil;
import org.ocpsoft.rewrite.config.Rule;
import org.ocpsoft.rewrite.context.EvaluationContext;
/**
* Maps from a package to a organization name.
*/
public class PackageNameMapping extends GraphRule implements PackageNameMappingWithPackagePattern, PackageNameMappingWithOrganization, PreRulesetEvaluation
{
private static final Logger LOG = Logger.getLogger(PackageNameMapping.class.getSimpleName());
private String id = this.getClass().getName() + "_" + UUID.randomUUID().toString();
private String organization;
private String packagePattern;
/**
* Gets the organization for the given package (or Maven group id).
*/
public static String getOrganizationForPackage(GraphRewrite event, String pkg)
{
final String pkgComparison = pkg + ".";
String organization = null;
for (Map.Entry<String, String> entry : getMappings(event).entrySet())
{
final String pkgPattern = entry.getKey() + ".";
if (StringUtils.startsWith(pkgComparison, pkgPattern))
{
organization = entry.getValue();
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(" -- Found organization: " + organization);
}
break;
}
}
return organization;
}
/**
* Sets the package pattern to match against.
*/
public static PackageNameMappingWithPackagePattern fromPackage(String packagePattern)
{
PackageNameMapping packageNameMapping = new PackageNameMapping();
packageNameMapping.setPackagePattern(packagePattern);
return packageNameMapping;
}
@Override
public Rule withId(String id)
{
this.id = id;
return this;
}
/**
* Sets the organization to map the package to.
*/
@Override
public PackageNameMappingWithOrganization toOrganization(String organization)
{
setOrganization(organization);
return this;
}
/**
* Indicates that all of the packages within an archive are "known" by the package mapper. Generally
* this indicates that the archive does not contain customer code.
*/
public static boolean isExclusivelyKnownArchive(GraphRewrite event, String filePath)
{
String extension = StringUtils.substringAfterLast(filePath, ".");
if (!StringUtils.equalsIgnoreCase(extension, "jar"))
return false;
ZipFile archive;
try
{
archive = new ZipFile(filePath);
} catch (IOException e)
{
return false;
}
WindupJavaConfigurationService javaConfigurationService = new WindupJavaConfigurationService(event.getGraphContext());
WindupJavaConfigurationModel javaConfigurationModel = WindupJavaConfigurationService.getJavaConfigurationModel(event.getGraphContext());
// indicates that the user did specify some packages to scan explicitly (as opposed to scanning everything)
boolean customerPackagesSpecified = javaConfigurationModel.getScanJavaPackages().iterator().hasNext();
// this should only be true if:
// 1) the package does not contain *any* customer packages.
// 2) the package contains "known" vendor packages.
boolean exclusivelyKnown = false;
String organization = null;
Enumeration<?> e = archive.entries();
// go through all entries...
ZipEntry entry;
while (e.hasMoreElements())
{
entry = (ZipEntry) e.nextElement();
String entryName = entry.getName();
if (entry.isDirectory() || !StringUtils.endsWith(entryName, ".class"))
continue;
String classname = PathUtil.classFilePathToClassname(entryName);
// if the package isn't current "known", try to match against known packages for this entry.
if (!exclusivelyKnown)
{
organization = getOrganizationForPackage(event, classname);
if (organization != null)
{
exclusivelyKnown = true;
} else
{
// we couldn't find a package definitively, so ignore the archive
exclusivelyKnown = false;
break;
}
}
// If the user specified package names and this is in those package names, then scan it anyway
if (customerPackagesSpecified && javaConfigurationService.shouldScanPackage(classname))
{
return false;
}
}
if (exclusivelyKnown)
LOG.info("Known Package: " + archive.getName() + "; Organization: " + organization);
// Return the evaluated exclusively known value.
return exclusivelyKnown;
}
@Override
public void preRulesetEvaluation(GraphRewrite event)
{
PackageNameMapping.getMappings(event).put(packagePattern, organization);
}
private static Map<String, String> getMappings(GraphRewrite event)
{
@SuppressWarnings("unchecked")
Map<String, String> map = (Map<String, String>) event.getRewriteContext().get(PackageNameMapping.class);
if (map == null)
{
map = new HashMap<>();
event.getRewriteContext().put(PackageNameMapping.class, map);
}
return map;
}
@Override
public boolean evaluate(GraphRewrite event, EvaluationContext context)
{
/*
* This is empty because we only care about PreRulesetEvaluation
*/
return true;
}
@Override
public void perform(GraphRewrite event, EvaluationContext context)
{
/*
* This is empty because we only care about PreRulesetEvaluation
*/
}
/**
* Contains the organization name.
*/
public String getOrganization()
{
return organization;
}
/**
* Contains the organization name.
*/
public void setOrganization(String organization)
{
this.organization = organization;
}
/**
* Contains the package pattern.
*/
public String getPackagePattern()
{
return packagePattern;
}
/**
* Contains the package pattern.
*/
public void setPackagePattern(String packagePattern)
{
this.packagePattern = packagePattern;
}
@Override
public String getId()
{
return id;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append(getClass().getName());
builder.append(".fromPackage(" + packagePattern + ")");
builder.append(".toOrganization(" + organization + ")");
return builder.toString();
}
}