天天看點

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、其他問題

        其他問題包括配置檔案中類替換、代碼中的類替換、各别方法替換等,就不一一說明了。

五、總結

        說實話,這個任務即使已經完成了,我也覺得挺沒必要的,因為與其說把老舊的技術更新一個新的軟體版本,不如直接把架構換成最新的技術,這樣才是從根本上解決問題,效率才更高。可能上司結合目前的業務需要和成本把控,有自己的想法吧,我就不多操心了。。。