package org.jboss.windup.rules.apps.javaee.rules; import static org.joox.JOOX.$; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.logging.Logger; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.jboss.windup.config.GraphRewrite; import org.jboss.windup.config.metadata.RuleMetadata; import org.jboss.windup.config.phase.InitialAnalysisPhase; import org.jboss.windup.config.query.Query; import org.jboss.windup.config.ruleprovider.IteratingRuleProvider; import org.jboss.windup.graph.GraphContext; import org.jboss.windup.graph.model.ProjectModel; import org.jboss.windup.graph.service.GraphService; import org.jboss.windup.graph.service.Service; import org.jboss.windup.config.projecttraversal.ProjectTraversalCache; import org.jboss.windup.reporting.model.TechnologyTagLevel; import org.jboss.windup.reporting.model.TechnologyTagModel; import org.jboss.windup.reporting.service.ClassificationService; import org.jboss.windup.reporting.service.TechnologyTagService; import org.jboss.windup.rules.apps.java.decompiler.FernflowerDecompilerOperation; import org.jboss.windup.rules.apps.java.model.AbstractJavaSourceModel; import org.jboss.windup.rules.apps.java.model.AmbiguousJavaClassModel; import org.jboss.windup.rules.apps.java.model.JavaClassFileModel; import org.jboss.windup.rules.apps.java.model.JavaClassModel; import org.jboss.windup.rules.apps.java.model.JavaSourceFileModel; import org.jboss.windup.rules.apps.java.model.PhantomJavaClassModel; import org.jboss.windup.rules.apps.java.service.JavaClassService; import org.jboss.windup.rules.apps.javaee.model.EjbDeploymentDescriptorModel; import org.jboss.windup.rules.apps.javaee.model.EjbEntityBeanModel; import org.jboss.windup.rules.apps.javaee.model.EjbMessageDrivenModel; import org.jboss.windup.rules.apps.javaee.model.EjbSessionBeanModel; import org.jboss.windup.rules.apps.javaee.model.EnvironmentReferenceModel; import org.jboss.windup.rules.apps.javaee.model.EnvironmentReferenceTagType; import org.jboss.windup.rules.apps.javaee.model.JmsDestinationModel; import org.jboss.windup.rules.apps.javaee.service.EnvironmentReferenceService; import org.jboss.windup.rules.apps.javaee.service.JmsDestinationService; import org.jboss.windup.rules.apps.xml.model.DoctypeMetaModel; import org.jboss.windup.rules.apps.xml.model.NamespaceMetaModel; import org.jboss.windup.rules.apps.xml.model.XmlFileModel; import org.jboss.windup.rules.apps.xml.service.XmlFileService; import org.jboss.windup.util.xml.DoctypeUtils; import org.jboss.windup.util.xml.NamespaceUtils; import org.ocpsoft.rewrite.config.ConditionBuilder; import org.ocpsoft.rewrite.context.EvaluationContext; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Discovers ejb-jar.xml files and parses the related metadata * * @author <a href="mailto:bradsdavis@gmail.com">Brad Davis</a> * @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a> */ @RuleMetadata(phase = InitialAnalysisPhase.class, perform = "Discover EJB-JAR XML Files") public class DiscoverEjbConfigurationXmlRuleProvider extends IteratingRuleProvider<XmlFileModel> { private static final Logger LOG = Logger.getLogger(DiscoverEjbConfigurationXmlRuleProvider.class.getSimpleName()); private static final String TECH_TAG = "EJB XML"; private static final TechnologyTagLevel TECH_TAG_LEVEL = TechnologyTagLevel.INFORMATIONAL; private static final String REGEX_DTD = "(?i).*enterprise.javabeans.*"; @Override public ConditionBuilder when() { return Query.fromType(XmlFileModel.class).withProperty(XmlFileModel.ROOT_TAG_NAME, "ejb-jar"); } public void perform(GraphRewrite event, EvaluationContext context, XmlFileModel payload) { try { Document doc = new XmlFileService(event.getGraphContext()).loadDocument(event, context, payload); extractMetadata(event, context, payload, doc); } catch (Exception ex) { payload.setParseError("Failed to parse EJB-JAR definitions: " + ex.getMessage()); } } private void extractMetadata(GraphRewrite event, EvaluationContext context, XmlFileModel xmlModel, Document doc) { ClassificationService classificationService = new ClassificationService(event.getGraphContext()); TechnologyTagService technologyTagService = new TechnologyTagService(event.getGraphContext()); TechnologyTagModel technologyTag = technologyTagService.addTagToFileModel(xmlModel, TECH_TAG, TECH_TAG_LEVEL); classificationService.attachClassification(event, context, xmlModel, "EJB XML", "Enterprise Java Bean XML Descriptor."); // otherwise, it is a EJB-JAR XML. if (xmlModel.getDoctype() != null) { // check doctype. if (!processDoctypeMatches(xmlModel.getDoctype())) { // move to next document. return; } String version = processDoctypeVersion(xmlModel.getDoctype()); extractMetadata(event, context, xmlModel, doc, version); } else { String namespace = $(doc).find("ejb-jar").namespaceURI(); if (StringUtils.isBlank(namespace)) { namespace = doc.getFirstChild().getNamespaceURI(); } String version = $(doc).attr("version"); // if the version attribute isn't found, then grab it from the XSD name if we can. if (StringUtils.isBlank(version)) { for (NamespaceMetaModel ns : xmlModel.getNamespaces()) { if (StringUtils.equals(ns.getURI(), namespace)) { version = NamespaceUtils.extractVersion(ns.getSchemaLocation()); } } } if (StringUtils.isNotBlank(version)) { technologyTag.setVersion(version); } extractMetadata(event, context, xmlModel, doc, version); } } private void extractMetadata(GraphRewrite event, EvaluationContext context, XmlFileModel xml, Document doc, String versionInformation) { EjbDeploymentDescriptorModel facet = GraphService.addTypeToModel(event.getGraphContext(), xml, EjbDeploymentDescriptorModel.class); if (StringUtils.isNotBlank(versionInformation)) { facet.setSpecificationVersion(versionInformation); } // process all session beans... for (Element element : $(doc).find("session").get()) { processSessionBeanElement(event, context, facet, element); } // process all message driven beans... for (Element element : $(doc).find("message-driven").get()) { processMessageDrivenElement(event, context, facet, element); } // process all entity beans... for (Element element : $(doc).find("entity").get()) { processEntityElement(event, context, facet, element); } } private boolean processDoctypeMatches(DoctypeMetaModel entry) { if (StringUtils.isNotBlank(entry.getPublicId())) { if (Pattern.matches(REGEX_DTD, entry.getPublicId())) { return true; } } if (StringUtils.isNotBlank(entry.getSystemId())) { if (Pattern.matches(REGEX_DTD, entry.getSystemId())) { return true; } } return false; } private String processDoctypeVersion(DoctypeMetaModel entry) { String publicId = entry.getPublicId(); String systemId = entry.getSystemId(); // extract the version information from the public / system ID. String versionInformation = DoctypeUtils.extractVersion(publicId, systemId); return versionInformation; } private void processSessionBeanElement(GraphRewrite event, EvaluationContext context, EjbDeploymentDescriptorModel ejbConfig, Element element) { JavaClassService javaClassService = new JavaClassService(event.getGraphContext()); Set<ProjectModel> applications = ProjectTraversalCache.getApplicationsForProject(event.getGraphContext(), ejbConfig.getProjectModel()); JavaClassModel home = null; JavaClassModel localHome = null; JavaClassModel remote = null; JavaClassModel local = null; JavaClassModel ejb = null; String ejbId = extractAttributeAndTrim(element, "id"); String displayName = extractChildTagAndTrim(element, "display-name"); String ejbName = extractChildTagAndTrim(element, "ejb-name"); // get local class. String localClz = extractChildTagAndTrim(element, "local"); if (localClz != null) { local = getOrCreatePhantom(event, context, javaClassService, localClz); } // get local home class. String localHomeClz = extractChildTagAndTrim(element, "local-home"); if (localHomeClz != null) { localHome = getOrCreatePhantom(event, context, javaClassService, localHomeClz); } // get home class. String homeClz = extractChildTagAndTrim(element, "home"); if (homeClz != null) { home = getOrCreatePhantom(event, context, javaClassService, homeClz); } // get remote class. String remoteClz = extractChildTagAndTrim(element, "remote"); if (remoteClz != null) { remote = getOrCreatePhantom(event, context, javaClassService, remoteClz); } // get the ejb class. String ejbClz = extractChildTagAndTrim(element, "ejb-class"); if (ejbClz != null) { ejb = getOrCreatePhantom(event, context, javaClassService, ejbClz); } String sessionType = extractChildTagAndTrim(element, "session-type"); String transactionType = extractChildTagAndTrim(element, "transaction-type"); Service<EjbSessionBeanModel> sessionBeanService = new GraphService<>(event.getGraphContext(), EjbSessionBeanModel.class); EjbSessionBeanModel sessionBean = sessionBeanService.create(); sessionBean.setApplications(applications); sessionBean.setEjbId(ejbId); sessionBean.setDisplayName(displayName); sessionBean.setBeanName(ejbName); sessionBean.setEjbLocal(local); sessionBean.setEjbLocalHome(localHome); sessionBean.setEjbHome(home); sessionBean.setEjbRemote(remote); sessionBean.setEjbClass(ejb); sessionBean.setSessionType(sessionType); sessionBean.setTransactionType(transactionType); List<EnvironmentReferenceModel> refs = processEnvironmentReference(event.getGraphContext(), element); for (EnvironmentReferenceModel ref : refs) { sessionBean.addEnvironmentReference(ref); } ejbConfig.addEjbSessionBean(sessionBean); } private void processMessageDrivenElement(GraphRewrite event, EvaluationContext context, EjbDeploymentDescriptorModel ejbConfig, Element element) { JavaClassService javaClassService = new JavaClassService(event.getGraphContext()); JavaClassModel ejb = null; Set<ProjectModel> applications = ProjectTraversalCache.getApplicationsForProject(event.getGraphContext(), ejbConfig.getProjectModel()); String ejbId = extractAttributeAndTrim(element, "id"); String displayName = extractChildTagAndTrim(element, "display-name"); String ejbName = extractChildTagAndTrim(element, "ejb-name"); // get the ejb class. String ejbClz = extractChildTagAndTrim(element, "ejb-class"); if (ejbClz != null) { ejb = getOrCreatePhantom(event, context, javaClassService, ejbClz); } String sessionType = extractChildTagAndTrim(element, "session-type"); String transactionType = extractChildTagAndTrim(element, "transaction-type"); String destination = null; for (Element activationConfigPropertyElement : $($(element).find("activation-config")).find("activation-config-property").get()) { String propName = extractChildTagAndTrim(activationConfigPropertyElement, "activation-config-property-name"); String propValue = extractChildTagAndTrim(activationConfigPropertyElement, "activation-config-property-value"); if ("destination".equals(propName)) { destination = propValue; } } destination = StringUtils.trimToNull(destination); Service<EjbMessageDrivenModel> sessionBeanService = new GraphService<>(event.getGraphContext(), EjbMessageDrivenModel.class); EjbMessageDrivenModel mdb = sessionBeanService.create(); mdb.setApplications(applications); mdb.setEjbClass(ejb); mdb.setBeanName(ejbName); mdb.setDisplayName(displayName); mdb.setEjbId(ejbId); mdb.setSessionType(sessionType); mdb.setTransactionType(transactionType); if (StringUtils.isNotBlank(destination)) { JmsDestinationService jmsDestinationService = new JmsDestinationService(event.getGraphContext()); JmsDestinationModel jndiRef = jmsDestinationService.createUnique(applications, destination); mdb.setDestination(jndiRef); } List<EnvironmentReferenceModel> refs = processEnvironmentReference(event.getGraphContext(), element); for (EnvironmentReferenceModel ref : refs) { mdb.addEnvironmentReference(ref); } ejbConfig.addMessageDriven(mdb); } private void processEntityElement(GraphRewrite event, EvaluationContext context, EjbDeploymentDescriptorModel ejbConfig, Element element) { JavaClassService javaClassService = new JavaClassService(event.getGraphContext()); JavaClassModel localHome = null; JavaClassModel local = null; JavaClassModel ejb = null; Set<ProjectModel> applications = ProjectTraversalCache.getApplicationsForProject(event.getGraphContext(), ejbConfig.getProjectModel()); String ejbId = extractAttributeAndTrim(element, "id"); String displayName = extractChildTagAndTrim(element, "display-name"); String ejbName = extractChildTagAndTrim(element, "ejb-name"); String tableName = extractChildTagAndTrim(element, "table-name"); // get local class. String localClz = extractChildTagAndTrim(element, "local"); if (localClz != null) { local = getOrCreatePhantom(event, context, javaClassService, localClz); } // get local home class. String localHomeClz = extractChildTagAndTrim(element, "local-home"); if (localHomeClz != null) { localHome = getOrCreatePhantom(event, context, javaClassService, localHomeClz); } // get the ejb class. String ejbClz = extractChildTagAndTrim(element, "ejb-class"); if (ejbClz != null) { ejb = getOrCreatePhantom(event, context, javaClassService, ejbClz); } String persistenceType = extractChildTagAndTrim(element, "persistence-type"); // create new entity facet. Service<EjbEntityBeanModel> ejbEntityService = new GraphService<>(event.getGraphContext(), EjbEntityBeanModel.class); EjbEntityBeanModel entity = ejbEntityService.create(); entity.setApplications(applications); entity.setPersistenceType(persistenceType); entity.setEjbId(ejbId); entity.setDisplayName(displayName); entity.setBeanName(ejbName); entity.setTableName(tableName); entity.setEjbClass(ejb); entity.setEjbLocalHome(localHome); entity.setEjbLocal(local); List<EnvironmentReferenceModel> refs = processEnvironmentReference(event.getGraphContext(), element); for (EnvironmentReferenceModel ref : refs) { entity.addEnvironmentReference(ref); } ejbConfig.addEjbEntityBean(entity); } private List<EnvironmentReferenceModel> processEnvironmentReference(GraphContext context, Element element) { EnvironmentReferenceService environmentReferenceService = new EnvironmentReferenceService(context); List<EnvironmentReferenceModel> resources = new LinkedList<>(); // find Environment Resource references... for (Element e : $(element).find("resource-ref").get()) { String id = $(e).attr("id"); String type = $(e).child("res-type").text(); String name = $(e).child("res-ref-name").text(); type = StringUtils.trim(type); name = StringUtils.trim(name); EnvironmentReferenceModel ref = environmentReferenceService.findEnvironmentReference(name, EnvironmentReferenceTagType.RESOURCE_REF); if (ref == null) { ref = environmentReferenceService.create(); ref.setName(name); ref.setReferenceId(id); ref.setReferenceType(type); ref.setReferenceTagType(EnvironmentReferenceTagType.RESOURCE_REF); } LOG.info("Reference: " + name + ", Type: " + type); resources.add(ref); } for (Element e : $(element).find("resource-env-ref").get()) { String id = $(e).attr("id"); String type = $(e).child("resource-env-ref-type").text(); String name = $(e).child("resource-env-ref-name").text(); type = StringUtils.trim(type); name = StringUtils.trim(name); EnvironmentReferenceModel ref = environmentReferenceService.findEnvironmentReference(name, EnvironmentReferenceTagType.RESOURCE_ENV_REF); if (ref == null) { ref = environmentReferenceService.create(); ref.setReferenceId(id); ref.setName(name); ref.setReferenceType(type); ref.setReferenceTagType(EnvironmentReferenceTagType.RESOURCE_ENV_REF); } LOG.info("Reference: " + name + ", Type: " + type + ", Tag: " + ref.getReferenceTagType()); resources.add(ref); } for (Element e : $(element).find("message-destination-ref").get()) { String id = $(e).attr("id"); String type = $(e).child("message-destination-type").text(); String name = $(e).child("message-destination-ref-name").text(); type = StringUtils.trim(type); name = StringUtils.trim(name); EnvironmentReferenceModel ref = environmentReferenceService.findEnvironmentReference(name, EnvironmentReferenceTagType.MSG_DESTINATION_REF); if (ref == null) { ref = environmentReferenceService.create(); ref.setReferenceId(id); ref.setName(name); ref.setReferenceType(type); ref.setReferenceTagType(EnvironmentReferenceTagType.MSG_DESTINATION_REF); } LOG.info("Reference: " + name + ", Type: " + type + ", Tag: " + ref.getReferenceTagType()); resources.add(ref); } for (Element e : $(element).find("ejb-local-ref").get()) { String id = $(e).attr("id"); String type = $(e).child("ejb-ref-type").text(); String name = $(e).child("ejb-ref-name").text(); type = StringUtils.trim(type); name = StringUtils.trim(name); EnvironmentReferenceModel ref = environmentReferenceService.findEnvironmentReference(name, EnvironmentReferenceTagType.EJB_LOCAL_REF); if (ref == null) { ref = environmentReferenceService.create(); ref.setReferenceId(id); ref.setName(name); ref.setReferenceType(type); ref.setReferenceTagType(EnvironmentReferenceTagType.EJB_LOCAL_REF); } LOG.info("Reference: " + name + ", Type: " + type + ", Tag: " + ref.getReferenceTagType()); resources.add(ref); } for (Element e : $(element).find("ejb-ref").get()) { String id = $(e).attr("id"); String type = $(e).child("ejb-ref-type").text(); String name = $(e).child("ejb-ref-name").text(); type = StringUtils.trim(type); name = StringUtils.trim(name); EnvironmentReferenceModel ref = environmentReferenceService.findEnvironmentReference(name, EnvironmentReferenceTagType.EJB_REF); if (ref == null) { ref = environmentReferenceService.create(); ref.setReferenceId(id); ref.setName(name); ref.setReferenceType(type); ref.setReferenceTagType(EnvironmentReferenceTagType.EJB_REF); } LOG.info("Reference: " + name + ", Type: " + type + ", Tag: " + ref.getReferenceTagType()); resources.add(ref); } return resources; } private JavaClassModel getOrCreatePhantom(GraphRewrite event, EvaluationContext context, JavaClassService service, String fqcn) { JavaClassModel classModel = service.getOrCreatePhantom(fqcn); if (classModel instanceof AmbiguousJavaClassModel) { for (JavaClassModel reference : ((AmbiguousJavaClassModel) classModel).getReferences()) { markAsReportReportable(event, context, reference); } } else if (!(classModel instanceof PhantomJavaClassModel)) { markAsReportReportable(event, context, classModel); } return classModel; } private void markAsReportReportable(GraphRewrite event, EvaluationContext context, JavaClassModel reference) { AbstractJavaSourceModel originalSource = reference.getOriginalSource(); JavaSourceFileModel decompiledSource = reference.getDecompiledSource(); if (originalSource == null && decompiledSource == null && reference.getClassFile() != null && reference.getClassFile() instanceof JavaClassFileModel) { JavaClassFileModel javaClassFileModel = (JavaClassFileModel) reference.getClassFile(); javaClassFileModel.setSkipDecompilation(false); // try to decompile it FernflowerDecompilerOperation decompilerOperation = new FernflowerDecompilerOperation(); decompilerOperation.setFilesToDecompile(Collections.singletonList(javaClassFileModel)); decompilerOperation.perform(event, context); // Commit to ensure that we are using the latest data (otherwise data from other threads may not // be visible). event.getGraphContext().getGraph().getBaseGraph().commit(); if (reference.getDecompiledSource() != null) { reference.getDecompiledSource().setGenerateSourceReport(true); } } if (originalSource != null) originalSource.setGenerateSourceReport(true); if (decompiledSource != null) decompiledSource.setGenerateSourceReport(true); } private String extractAttributeAndTrim(Element element, String property) { String result = $(element).attr(property); return StringUtils.trimToNull(result); } private String extractChildTagAndTrim(Element element, String property) { String result = $(element).find(property).first().text(); return StringUtils.trimToNull(result); } }