package org.jboss.windup.rules.apps.javaee.rules.weblogic; 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.service.GraphService; import org.jboss.windup.config.projecttraversal.ProjectTraversalCache; import org.jboss.windup.reporting.model.ClassificationModel; 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.reporting.category.IssueCategoryRegistry; 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.model.ThreadPoolModel; 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 WebLogic EJB XML files and parses the related metadata * * @author <a href="mailto:bradsdavis@gmail.com">Brad Davis</a> */ @RuleMetadata(phase = InitialAnalysisPhase.class, after = DiscoverEjbConfigurationXmlRuleProvider.class, perform = "Discover WebLogic EJB XML Files") public class ResolveWebLogicEjbXmlRuleProvider extends IteratingRuleProvider<XmlFileModel> { private static final Logger LOG = Logger.getLogger(ResolveWebLogicEjbXmlRuleProvider.class.getSimpleName()); @Override public ConditionBuilder when() { return Query.fromType(XmlFileModel.class).withProperty(XmlFileModel.ROOT_TAG_NAME, "weblogic-ejb-jar"); } @Override public void perform(GraphRewrite event, EvaluationContext context, XmlFileModel weblogicEjbXml) { EnvironmentReferenceService envRefService = new EnvironmentReferenceService(event.getGraphContext()); JNDIResourceService jndiResourceService = new JNDIResourceService(event.getGraphContext()); JmsDestinationService jmsDestinationService = new JmsDestinationService(event.getGraphContext()); XmlFileService xmlFileService = new XmlFileService(event.getGraphContext()); VendorSpecificationExtensionService vendorSpecificationService = new VendorSpecificationExtensionService(event.getGraphContext()); GraphService<ThreadPoolModel> threadPoolService = new GraphService<>(event.getGraphContext(), ThreadPoolModel.class); GraphService<EjbSessionBeanModel> ejbSessionBeanService = new GraphService<>(event.getGraphContext(), EjbSessionBeanModel.class); GraphService<EjbMessageDrivenModel> mdbService = new GraphService<>(event.getGraphContext(), EjbMessageDrivenModel.class); ClassificationService classificationService = new ClassificationService(event.getGraphContext()); ClassificationModel classif = classificationService.attachClassification(event, context, weblogicEjbXml, "WebLogic EJB XML", "WebLogic Enterprise Java Bean XML Descriptor."); // TODO -- this classification duplicates a hint/clsf in the // weblogic-xml-descriptor-04000 XML rule. This should probably get a // better fix, but for now the important thing is to avoid duplicating // the effort added by that hint. classif.setEffort(0); IssueCategoryRegistry issueCategoryRegistry = IssueCategoryRegistry.instance(event.getRewriteContext()); classif.setIssueCategory(issueCategoryRegistry.loadFromGraph(event.getGraphContext(), IssueCategoryRegistry.MANDATORY)); TechnologyTagService technologyTagService = new TechnologyTagService(event.getGraphContext()); technologyTagService.addTagToFileModel(weblogicEjbXml, "WebLogic EJB XML", TechnologyTagLevel.IMPORTANT); Document doc = xmlFileService.loadDocumentQuiet(event, context, weblogicEjbXml); // mark as vendor extension; create reference to ejb-jar.xml vendorSpecificationService.associateAsVendorExtension(weblogicEjbXml, "ejb-jar.xml"); Set<ProjectModel> applications = ProjectTraversalCache.getApplicationsForProject(event.getGraphContext(), weblogicEjbXml.getProjectModel()); for (Element resourceRef : $(doc).find("resource-description").get()) { String jndiLocation = $(resourceRef).child("jndi-name").text(); String resourceName = $(resourceRef).child("res-ref-name").text(); if (StringUtils.isNotBlank(jndiLocation) && StringUtils.isNotBlank(resourceName)) { JNDIResourceModel resource = jndiResourceService.createUnique(applications, jndiLocation); LOG.info("JNDI Name: " + jndiLocation + " to Resource: " + resourceName); // now, look up the resource which is resolved by DiscoverEjbConfigurationXmlRuleProvider for (EnvironmentReferenceModel ref : envRefService.findAllByProperty(EnvironmentReferenceModel.NAME, resourceName)) { envRefService.associateEnvironmentToJndi(resource, ref); } } } // register beans to JNDI for (Element resourceRef : $(doc).find("ejb-local-reference-description").get()) { String resourceName = $(resourceRef).child("ejb-ref-name").text(); String jndiLocation = $(resourceRef).child("jndi-name").text(); if (StringUtils.isNotBlank(jndiLocation) && StringUtils.isNotBlank(resourceName)) { JNDIResourceModel resource = jndiResourceService.createUnique(applications, jndiLocation); LOG.info("JNDI Name: " + jndiLocation + " to Resource: " + resourceName); // now, look up the resource which is resolved by DiscoverEjbConfigurationXmlRuleProvider for (EnvironmentReferenceModel ref : envRefService.findAllByProperty(EnvironmentReferenceModel.NAME, resourceName)) { envRefService.associateEnvironmentToJndi(resource, ref); } for (EjbSessionBeanModel ejb : ejbSessionBeanService.findAllByProperty(EjbSessionBeanModel.EJB_BEAN_NAME, resourceName)) { ejb.setGlobalJndiReference(resource); } } } // bind the EJB beans to JNDI. for (Element enterpriseBeanTag : $(doc).find("weblogic-enterprise-bean").get()) { // register the EJB to the JNDI location, if it exists. String localJndiLocation = $(enterpriseBeanTag).child("local-jndi-name").text(); String jndiLocation = $(enterpriseBeanTag).child("jndi-name").text(); String ejbName = $(enterpriseBeanTag).child("ejb-name").text(); // resolve cluster values String sessionClustered = $(enterpriseBeanTag).find("stateless-bean-is-clusterable").text(); sessionClustered = StringUtils.trim(sessionClustered); if (StringUtils.isBlank(sessionClustered)) { // not stateless or not set. sessionClustered = $(enterpriseBeanTag).find("home-is-clusterable").text(); sessionClustered = StringUtils.trim(sessionClustered); } // parse thread pool information ThreadPoolModel threadPoolModel = null; for (Element poolDescriptor : $(enterpriseBeanTag).find("pool").get()) { String maxSize = $(poolDescriptor).child("max-beans-in-free-pool").text(); String minSize = $(poolDescriptor).child("initial-beans-in-free-pool").text(); threadPoolModel = threadPoolService.create(); threadPoolModel.setApplications(ProjectTraversalCache.getApplicationsForProject(event.getGraphContext(), weblogicEjbXml.getProjectModel())); threadPoolModel.setPoolName(ejbName + "-ThreadPool"); if (StringUtils.isNotBlank(maxSize)) { try { threadPoolModel.setMaxPoolSize(Integer.parseInt(maxSize)); } catch (Exception e) { LOG.warning("Unable to parse max pool size: " + maxSize); } } if (StringUtils.isNotBlank(minSize)) { try { threadPoolModel.setMinPoolSize(Integer.parseInt(minSize)); } catch (Exception e) { LOG.warning("Unable to parse min pool size: " + minSize); } } break; } // set thread pool if (threadPoolModel != null) { for (EjbSessionBeanModel sessionBean : ejbSessionBeanService.findAllByProperty(EjbSessionBeanModel.EJB_BEAN_NAME, ejbName)) { sessionBean.setThreadPool(threadPoolModel); } for (EjbMessageDrivenModel mdb : mdbService.findAllByProperty(EjbMessageDrivenModel.EJB_BEAN_NAME, ejbName)) { mdb.setThreadPool(threadPoolModel); } } Map<String, Integer> txTimeouts = parseTxTimeout(enterpriseBeanTag, ejbName); if (StringUtils.isNotBlank(jndiLocation) && StringUtils.isNotBlank(ejbName)) { JNDIResourceModel jndiRef = jndiResourceService.createUnique(applications, jndiLocation); // look up the EJB by the name, and associate to JNDI. for (EjbSessionBeanModel sessionBean : ejbSessionBeanService.findAllByProperty(EjbSessionBeanModel.EJB_BEAN_NAME, ejbName)) { LOG.info("Registering EJB: " + ejbName + " to JNDI: " + jndiLocation); sessionBean.setGlobalJndiReference(jndiRef); } } if (StringUtils.isNotBlank(localJndiLocation) && StringUtils.isNotBlank(ejbName)) { // look up the EJB by the name, and associate to JNDI. JNDIResourceModel localJndiRef = jndiResourceService.createUnique(applications, localJndiLocation); for (EjbSessionBeanModel sessionBean : ejbSessionBeanService.findAllByProperty(EjbSessionBeanModel.EJB_BEAN_NAME, ejbName)) { LOG.info("Registering EJB: " + ejbName + " to JNDI: " + jndiLocation); sessionBean.setLocalJndiReference(localJndiRef); } } if (txTimeouts.size() > 0 && StringUtils.isNotBlank(ejbName)) { for (EjbSessionBeanModel sessionBean : ejbSessionBeanService.findAllByProperty(EjbSessionBeanModel.EJB_BEAN_NAME, ejbName)) { sessionBean.setTxTimeouts(txTimeouts); } } // extract the JNDI location of any message driven beans. for (Element messageDrivenDescriptor : $(enterpriseBeanTag).find("message-driven-descriptor").get()) { for (EjbMessageDrivenModel mdb : mdbService.findAllByProperty(EjbMessageDrivenModel.EJB_BEAN_NAME, ejbName)) { String destination = $(messageDrivenDescriptor).child("destination-jndi-name").text(); if (StringUtils.isNotBlank(destination)) { JmsDestinationModel jndiRef = jmsDestinationService.createUnique(applications, destination); mdb.setDestination(jndiRef); } if (txTimeouts.size() > 0) { mdb.setTxTimeouts(txTimeouts); } } } // sets the clustered value to the session bean. if (StringUtils.equalsIgnoreCase("true", sessionClustered)) { for (EjbSessionBeanModel sessionBean : ejbSessionBeanService.findAllByProperty(EjbSessionBeanModel.EJB_BEAN_NAME, ejbName)) { LOG.info("Setting bean as clustered: " + ejbName); sessionBean.setClustered(true); } } } } private Map<String, Integer> parseTxTimeout(Element enterpriseBeanTag, String ejbName) { Map<String, Integer> transactionTimeouts = new HashMap<>(); String transactionTimeoutSeconds = $(enterpriseBeanTag).child("transaction-descriptor").child("trans-timeout-seconds").text(); String methodName = "*"; if (StringUtils.isNotBlank(transactionTimeoutSeconds)) { try { Integer txTimeout = Integer.parseInt(transactionTimeoutSeconds); transactionTimeouts.put(methodName, txTimeout); } catch (Exception e) { LOG.info("EJB: " + ejbName + " contains bad reference to TX Timeout on Method: " + methodName); } } return transactionTimeouts; } }