Я развернул зрелое веб-приложение на новом сервере на tomcat 7. База данных, используемая системой, совершенно пуста, поскольку мы находимся на ранних стадиях ее настройки для использования.
Переход к приложению, вы получаете страницу входа.Войдите в систему, и обычно вы попадете на главную страницу приложения.
Но после прихода на следующее утро у нас всегда возникает одна и та же проблема:
- Мы поднимаем логинЭкран - без проблем
- Введите наше имя пользователя и пароль - система зависает
- Заходим в tomcat и, используя системный трей, останавливаем сервис.
- Индикатор выполнения службы остановки останавливается, а затем исчезает, но в диалоговом окне свойств tomcat по-прежнему отображается «Запущено», а кнопки «Пуск» и «Стоп» отключены.
- Мы проверяем логи tomcat и ошибок нет.
- Мы перезагружаем сервер, и он снова работает нормально
Ничего очевидного мы не видим.Запрос tomcat 'Find Leaks' ничего не показывает, а просмотр размеров кучи в VisualVM показывает непротиворечивую схему захвата кучи с последующим сбором мусора, приводящим его к тому же низкому уровню (так что никаких явных утечек)
Я думал, что это может быть тайм-аут подключения MySQL, но это не должно иметь место, потому что, если я войду в систему с неправильным паролем, система перейдет в базу данных, чтобы проверить пароль, и вернется, как ожидается, с «неправильным паролем».Единственный момент, когда он не срабатывает, это если вы вводите правильный пароль.
Единственная подсказка, которая у нас есть, - это ошибка при входе в систему, когда система использует некоторый пользовательский код для определения хоста пользователя.name:
2019-02-14 08:10:14,277 08:10:14.277 [http-bio-8080-exec-9] ERROR com.sw.app.ui.UserAuthenticatedWebSession - Unknown host!
java.net.UnknownHostException: null
at java.net.Inet6AddressImpl.getHostByAddr(Native Method) ~[na:1.8.0_201]
at java.net.InetAddress$2.getHostByAddr(Unknown Source) ~[na:1.8.0_201]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_201]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_201]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_201]
at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_201]
at com.sw.app.data.utils.NetUtilities.getHostName(NetUtilities.java:114) ~[app-data-4.0.15.1-SNAPSHOT.jar:na]
Это вызывается только в том случае, если пользователь успешно вошел в систему, чтобы сохранить данные о том, откуда он вошел, но исключение было обнаружено в коде, а затем просто зарегистрировано, а не распространено вверх, и тогда мыиспользовать имя хоста по умолчанию «неизвестно».Это код:
public static String getHostName( InetAddress inaHost ) throws UnknownHostException
{
try {
Class<? extends InetAddress> clazz = Class.forName( "java.net.InetAddress" ).asSubclass( InetAddress.class );
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
constructors[0].setAccessible( true );
InetAddress ina = (InetAddress)constructors[0].newInstance();
Field[] fields = ina.getClass().getDeclaredFields();
for( Field field : fields ) {
// Depends on the version of java we are dealing with:
// Older version - single nameservice
if( field.getName().equals( "nameService" ) ) {
return getHostName( field.get( null ), inaHost );
} else if( field.getName().equals( "nameServices" ) ) {
// newer version - multiple name services possible
StringBuilder builder = new StringBuilder();
field.setAccessible( true );
// A list of nameservice objects
@SuppressWarnings( "unchecked" )
List<Object> nameServices = (List<Object>)field.get( null );
for( Object nameService : nameServices ) {
String hostName = getHostName( nameService, inaHost );
if( builder.length() > 0 ) {
builder.append( ", " );
}
builder.append( hostName );
}
return builder.toString();
}
}
} catch( ClassNotFoundException cnfe ) {
throw new InvalidOperationException( "Class not found when looking up host name", cnfe );
} catch( IllegalAccessException iae ) {
throw new InvalidOperationException( "Cannot access method/field", iae );
} catch( InstantiationException ie ) {
throw new InvalidOperationException( "Cannot instantiate class", ie );
} catch( InvocationTargetException ite ) {
throw (UnknownHostException)ite.getCause();
}
return null;
}
/**
* Get the host name using reflection on the hidden class implementation of the InetAddress details.
* @param p_nameService
* @param p_address
* @return
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private static String getHostName( Object nameService, InetAddress address ) throws IllegalAccessException, InvocationTargetException {
Method[] methods = nameService.getClass().getDeclaredMethods();
for( Method method : methods ) {
// The nameService is assumed to have a method, getHostByAddr, which takes the byte[] inet address
if( method.getName().equals( "getHostByAddr" ) ) {
method.setAccessible( true );
return (String)method.invoke( nameService, address.getAddress() );
}
}
return "";
}
У кого-нибудь есть похожие проблемы?
- Правка -
Вот класс компонента конфигурации базы данных.
@Configuration
public class AppPersistence {
private static final Logger LOGGER = LoggerFactory.getLogger( AppPersistence.class );
protected static final String INTERNAL_IP_DOMAIN = "*******";
protected static final String JDBC_PROTOCOL = "jdbc:mysql://";
protected static final String DEFAULT_DATABASE_NAME = "*******";
/** The path for context-based property lookups */
protected static final String CONTEXT_LOOKUP_PATH = "java:comp/env";
/** This is the default location for the database - on the same machine as the deployment */
protected static final String DB_LOCAL = JDBC_PROTOCOL + "localhost:3306/" + DEFAULT_DATABASE_NAME;
@Bean
public DataSource createDataSource() throws Exception {
BasicDataSource source = new BasicDataSource();
// allow for parameterised config
source.setDriverClassName( Driver.class.getName() );
source.setUrl( getProperty( "app.database.url", DB_LOCAL ) );
source.setUsername( getProperty( "app.database.username", "*****" ) );
source.setPassword( getProperty( "app.database.password", "****" ) );
LOGGER.warn( "Connecting to: " + source.getUrl() );
return source;
}
protected String getProperty( String name, String default ) {
// first check system properties
String val = System.getProperty( name );
if( val != null ) {
logLookup( "System Properties", name, val );
return val;
}
// check environment variables
val = System.getenv( name );
if( val != null ) {
logLookup( "System Environment Variables", name, val );
return val;
}
// if we are deployed to a container, check the environment variables in that.
try {
Context context = InitialContext.doLookup( "java:comp/env" );
if( context != null ) {
Object valObj = context.lookup( name );
if( valObj != null ) {
logLookup( "Context", name, valObj.toString() );
return valObj.toString();
}
}
} catch( NamingException e ) {
// if running on a dev machine this will probably happen
LOGGER.warn( "Could not find context for lookup of " + p_name + " - assuming running in dev mode with defaults. Error was: " + e.toString( true ) );
LOGGER.info( "Error received on lookup of " + name + ":", e );
}
return p_default;
}
protected void logLookup( String source, String lookup, String value ) {
if( value.contains( "password" ) ) {
// avoid displaying any password info
LOGGER.warn( "Successfully looked up sensitive value from " + source + " for name '" + lookup + "': [******]" );
} else {
LOGGER.warn( "Successfully looked up value from " + source + " for name '" + lookup + "': '" + value + "'" );
}
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory( DataSource dataSource ) {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setPersistenceUnitName( "com.sw.app.data.persistence" );
entityManagerFactory.setDataSource( dataSource );
entityManagerFactory.setJpaVendorAdapter( new HibernateJpaVendorAdapter() );
entityManagerFactory.setLoadTimeWeaver( new InstrumentationLoadTimeWeaver() );
entityManagerFactory.setJpaDialect( new HibernateJpaDialect() );
entityManagerFactory.setPackagesToScan( "com.sw.app.data", "com.sw.app.rawimport",
"com.sw.app.view", "com.sw.app.warranty" );
entityManagerFactory.setJpaPropertyMap( hibernateJpaProperties( dataSource ) );
return entityManagerFactory;
}
private Map<String, ?> hibernateJpaProperties( DataSource dataSource ) {
HashMap<String, String> properties = new HashMap<>();
// Need to copy these values over, otherwise c3p0 can't see them.
if( dataSource instanceof BasicDataSource ) {
BasicDataSource source = (BasicDataSource)p_dataSource;
properties.put( "hibernate.connection.driver_class", source.getDriverClassName() );
properties.put( "hibernate.connection.url", source.getUrl() );
properties.put( "hibernate.connection.username", source.getUsername() );
properties.put( "hibernate.connection.password", source.getPassword() );
}
// Added to avoid some merge problems when updating entities (eg contact to custimport)
properties.put( "hibernate.event.merge.entity_copy_observer", "allow" );
// Second level cache
properties.put( "hibernate.cache.use_second_level_cache", "true" );
properties.put( "hibernate.cache.use_query_cache", "true" );
properties.put( "hibernate.cache.provider_class", "org.hibernate.cache.EhCacheProvider" );
properties.put( "hibernate.cache.region.factory_class", EhCacheRegionFactory.class.getName() );
properties.put( "hibernate.generate_statistics", "false" );
properties.put( "hibernate.show_sql", "false" );
properties.put( "hibernate.format_sql", "false" );
// validate | update | create | create-drop -->
properties.put( "hibernate.hbm2ddl.auto", "update" );
properties.put( "hibernate.dialect", MySQL5Dialect.class.getName() );
// [main] WARN org.hibernate.cfg.AnnotationBinder - HHH000457: Joined inheritance hierarchy [com.sw.system4.data.collateral.AbstractCollateral] defined explicit @DiscriminatorColumn. Legacy Hibernate behavior was to ignore the @DiscriminatorColumn. However, as part of issue HHH-6911 we now apply the explicit @DiscriminatorColumn. If you would prefer the legacy behavior, enable the `hibernate.discriminator.ignore_explicit_for_joined` setting (hibernate.discriminator.ignore_explicit_for_joined=true) -->
properties.put( "hibernate.discriminator.ignore_explicit_for_joined", "true" );
//properties.put("hibernate.hbm2ddl.import_files", "insert-data.sql");
//properties.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
// This wasnt required in persistence.xml, but for some reason is here.
properties.put( "hibernate.connection.provider_class", C3P0ConnectionProvider.class.getName() );
// just adding c3p0 props was enough in persistence.xml, but not here.
properties.put( "hibernate.c3p0.min_size", "5" );
properties.put( "hibernate.c3p0.max_size", "20" );
properties.put( "hibernate.c3p0.timeout", "300" ); // 5mins
properties.put( "hibernate.c3p0.max_statements", "50" );
properties.put( "hibernate.c3p0.idle_test_period", "100" );
properties.put( "hibernate.c3p0.preferredTestQuery", "select 1" );
properties.put( "hibernate.c3p0.testConnectionOnCheckout", "true" );
properties.put( "hibernate.c3p0.numHelperThreads", "12" );
properties.put( "hibernate.c3p0.maxStatementsPerConnection", "25" );
properties.put( "hibernate.c3p0.statementCacheNumDeferredCloseThreads", "1" );
return l_properties;
}
@Bean
public JpaTransactionManager transactionManager( EntityManagerFactory emf ) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory( emf );
return jpaTransactionManager;
}
}