天天看点

JDK1.8+Spring5.6+Hibernate4.3+Struts2 2.5+Tomcat9 系统软件升级全过程 以及问题解决一、背景介绍二、升级思路三、软件jar包情况四、难点及解决办法五、总结

       首先,不得不吐槽一下,现在还在用这些技术,真的稍微有点落后了。无奈自己遇上了这样的事情,面对复杂的JSP页面以及烦琐的配置文件,真得让人看着就头大,难道前后端分离加微服务它不香嘛?但是面对业务及种种原因,最后还是决定对现有的系统进行一下软件版本的升级。废话不多说了,这种事情既然让我遇上了,就要认真负责的干好。下面就开始记录一下项目升级的全过程。

一、背景介绍

      系统是ssh框架的系统,主要升级对象是JDK、Hibernate、Spring、Struts2、Tomcat,具体版本见下图:

JDK1.8+Spring5.6+Hibernate4.3+Struts2 2.5+Tomcat9 系统软件升级全过程 以及问题解决一、背景介绍二、升级思路三、软件jar包情况四、难点及解决办法五、总结

二、升级思路

        这里还要说一下,老大的要求是,在不影响系统运行的前提下,把上述软件版本升级到最新版本。最新!!再我的再三劝阻下,最后决定不一定是最新,相对较新的版本即可(因为最新的各个软件的兼容性很有可能会出现问题)。在这个前提下,我的第一思路是,首先先把涉及到的jar包全部替换成最新版本,然后遇到问题后再一一排查。但是面对众多的软件jar包,各个版本之间的兼容性,一个个试的话,那工作量可就太大了。于是我决定先确定一套可以相互兼容的Spring、Hibernate、Struts2、JDK的版本,基于这套版本,再调整其他软件。这里在说一下Hibernate,本来确定的是5.x的版本,但是由于系统中使用的jbpm版本是4.4,而Hibernate5.x版本和jbpm6才兼容。而jbpm如果想从4.4升级到6.x的版本,还需要对库里的数据表结构和数据进行修改和迁移,总的来说过程就很复杂了。综合考虑后才决定使用Hibernate4.3的版本。

      总结一下整体思路就是:

                     1、确定可以兼容的各软件框架的版本;

                     2、根据框架的版本,调整其他软件的版本;

                     3、调整好版本后,解决遇到的问题,对相应的配置文件及代码进行调整;

                     4、系统可以运行了之后,进行测试,对出现的问题进行解决。

三、软件jar包情况

       这里我没想好如何向大家展示所有需要的jar包,于是就先把我更改了的jar包全贴在这里了,如果有需要的朋友,可以私信联系我。

antlr-2.7.7.jar
aspectjrt.jar
aspectjweaver.jar
byte-buddy-1.10.2.jar
c3p0-0.9.2.1.jar
classmate-1.3.0.jar
commons-fileupload-1.4.jar
commons-io-2.6.jar
commons-lang3-3.8.1.jar
commons-logging-1.2.jar
freemarker-2.3.28.jar
geronimo-jta_1.1_spec-1.1.1.jar
hibernate-c3p0-5.1.13.Final.jar
hibernate-commons-annotations-4.0.4.Final.jar
hibernate-core-4.3.1.Final.jar
hibernate-jpa-2.1-api-1.0.0.Final.jar
jandex-2.0.3.Final.jar
javassist-3.20.0-GA.jar
jboss-logging-3.3.0.Final.jar
log4j-api-2.11.1.jar
mchange-commons-java-0.2.3.4.jar
mysql-connector-5.1.8.jar
ognl-3.1.21.jar
spring-aop-5.0.6.RELEASE.jar
spring-aspects-5.0.6.RELEASE.jar
spring-beans-5.0.6.RELEASE.jar
spring-context-5.0.6.RELEASE.jar
spring-context-indexer-5.0.6.RELEASE.jar
spring-context-support-5.0.6.RELEASE.jar
spring-core-5.0.6.RELEASE.jar
spring-expression-5.0.6.RELEASE.jar
spring-instrument-5.0.6.RELEASE.jar
spring-jcl-5.0.6.RELEASE.jar
spring-jdbc-5.0.6.RELEASE.jar
spring-jms-5.0.6.RELEASE.jar
spring-messaging-5.0.6.RELEASE.jar
spring-orm-4.0.1.RELEASE.jar
spring-oxm-5.0.6.RELEASE.jar
spring-test-5.0.6.RELEASE.jar
spring-tx-5.0.6.RELEASE.jar
spring-web-5.0.6.RELEASE.jar
spring-webflux-5.0.6.RELEASE.jar
spring-webmvc-5.0.6.RELEASE.jar
spring-websocket-5.0.6.RELEASE.jar
struts2-core-2.5.20.jar
struts2-spring-plugin-2.5.14.1.jar
org.springframework.aop-3.0.5.RELEASE.jar
org.springframework.asm-3.0.5.RELEASE.jar
org.springframework.aspects-3.0.5.RELEASE.jar
org.springframework.beans-3.0.5.RELEASE.jar
org.springframework.context.support-3.0.5.RELEASE.jar
org.springframework.context-3.0.5.RELEASE.jar
org.springframework.core-3.0.5.RELEASE.jar
org.springframework.expression-3.0.5.RELEASE.jar
org.springframework.instrument.tomcat-3.0.5.RELEASE.jar
org.springframework.instrument-3.0.5.RELEASE.jar
org.springframework.jdbc-3.0.5.RELEASE.jar
org.springframework.jms-3.0.5.RELEASE.jar
org.springframework.orm-3.0.5.RELEASE.jar
org.springframework.oxm-3.0.5.RELEASE.jar
org.springframework.test-3.0.5.RELEASE.jar
org.springframework.transaction-3.0.5.RELEASE.jar
org.springframework.web.portlet-3.0.5.RELEASE.jar
org.springframework.web.servlet-3.0.5.RELEASE.jar
org.springframework.web.struts-3.0.5.RELEASE.jar
org.springframework.web-3.0.5.RELEASE.jar
struts2-jasperreports-plugin-2.3.35.jar
struts2-jfreechart-plugin-2.3.35.jar
xwork-core-2.3.32.jar
ejb3-persistence.jar
hibernate-annotations.jar

四、难点及解决办法

     1、jbpm4.4与Hibernate4不兼容的问题

          jbpm4.4默认使用的是Hibernate3,如果想要使用Hibernate4整合,则需要自己写一下下面三个类,覆盖jbpm jar包里的类。

JDK1.8+Spring5.6+Hibernate4.3+Struts2 2.5+Tomcat9 系统软件升级全过程 以及问题解决一、背景介绍二、升级思路三、软件jar包情况四、难点及解决办法五、总结

     代码如下:

package org.jbpm.pvm.internal.lob;
 
import java.sql.SQLException;
 
import org.apache.struts2.ServletActionContext;
import org.hibernate.Hibernate;
import org.hibernate.LobHelper;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.internal.SessionImpl;
import org.jbpm.api.JbpmException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Repository;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
 
public class BlobStrategyBlob implements BlobStrategy {
 
	public void set(byte[] bytes, Lob lob) {
		if (bytes != null) {
			lob.cachedBytes = bytes;
			ApplicationContext ac =  WebApplicationContextUtils.getWebApplicationContext(ServletActionContext.getServletContext());
			SessionFactory sessionFactory = (SessionFactory)ac.getBean("sessionFactory");
			Session session = sessionFactory.getCurrentSession();
			lob.blob = session.getLobHelper().createBlob(bytes);
		}
	}
 
	public byte[] get(Lob lob) {
		if (lob.cachedBytes != null) {
			return lob.cachedBytes;
		}
 
		java.sql.Blob sqlBlob = lob.blob;
		if (sqlBlob != null) {
			try {
				return sqlBlob.getBytes(1, (int) sqlBlob.length());
			} catch (SQLException e) {
				throw new JbpmException("couldn't extract bytes out of blob", e);
			}
		}
		return null;
	}
}
           
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jbpm.pvm.internal.processengine;
 
import org.hibernate.cfg.Configuration;
import org.jbpm.api.ProcessEngine;
import org.jbpm.internal.log.Log;
import org.jbpm.pvm.internal.cfg.ConfigurationImpl;
import org.jbpm.pvm.internal.env.EnvironmentFactory;
import org.jbpm.pvm.internal.env.EnvironmentImpl;
import org.jbpm.pvm.internal.env.PvmEnvironment;
import org.jbpm.pvm.internal.env.SpringContext;
import org.jbpm.pvm.internal.wire.descriptor.ProvidedObjectDescriptor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
 
/**
 * this environment factory will see only the singleton beans.
 * 
 * The created {@link SpringEnvironment}s will see the prototype beans and it
 * will cache them.
 * 
 * @author Andries Inze
 */
public class SpringProcessEngine extends ProcessEngineImpl implements EnvironmentFactory, ProcessEngine {
 
  private static final Log log = Log.getLog(SpringProcessEngine.class.getName());
  
  private static final long serialVersionUID = 1L;
 
  private ApplicationContext applicationContext;
 
  public static ProcessEngine create(ConfigurationImpl configuration) {
    SpringProcessEngine springProcessEngine = null;
    
    ApplicationContext applicationContext = null;
    if (configuration.isInstantiatedFromSpring()) {
      applicationContext = (ApplicationContext) configuration.getApplicationContext();
 
      springProcessEngine = new SpringProcessEngine();
      springProcessEngine.applicationContext = applicationContext;
      springProcessEngine.initializeProcessEngine(configuration);
 
      LocalSessionFactoryBean localSessionFactoryBean = springProcessEngine.get(LocalSessionFactoryBean.class);
      Configuration hibernateConfiguration = localSessionFactoryBean.getConfiguration();
      springProcessEngine.processEngineWireContext
          .getWireDefinition()
          .addDescriptor(new ProvidedObjectDescriptor(hibernateConfiguration, true));
      
      springProcessEngine.checkDb(configuration);
 
    } else {
      String springCfg = (String) configuration.getProcessEngineWireContext().get("spring.cfg");
      if (springCfg==null) {
        springCfg = "applicationContext.xml";
      }
      applicationContext = new ClassPathXmlApplicationContext(springCfg);
      springProcessEngine = (SpringProcessEngine) applicationContext.getBean("processEngine");
    }
    
    return springProcessEngine;
  }
  
  public EnvironmentImpl openEnvironment() {
    PvmEnvironment environment = new PvmEnvironment(this);
 
    if (log.isTraceEnabled())
      log.trace("opening jbpm-spring" + environment);
 
    environment.setContext(new SpringContext(applicationContext));
 
    installAuthenticatedUserId(environment);
    installProcessEngineContext(environment);
    installTransactionContext(environment);
 
    return environment;
  }
 
  @SuppressWarnings("unchecked")
  @Override
  public <T> T get(Class<T> type) {
    String[] names = applicationContext.getBeanNamesForType(type);
    
    if (names.length >= 1) {
      
      if (names.length > 1 && log.isWarnEnabled()) {
        log.warn("Multiple beans for type " + type + " found. Returning the first result.");
      }
      
      return (T) applicationContext.getBean(names[0]);
    }
 
    return super.get(type);
  }
 
  @Override
  public Object get(String key) {
    if (applicationContext.containsBean(key)) {
      return applicationContext.getBean(key);
    }
 
    return super.get(key);
  }
}
           
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jbpm.pvm.internal.wire.descriptor;
 
import java.sql.Connection;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.internal.SessionImpl;
import org.jbpm.internal.log.Log;
import org.jbpm.pvm.internal.env.EnvironmentImpl;
import org.jbpm.pvm.internal.tx.HibernateSessionResource;
import org.jbpm.pvm.internal.tx.StandardTransaction;
import org.jbpm.pvm.internal.wire.WireContext;
import org.jbpm.pvm.internal.wire.WireDefinition;
import org.jbpm.pvm.internal.wire.WireException;
 
/**
 * @author Tom Baeyens
 */
public class HibernateSessionDescriptor extends AbstractDescriptor {
 
	private static final long serialVersionUID = 1L;
	private static final Log log = Log.getLog(HibernateSessionDescriptor.class.getName());
 
	protected String factoryName;
	protected boolean useCurrent = false;
	protected boolean tx = true;
	protected boolean close = true;
	protected String standardTransactionName;
	protected String connectionName;
 
	public Object construct(WireContext wireContext) {
		EnvironmentImpl environment = EnvironmentImpl.getCurrent();
		if (environment == null) {
			throw new WireException("no environment");
		}
 
		// get the hibernate-session-factory
		SessionFactory sessionFactory = null;
		if (factoryName != null) {
			sessionFactory = (SessionFactory) wireContext.get(factoryName);
		} else {
			sessionFactory = environment.get(SessionFactory.class);
		}
		if (sessionFactory == null) {
			throw new WireException("couldn't find hibernate-session-factory " + (factoryName != null ? "'" + factoryName + "'" : "by type ") + "to open a hibernate-session");
		}
 
		// open the hibernate-session
		Session session = null;
		if (useCurrent) {
			if (log.isTraceEnabled())
				log.trace("getting current hibernate session");
			session = sessionFactory.getCurrentSession();
 
		} else if (connectionName != null) {
			Connection connection = (Connection) wireContext.get(connectionName);
			if (log.isTraceEnabled())
				log.trace("creating hibernate session with connection " + connection);
			session = (Session) sessionFactory.openStatelessSession(connection);
 
		} else {
			if (log.isTraceEnabled())
				log.trace("creating hibernate session");
			session = sessionFactory.openSession();
		}
 
		StandardTransaction standardTransaction = environment.get(StandardTransaction.class);
		if (standardTransaction != null) {
			HibernateSessionResource hibernateSessionResource = new HibernateSessionResource(session);
			standardTransaction.enlistResource(hibernateSessionResource);
		}
 
		return session;
	}
 
	public Class<?> getType(WireDefinition wireDefinition) {
		return SessionImpl.class;
	}
 
	public void setFactoryName(String factoryName) {
		this.factoryName = factoryName;
	}
 
	public void setTx(boolean tx) {
		this.tx = tx;
	}
 
	public void setStandardTransactionName(String standardTransactionName) {
		this.standardTransactionName = standardTransactionName;
	}
 
	public void setConnectionName(String connectionName) {
		this.connectionName = connectionName;
	}
 
	public void setUseCurrent(boolean useCurrent) {
		this.useCurrent = useCurrent;
	}
 
	public void setClose(boolean close) {
		this.close = close;
	}
}
           

        另外,再发布到服务器上的时候,由于我这里代码的位置是在作为依赖引入到web项目的位置,导致此路径在服务器上的时候,会出现读取不到的情况,导致依然会读取jbpm jar包里的类。解决办法是可以将类加在web主项目中,或者将这三个类打到jbpm jar包里。由于web主项目主要是一些业务代码,所以我是将这三个类打到了jar包里。具体做法是首先将这三个类编译成class文件,然后将发布的war包解压开,将class文件替换掉war包中的相应的类,然后再将war包压缩,发布到服务器,再次测试就没问题了。

      2、Hibernate3之后的版本废除了Session类的connection()方法

解决办法:按照官网推荐的方式,采用了Session类的doWork()方法,替换了connection()方法,如下:

public void deleteAllByIds(final String ids){
		final String sql = "delete from SCM_MST_DOCUMENT_TBL where id in(?)";
		// 4.x后请使用以下方式  
		this.getSessionFactory().getCurrentSession().doWork(new Work() {  
			@Override  
			public void execute(Connection connection) throws SQLException{  
				PreparedStatement ps = null;
				ResultSet rs = null;
				try {
					ps = connection.prepareStatement(sql);
					String[] id = ids.split(",");
					for(String value:id){
						ps.setString(1, value);
						rs = ps.executeQuery();
					}
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}finally {
					ps.close();
					rs.close();
				}     

			}  
		});  
	}
           

      3、Hibernate.createBlob()方法禁用

解决办法:采用SerialBlob类的实例,如下:

public File saveFile(Document document, byte[] b) throws Exception {

		// 保存文件
		File model = new File();

		model.setDocumentId(document.getId());
		SerialBlob blob = new SerialBlob(b);

		model.setFileBlob(blob);
		model.setLoginUser(document.getLoginUser());

		saveOrUpdate(model);

		return model;
	}
           

      4、struts2升级后页面action返回的jsp报错404

解决办法:此问题原因是新版本struts2的配置文件struts.xml中,通配符“*”无效了。解决办法是在配置文件中添加属性strict-method-invocation="false",如下:

JDK1.8+Spring5.6+Hibernate4.3+Struts2 2.5+Tomcat9 系统软件升级全过程 以及问题解决一、背景介绍二、升级思路三、软件jar包情况四、难点及解决办法五、总结

另外,原来项目中对结果的处理如图,是在配置文件里未对结果进行转发到相应的jsp页面,而是在各自的代码中直接返回相应的页面。这种方式我看网上用的很少,所以没用到的请忽略这里。换新版本后,结果显示也出现了问题。解决办法是在配置文件开始出,重新定义了result-type,并且重写了StrutsResultSupport类,继承了新版本struts2的StrutsResultSupport类。如下:

JDK1.8+Spring5.6+Hibernate4.3+Struts2 2.5+Tomcat9 系统软件升级全过程 以及问题解决一、背景介绍二、升级思路三、软件jar包情况四、难点及解决办法五、总结

代码如下:

JDK1.8+Spring5.6+Hibernate4.3+Struts2 2.5+Tomcat9 系统软件升级全过程 以及问题解决一、背景介绍二、升级思路三、软件jar包情况四、难点及解决办法五、总结

      5、jsp页面中的struts标签中的属性有变更

解决办法:escape属性改为escapeHtml;<s:iterator >、<s:bean>的id属性改为var属性;<s:set>标签的name属性换成了var属性。

      6、新版本Spring去除了TimeTask的定时器

解决办法:采用了quartz的定时器,修改了配置文件,如下:

<!-- 配置触发器 -->
	<!-- 配置JOB类 -->
	<bean id="schedulers" class="com.lengsun.scm.io.service.TimerTask"></bean>
	<!-- 配置JobDetail -->
	<bean id="jobDetail"
		class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
		<property name="targetObject" ref="schedulers"></property>
		<property name="targetMethod" value="execute"></property>
	</bean>
	<!-- 配置trigger -->
	<bean id="cronTrigger"
		class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"
		autowire="no">
		<!-- 调度程序 -->
		<property name="jobDetail" ref="jobDetail" />
		<!-- 表达式(重点) -->
		<property name="cronExpression" value="10/60 * * * * ? *" />
	</bean>
	<!-- 配置调度中心 -->
	<bean id="scheduler"
		class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
		autowire="no">
		<property name="triggers">
			<list>
				<ref bean="cronTrigger" />
			</list>
		</property>
	</bean>
           

      7、其他问题

        其他问题包括配置文件中类替换、代码中的类替换、各别方法替换等,就不一一说明了。

五、总结

        说实话,这个任务即使已经完成了,我也觉得挺没必要的,因为与其说把老旧的技术升级一个新的软件版本,不如直接把框架换成最新的技术,这样才是从根本上解决问题,效率才更高。可能领导结合当前的业务需要和成本把控,有自己的想法吧,我就不多操心了。。。