package org.jboss.windup.rules.apps.javaee.rules.jboss; import static org.joox.JOOX.$; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.logging.Logger; 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.model.ProjectModel; import org.jboss.windup.graph.model.resource.FileModel; import org.jboss.windup.graph.service.GraphService; import org.jboss.windup.config.projecttraversal.ProjectTraversalCache; import org.jboss.windup.reporting.model.TechnologyTagLevel; import org.jboss.windup.reporting.service.ClassificationService; import org.jboss.windup.reporting.service.TechnologyTagService; 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.JNDIResourceModel; import org.jboss.windup.rules.apps.javaee.model.JmsDestinationModel; import org.jboss.windup.rules.apps.javaee.rules.DiscoverEjbConfigurationXmlRuleProvider; import org.jboss.windup.rules.apps.javaee.service.EnvironmentReferenceService; import org.jboss.windup.rules.apps.javaee.service.JNDIResourceService; import org.jboss.windup.rules.apps.javaee.service.JmsDestinationService; import org.jboss.windup.rules.apps.javaee.service.VendorSpecificationExtensionService; import org.jboss.windup.rules.apps.xml.model.XmlFileModel; import org.jboss.windup.rules.apps.xml.service.XmlFileService; import org.ocpsoft.rewrite.config.ConditionBuilder; import org.ocpsoft.rewrite.context.EvaluationContext; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Discovers JBoss EJB XML files and parses the related metadata Handles XML files prior to EAP 6. (jboss.xml) * * @author <a href="mailto:bradsdavis@gmail.com">Brad Davis</a> */ @RuleMetadata(phase = InitialAnalysisPhase.class, after = DiscoverEjbConfigurationXmlRuleProvider.class, perform = "Discover JBoss EJB XML Files") public class ResolveJBossLegacyEjbXmlRuleProvider extends IteratingRuleProvider<XmlFileModel> { private static final Logger LOG = Logger.getLogger(ResolveJBossLegacyEjbXmlRuleProvider.class.getSimpleName()); @Override public ConditionBuilder when() { return Query.fromType(XmlFileModel.class).withProperty(XmlFileModel.ROOT_TAG_NAME, "jboss").withProperty(FileModel.FILE_NAME, "jboss.xml"); } @Override public void perform(GraphRewrite event, EvaluationContext context, XmlFileModel payload) { XmlFileService xmlFileService = new XmlFileService(event.getGraphContext()); Document doc = xmlFileService.loadDocumentQuiet(event, context, payload); if ($(doc).find("enterprise-beans").isEmpty()) { LOG.warning("Expected enterprise-beans tag. Ignoring: " + payload.getFileName()); return; } EnvironmentReferenceService envRefService = new EnvironmentReferenceService(event.getGraphContext()); JNDIResourceService jndiResourceService = new JNDIResourceService(event.getGraphContext()); JmsDestinationService jmsDestinationService = new JmsDestinationService(event.getGraphContext()); GraphService<EjbSessionBeanModel> ejbSessionBeanService = new GraphService<>(event.getGraphContext(), EjbSessionBeanModel.class); GraphService<EjbMessageDrivenModel> mdbService = new GraphService<>(event.getGraphContext(), EjbMessageDrivenModel.class); ClassificationService classificationService = new ClassificationService(event.getGraphContext()); classificationService.attachClassification(event, context, payload, "JBoss Legacy EJB XML", "JBoss Enterprise Java Bean XML Descriptor prior to EAP 6."); VendorSpecificationExtensionService vendorSpecificationService = new VendorSpecificationExtensionService(event.getGraphContext()); //mark as vendor extension; create reference to ejb-jar.xml vendorSpecificationService.associateAsVendorExtension(payload, "ejb-jar.xml"); TechnologyTagService technologyTagService = new TechnologyTagService(event.getGraphContext()); technologyTagService.addTagToFileModel(payload, "JBoss EJB XML", TechnologyTagLevel.IMPORTANT); Set<ProjectModel> applications = ProjectTraversalCache.getApplicationsForProject(event.getGraphContext(), payload.getProjectModel()); // first, find all resource managers for later resolution. Map<String, String> resourceManagerReferences = new HashMap<>(); for (Element resourceRef : $(doc).find("resource-managers").children("resource-manager").get()) { String resourceName = $(resourceRef).child("res-name").text(); String resourceJNDI = $(resourceRef).child("res-jndi-name").text(); resourceManagerReferences.put(resourceName, resourceJNDI); LOG.info("Found Resource Manager: " + resourceName + ", " + resourceJNDI); } // register beans to JNDI: http://grepcode.com/file/repository.jboss.org/nexus/content/repositories/releases/org.jboss.ejb3/jboss-ejb3-core/0.1.0/test/naming/META-INF/jboss1.xml?av=f for (Element resourceRef : $(doc).find("resource-ref").get()) { processBinding(envRefService, jndiResourceService, applications, resourceManagerReferences, resourceRef, "res-ref-name", "jndi-name"); } for (Element resourceRef : $(doc).find("resource-env-ref").get()) { processBinding(envRefService, jndiResourceService, applications, resourceManagerReferences, resourceRef, "resource-env-ref-name", "jndi-name"); } for (Element resourceRef : $(doc).find("message-destination-ref").get()) { processBinding(envRefService, jndiResourceService, applications, resourceManagerReferences, resourceRef, "message-destination-ref-name", "jndi-name"); } for (Element resourceRef : $(doc).find("ejb-ref").get()) { processBinding(envRefService, jndiResourceService, applications, resourceManagerReferences, resourceRef, "ejb-ref-name", "jndi-name"); } for (Element resourceRef : $(doc).find("ejb-local-ref").get()) { processBinding(envRefService, jndiResourceService, applications, resourceManagerReferences, resourceRef, "ejb-ref-name", "local-jndi-name"); } for (Element ejbRef : $(doc).find("session").get()) { String ejbName = $(ejbRef).child("ejb-name").content(); String sessionClustered = $(ejbRef).child("clustered").content(); sessionClustered = StringUtils.trim(sessionClustered); //transaction timeout Map<String, Integer> txTimeouts = parseTxTimeout(ejbRef, ejbName); if (StringUtils.isNotBlank(ejbName)) { LOG.info("Looking up name: " + ejbName); for (EjbSessionBeanModel ejb : ejbSessionBeanService.findAllByProperty(EjbMessageDrivenModel.EJB_BEAN_NAME, ejbName)) { String jndi = $(ejbRef).child("jndi-name").content(); String localJNDI = $(ejbRef).child("local-jndi-name").content(); if (StringUtils.isNotBlank(jndi)) { JNDIResourceModel jndiRef = jndiResourceService.createUnique(applications, jndi); ejb.setGlobalJndiReference(jndiRef); } if (StringUtils.isNotBlank(localJNDI)) { JNDIResourceModel jndiRef = jndiResourceService.createUnique(applications, localJNDI); ejb.setLocalJndiReference(jndiRef); } if(StringUtils.equalsIgnoreCase("true", sessionClustered)) { ejb.setClustered(true); } ejb.setTxTimeouts(txTimeouts); } } } // bind the MDBs to the JMS Destination. for (Element messageDrivenRef : $(doc).find("message-driven").get()) { // register the EJB to the JNDI location, if it exists. String ejbName = $(messageDrivenRef).child("ejb-name").text(); Map<String, Integer> txTimeouts = parseTxTimeout(messageDrivenRef, ejbName); LOG.info("Found MDB: " + ejbName); if (StringUtils.isNotBlank(ejbName)) { for (EjbMessageDrivenModel mdb : mdbService.findAllByProperty(EjbMessageDrivenModel.EJB_BEAN_NAME, ejbName)) { String destination = $(messageDrivenRef).child("destination-jndi-name").text(); if (StringUtils.isNotBlank(destination)) { JmsDestinationModel jndiRef = jmsDestinationService.createUnique(applications, destination); mdb.setDestination(jndiRef); } mdb.setTxTimeouts(txTimeouts); } } } } private Map<String, Integer> parseTxTimeout(Element elementRef, String ejbName) { Map<String, Integer> transactionTimeouts = new HashMap<>(); for (Element methodRef : $(elementRef).child("method-attributes").find("method").get()) { String methodName = $(methodRef).child("method-name").content(); String transactionTimeout = $(methodRef).child("transaction-timeout").content(); if(StringUtils.isNotBlank(transactionTimeout)) { try { Integer txTimeout = Integer.parseInt(transactionTimeout); transactionTimeouts.put(methodName, txTimeout); } catch(Exception e) { LOG.info("EJB: "+ejbName+" contains bad reference to TX Timeout on Method: "+methodName); } } } return transactionTimeouts; } private void processBinding(EnvironmentReferenceService envRefService, JNDIResourceService jndiResourceService, Set<ProjectModel> applications, Map<String, String> resourceManagerReferences, Element resourceRef, String tagName, String tagJndi) { String jndiLocation = $(resourceRef).child(tagJndi).text(); String resourceRefName = $(resourceRef).child(tagName).text(); String resourceName = $(resourceRef).child("resource-name").text(); LOG.info("Processing binding: "+$(resourceRef).toString()); LOG.info("JNDI Name: " + jndiLocation + " to Resource: " + resourceRefName); // resolve jndilocation from resourcename...: https://docs.jboss.org/ejb3/app-server/tutorial/jboss_resource_ref/META-INF/jboss.xml if (StringUtils.isBlank(jndiLocation) && StringUtils.isNotBlank(resourceName)) { jndiLocation = resourceManagerReferences.get(resourceName); } if (StringUtils.isNotBlank(jndiLocation) && StringUtils.isNotBlank(resourceRefName)) { JNDIResourceModel resource = jndiResourceService.createUnique(applications, jndiLocation); LOG.info("JNDI Name: " + jndiLocation + " to Resource: " + resourceRefName); // now, look up the resource which is resolved by DiscoverEjbConfigurationXmlRuleProvider for (EnvironmentReferenceModel ref : envRefService.findAllByProperty(EnvironmentReferenceModel.NAME, resourceRefName)) { envRefService.associateEnvironmentToJndi(resource, ref); } } } }