/*******************************************************************************
* Copyright (c) 2002, 2015 Innoopract Informationssysteme GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Innoopract Informationssysteme GmbH - initial API and implementation
* EclipseSource - ongoing development
******************************************************************************/
package org.eclipse.rap.rwt.internal.service;
import static java.util.Collections.synchronizedList;
import static org.eclipse.rap.rwt.testfixture.internal.ConcurrencyTestUtil.joinThreads;
import static org.eclipse.rap.rwt.testfixture.internal.ConcurrencyTestUtil.runInThread;
import static org.eclipse.rap.rwt.testfixture.internal.ConcurrencyTestUtil.startThreads;
import static org.eclipse.rap.rwt.testfixture.internal.SerializationTestUtil.serializeAndDeserialize;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.eclipse.rap.rwt.client.Client;
import org.eclipse.rap.rwt.client.service.ClientInfo;
import org.eclipse.rap.rwt.internal.application.ApplicationContextImpl;
import org.eclipse.rap.rwt.internal.client.ClientMessages;
import org.eclipse.rap.rwt.internal.client.ClientSelector;
import org.eclipse.rap.rwt.internal.lifecycle.ISessionShutdownAdapter;
import org.eclipse.rap.rwt.remote.Connection;
import org.eclipse.rap.rwt.service.ApplicationContextListener;
import org.eclipse.rap.rwt.service.UISession;
import org.eclipse.rap.rwt.service.UISessionEvent;
import org.eclipse.rap.rwt.service.UISessionListener;
import org.eclipse.rap.rwt.testfixture.internal.TestHttpSession;
import org.eclipse.rap.rwt.testfixture.internal.TestLogger;
import org.eclipse.rap.rwt.testfixture.internal.TestServletContext;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
public class UISessionImpl_Test {
private HttpSession httpSession;
private UISessionImpl uiSession;
private List<Throwable> servletLogEntries;
private Locale localeBuffer;
private ApplicationContextImpl applicationContext;
@Before
public void setUp() {
localeBuffer = Locale.getDefault();
Locale.setDefault( Locale.ENGLISH );
applicationContext = mock( ApplicationContextImpl.class );
httpSession = new TestHttpSession();
uiSession = new UISessionImpl( applicationContext, httpSession );
uiSession.attachToHttpSession();
HttpServletRequest request = mock( HttpServletRequest.class );
HttpServletResponse response = mock( HttpServletResponse.class );
ServiceContext serviceContext = new ServiceContext( request, response, uiSession );
ContextProvider.setContext( serviceContext );
servletLogEntries = new LinkedList<Throwable>();
TestServletContext servletContext = ( TestServletContext )httpSession.getServletContext();
servletContext.setLogger( new TestLogger() {
@Override
public void log( String message, Throwable throwable ) {
servletLogEntries.add( throwable );
}
} );
}
@After
public void tearDown() {
ContextProvider.disposeContext();
Locale.setDefault( localeBuffer );
}
@Test
public void testAttachToSession_doesNotOverrideOtherUISession() {
UISessionImpl uiSession2 = new UISessionImpl( applicationContext, httpSession, "foo" );
uiSession2.attachToHttpSession();
UISessionImpl result = UISessionImpl.getInstanceFromSession( httpSession, null );
assertSame( result, uiSession );
}
@Test
public void testGetInstanceFromSession() {
UISessionImpl result = UISessionImpl.getInstanceFromSession( httpSession, null );
assertSame( result, uiSession );
}
@Test
public void testGetInstanceFromSession_withConnectionId() {
UISessionImpl uiSession2 = new UISessionImpl( applicationContext, httpSession, "foo" );
uiSession2.attachToHttpSession();
UISessionImpl result = UISessionImpl.getInstanceFromSession( httpSession, "foo" );
assertSame( result, uiSession2 );
}
@Test
public void testGetInstanceFromSession_returnsNullAfterInvalidate() {
httpSession.invalidate();
UISessionImpl result = UISessionImpl.getInstanceFromSession( httpSession, null );
assertNull( result );
}
@Test
public void testShutdown() {
uiSession.shutdown();
assertNull( UISessionImpl.getInstanceFromSession( httpSession, null ) );
assertFalse( uiSession.isBound() );
}
@Test
public void testShutdown_withConnectionId() {
UISessionImpl uiSession2 = new UISessionImpl( applicationContext, httpSession, "foo" );
uiSession2.attachToHttpSession();
uiSession2.shutdown();
assertNull( UISessionImpl.getInstanceFromSession( httpSession, "foo" ) );
assertFalse( uiSession2.isBound() );
}
@Test
public void testShutdown_doesnNotShutdownAnotherUISession() {
UISessionImpl uiSession2 = new UISessionImpl( applicationContext, httpSession, "foo" );
uiSession2.attachToHttpSession();
uiSession2.shutdown();
assertNotNull( UISessionImpl.getInstanceFromSession( httpSession, null ) );
assertTrue( uiSession.isBound() );
}
@Test
public void testSessionShutdownOnApplicationContextDeactivation() {
ArgumentCaptor<ApplicationContextListener> listenerCaptor
= ArgumentCaptor.forClass( ApplicationContextListener.class );
verify( applicationContext ).addApplicationContextListener( listenerCaptor.capture() );
listenerCaptor.getValue().beforeDestroy( null );
assertNull( UISessionImpl.getInstanceFromSession( httpSession, null ) );
assertFalse( uiSession.isBound() );
}
@Test
public void testGetId() {
assertNotNull( uiSession.getId() );
}
@Test
public void testGetId_isUnique() {
String id = uiSession.getId();
String id2 = new UISessionImpl( null, null ).getId();
assertFalse( id2.equals( id ) );
}
@Test
public void testGetHttpSession() {
assertSame( httpSession, uiSession.getHttpSession() );
}
@Test
public void testSetHttpSession() {
HttpSession anotherSession = new TestHttpSession();
uiSession.setHttpSession( anotherSession );
assertSame( anotherSession, uiSession.getHttpSession() );
}
@Test
public void testSetHttpSession_failsWithNullArgument() {
try {
uiSession.setHttpSession( null );
fail();
} catch( NullPointerException expected ) {
}
}
@Test
public void testSetHttpSession_doesNotChangeId() {
String initialId = uiSession.getId();
TestHttpSession anotherSession = new TestHttpSession();
anotherSession.setId( "some.other.id" );
uiSession.setHttpSession( anotherSession );
String id = uiSession.getId();
assertEquals( initialId, id );
}
@Test
public void testSetHttpSession_doesNotTriggerListener() {
UISessionListener listener = mock( UISessionListener.class );
uiSession.addUISessionListener( listener );
uiSession.setHttpSession( mock( HttpSession.class ) );
verify( listener, never() ).beforeDestroy( any( UISessionEvent.class ) );
}
@Test
public void testIsBound() {
assertTrue( uiSession.isBound() );
}
@Test
public void testIsBound_isFalseAfterSessionWasInvalidated() {
httpSession.invalidate();
assertFalse( uiSession.isBound() );
}
@Test
public void testGetAttribute() {
Object value = new Object();
uiSession.setAttribute( "name", value );
Object result = uiSession.getAttribute( "name" );
assertSame( value, result );
}
@Test
public void testGetAttribute_failsWithNullName() {
try {
uiSession.getAttribute( null );
fail();
} catch( NullPointerException expected ) {
}
}
@Test
public void testGetAttribute_returnsNullWithNonExistingName() {
Object attribute = uiSession.getAttribute( "does.not.exist" );
assertNull( attribute );
}
@Test
public void testGetAttribute_returnsNullWhenUnbound() {
uiSession.setAttribute( "name", null );
httpSession.invalidate();
Object result = uiSession.getAttribute( "name" );
assertNull( result );
}
@Test
public void testSetAttribute_failsWithNullName() {
try {
uiSession.setAttribute( null, new Object() );
fail();
} catch( NullPointerException expected ) {
}
}
@Test
public void testSetAttribute_returnsFalseWhenUnbound() {
httpSession.invalidate();
boolean result = uiSession.setAttribute( "name", null );
assertFalse( result );
}
@Test
public void testSetAttribute_whileDestroyingUISession() {
final AtomicBoolean resultCaptor = new AtomicBoolean();
uiSession.addUISessionListener( new UISessionListener() {
@Override
public void beforeDestroy( UISessionEvent event ) {
resultCaptor.set( uiSession.setAttribute( "name", new Object() ) );
}
} );
httpSession.invalidate();
assertTrue( resultCaptor.get() );
}
@Test
public void testRemoveAttribute_failsWithNullName() {
try {
uiSession.removeAttribute( null );
fail();
} catch( NullPointerException expected ) {
}
}
@Test
public void testRemoveAttribute_removesExistingAttribute() {
uiSession.setAttribute( "name", new Object() );
uiSession.removeAttribute( "name" );
assertNull( uiSession.getAttribute( "name" ) );
}
@Test
public void testRemoveAttribute_returnsNullForNonExistingAttribute() {
uiSession.removeAttribute( "does.not.exist" );
assertNull( uiSession.getAttribute( "does.not.exist" ) );
}
@Test
public void testRemoveAttribute_returnsFalseWhenUnbound() {
uiSession.setAttribute( "name", null );
httpSession.invalidate();
boolean result = uiSession.removeAttribute( "name" );
assertFalse( result );
}
@Test
public void testGetAttributeNames() {
uiSession.setAttribute( "name", new Object() );
Enumeration attributeNames = uiSession.getAttributeNames();
assertTrue( attributeNames.hasMoreElements() );
assertEquals( "name", attributeNames.nextElement() );
assertFalse( attributeNames.hasMoreElements() );
}
@Test
public void testGetAttributeNames_returnsSnapshot() {
uiSession.setAttribute( "name", new Object() );
Enumeration attributeNames = uiSession.getAttributeNames();
uiSession.setAttribute( "other.name", new Object() );
assertTrue( attributeNames.hasMoreElements() );
assertEquals( "name", attributeNames.nextElement() );
assertFalse( attributeNames.hasMoreElements() );
}
@Test
public void testGetAttributeNames_isEmptyWhenUnbound() {
uiSession.setAttribute( "name", "value" );
httpSession.invalidate();
Enumeration attributeNames = uiSession.getAttributeNames();
assertNotNull( attributeNames );
assertFalse( attributeNames.hasMoreElements() );
}
@Test
public void testGetAttributeNames_isThreadSafe() throws InterruptedException {
final List<Throwable> errors = synchronizedList( new ArrayList<Throwable>() );
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Object object = new Object();
uiSession.setAttribute( object.toString(), object );
Enumeration attributeNames = uiSession.getAttributeNames();
while( attributeNames.hasMoreElements() ) {
attributeNames.nextElement();
}
} catch( Throwable e ) {
errors.add( e );
}
}
};
joinThreads( startThreads( 100, runnable ) );
assertEquals( Collections.emptyList(), errors );
}
@Test
public void testAddUISessionListener_failsWithNullArgument() {
try {
uiSession.addUISessionListener( null );
fail();
} catch( NullPointerException expected ) {
}
}
@Test
public void testAddUISessionListener_returnsFalseWhenUnbound() {
httpSession.invalidate();
EmptyUISessionListener listener = new EmptyUISessionListener();
boolean added = uiSession.addUISessionListener( listener );
assertFalse( added );
}
@Test
public void testAddUISessionListener_whileDestroyingUISession() {
final AtomicBoolean aboutUnboundListener = new AtomicBoolean( true );
uiSession.addUISessionListener( new UISessionListener() {
@Override
public void beforeDestroy( UISessionEvent event ) {
UISessionListener listener = new EmptyUISessionListener();
aboutUnboundListener.set( uiSession.addUISessionListener( listener ) );
}
} );
httpSession.invalidate();
assertFalse( aboutUnboundListener.get() );
}
@Test
public void testRemoveUISessionListener_failsWithNullArgument() {
try {
uiSession.removeUISessionListener( null );
fail();
} catch( NullPointerException expected ) {
}
}
@Test
public void testRemoveUISessionListener_returnsFalseWhenUnbound() {
httpSession.invalidate();
EmptyUISessionListener listener = new EmptyUISessionListener();
boolean removed = uiSession.removeUISessionListener( listener );
assertFalse( removed );
}
@Test
public void testBeforeDestroyEvent_hasServiceContext() {
final AtomicBoolean resultCaptor = new AtomicBoolean();
uiSession.addUISessionListener( new UISessionListener() {
@Override
public void beforeDestroy( UISessionEvent event ) {
resultCaptor.set( ContextProvider.hasContext() );
}
} );
httpSession.invalidate();
assertTrue( resultCaptor.get() );
}
@Test
public void testBeforeDestroyEvent_details() {
final List<UISessionEvent> eventLog = new LinkedList<UISessionEvent>();
uiSession.addUISessionListener( new UISessionListener() {
@Override
public void beforeDestroy( UISessionEvent event ) {
eventLog.add( event );
}
} );
httpSession.invalidate();
assertEquals( 1, eventLog.size() );
assertSame( uiSession, eventLog.get( 0 ).getUISession() );
}
@Test
public void testExceptionHandlingInUISessionListeners() {
uiSession.addUISessionListener( new UISessionListener() {
@Override
public void beforeDestroy( UISessionEvent event ) {
throw new RuntimeException();
}
} );
uiSession.addUISessionListener( new UISessionListener() {
@Override
public void beforeDestroy( UISessionEvent event ) {
throw new RuntimeException();
}
} );
httpSession.invalidate();
assertEquals( 2, servletLogEntries.size() );
}
@Test
public void testShutdownCallback() {
ArgumentCaptor<Runnable> shutdownCallbackCaptor = ArgumentCaptor.forClass( Runnable.class );
UISessionListener listener = mock( UISessionListener.class );
ISessionShutdownAdapter adapter = mock( ISessionShutdownAdapter.class );
uiSession.addUISessionListener( listener );
uiSession.setShutdownAdapter( adapter );
uiSession.shutdown();
verify( adapter ).interceptShutdown();
verify( adapter ).setShutdownCallback( shutdownCallbackCaptor.capture() );
verify( listener, never() ).beforeDestroy( any( UISessionEvent.class ) );
assertTrue( uiSession.isBound() );
shutdownCallbackCaptor.getValue().run();
verify( listener ).beforeDestroy( any( UISessionEvent.class ) );
assertFalse( uiSession.isBound() );
}
@Test
public void testOverrideAtributeWithNull() {
String attributeName = "name";
uiSession.setAttribute( attributeName, new Object() );
uiSession.setAttribute( attributeName, null );
assertNull( uiSession.getAttribute( attributeName ) );
}
@Test
public void testOverrideSerializableAttributeWithNonSerializable() {
String attributeName = "name";
Serializable serializableAttribute = new String();
uiSession.setAttribute( attributeName, serializableAttribute );
Object overridingAtribute = new Object();
uiSession.setAttribute( attributeName, overridingAtribute );
assertSame( overridingAtribute, uiSession.getAttribute( attributeName ) );
}
@Test
public void testOverrideNonSerializableAttributeWithSerializable() {
String attributeName = "name";
Object nonSerializableAttribute = new Object();
uiSession.setAttribute( attributeName, nonSerializableAttribute );
Serializable overridingAtribute = new String();
uiSession.setAttribute( attributeName, overridingAtribute );
assertSame( overridingAtribute, uiSession.getAttribute( attributeName ) );
}
@Test
public void testGetClient() {
Client client = mock( Client.class );
ClientSelector clientSelector = mock( ClientSelector.class );
when( clientSelector.getSelectedClient( any( UISession.class ) ) ).thenReturn( client );
when( applicationContext.getClientSelector() ).thenReturn( clientSelector );
Client result = uiSession.getClient();
assertSame( client, result );
}
@Test
public void testGetConnection_returnsAnObject() {
Connection result = uiSession.getConnection();
assertNotNull( result );
}
@Test
public void testGetConnection_returnsSameObject() {
Connection result = uiSession.getConnection();
assertSame( uiSession.getConnection(), result );
}
@Test
public void testSetLocale_canBeResetWithNull() {
fakeClient( mockClientWithLocale( null ) );
uiSession.setLocale( Locale.ITALIAN );
uiSession.setLocale( null );
assertSame( Locale.getDefault(), uiSession.getLocale() );
}
@Test
public void testGetLocale_returnsSetLocale() {
fakeClient( mockClientWithLocale( null ) );
uiSession.setLocale( Locale.UK );
Locale locale = uiSession.getLocale();
assertSame( Locale.UK, locale );
}
@Test
public void testGetLocale_fallsBackToClientLocale() {
fakeClient( mockClientWithLocale( Locale.ITALIAN ) );
Locale locale = uiSession.getLocale();
assertSame( Locale.ITALIAN, locale );
}
@Test
public void testGetLocale_fallsBackToSystemLocale_withoutClientInfo() {
fakeClient( mock( Client.class ) );
Locale locale = uiSession.getLocale();
assertSame( Locale.getDefault(), locale );
}
@Test
public void testGetLocale_fallsBackToSystemLocale_withoutClientLocale() {
fakeClient( mockClientWithLocale( null ) );
Locale locale = uiSession.getLocale();
assertSame( Locale.getDefault(), locale );
}
@Test
public void testGetLocale_worksInBackgroundThread() throws Throwable {
fakeClient( mock( Client.class ) );
final AtomicReference<Locale> localeCaptor = new AtomicReference<Locale>();
runInThread( new Runnable() {
@Override
public void run() {
localeCaptor.set( uiSession.getLocale() );
}
} );
assertNotNull( localeCaptor.get() );
}
@Test
public void testSetLocale_updatesClientMessages() {
ClientMessages messages = mock( ClientMessages.class );
fakeClient( mockClientWithClientMessages( messages ) );
uiSession.setLocale( Locale.CANADA );
verify( messages ).update( eq( Locale.CANADA ) );
}
@Test
public void testSetLocale_doesNotUpdateClientMessagesIfUnchanged() {
ClientMessages messages = mock( ClientMessages.class );
Client client = mockClientWithLocale( Locale.CANADA );
when( client.getService( same( ClientMessages.class ) ) ).thenReturn( messages );
fakeClient( client );
uiSession.setLocale( Locale.CANADA );
verify( messages, times( 0 ) ).update( eq( Locale.CANADA ) );
}
@Test
public void testSetLocale_updatesWithDefault() {
ClientMessages messages = mock( ClientMessages.class );
Client client = mockClientWithLocale( Locale.CANADA );
when( client.getService( same( ClientMessages.class ) ) ).thenReturn( messages );
fakeClient( client );
uiSession.setLocale( Locale.ITALY );
reset( messages );
uiSession.setLocale( null );
verify( messages ).update( eq( Locale.CANADA ) );
}
@Test
public void testExec_failsWithNullArgument() {
try {
uiSession.exec( null );
fail();
} catch( NullPointerException exception ) {
assertTrue( exception.getMessage().contains( "runnable" ) );
}
}
@Test
public void testExec_executesRunnable() {
Runnable runnable = mock( Runnable.class );
uiSession.exec( runnable );
verify( runnable ).run();
}
@Test
public void testExec_executesRunnableWithSameContextInUIThread() {
ServiceContext context = ContextProvider.getContext();
ContextTrackerRunnable runnable = new ContextTrackerRunnable();
uiSession.exec( runnable );
assertSame( context, runnable.getContext() );
}
@Test
public void testExec_executesRunnableWithFakeContextInBGThread() throws Throwable {
ServiceContext context = ContextProvider.getContext();
final ContextTrackerRunnable runnable = new ContextTrackerRunnable();
runInThread( new Runnable() {
@Override
public void run() {
uiSession.exec( runnable );
}
} );
assertNotSame( context, runnable.getContext() );
assertSame( uiSession, runnable.getUISession() );
}
@Test
public void testExec_executesRunnableWithFakeContextInDifferentUIThread() throws Throwable {
ServiceContext context = ContextProvider.getContext();
final ContextTrackerRunnable runnable = new ContextTrackerRunnable();
runInThread( new Runnable() {
@Override
public void run() {
HttpServletRequest request = mock( HttpServletRequest.class );
HttpServletResponse response = mock( HttpServletResponse.class );
ApplicationContextImpl applicationContext = mock( ApplicationContextImpl.class );
ContextProvider.setContext( new ServiceContext( request, response, applicationContext ) );
uiSession.exec( runnable );
ContextProvider.disposeContext();
}
} );
assertNotSame( context, runnable.getContext() );
assertSame( uiSession, runnable.getUISession() );
}
@Test
public void testSetApplicationContext() {
ApplicationContextImpl otherApplicationContext = mock( ApplicationContextImpl.class );
uiSession.setApplicationContext( otherApplicationContext );
assertSame( otherApplicationContext, uiSession.getApplicationContext() );
}
@Test
public void testSetApplicationContext_updatesApplicationContextListener() {
ApplicationContextImpl otherApplicationContext = mock( ApplicationContextImpl.class );
uiSession.setApplicationContext( otherApplicationContext );
verify( applicationContext )
.removeApplicationContextListener( any( ApplicationContextListener.class ) );
verify( otherApplicationContext )
.addApplicationContextListener( any( ApplicationContextListener.class ) );
}
@Test
public void testDestroy_removesApplicationContextListener() {
uiSession.shutdown();
verify( applicationContext )
.removeApplicationContextListener( any( ApplicationContextListener.class ) );
}
@Test
public void testDestroy_nullsOutApplicationContext() {
uiSession.shutdown();
assertNull( uiSession.getApplicationContext() );
}
@Test
public void testApplicationContextInUISessionIsNotSerialized() throws Exception {
UISessionImpl deserializedUiSession = serializeAndDeserialize( uiSession );
assertNull( deserializedUiSession.getApplicationContext() );
}
@Test
public void testGetConnectionId_withoutConnectionId() {
assertNull( uiSession.getConnectionId() );
}
@Test
public void testGetConnectionId_withConnectionId() {
uiSession = new UISessionImpl( applicationContext, httpSession, "foo" );
assertEquals( "foo", uiSession.getConnectionId() );
}
private static Client mockClientWithLocale( Locale locale ) {
Client client = mock( Client.class );
ClientInfo clientInfo = mock( ClientInfo.class );
when( clientInfo.getLocale() ).thenReturn( locale );
when( client.getService( same( ClientInfo.class ) ) ).thenReturn( clientInfo );
return client;
}
private static Client mockClientWithClientMessages( ClientMessages messages ) {
Client client = mock( Client.class );
when( client.getService( same( ClientMessages.class ) ) ).thenReturn( messages );
return client;
}
private void fakeClient( Client client ) {
ClientSelector clientSelector = mock( ClientSelector.class );
when( clientSelector.getSelectedClient( any( UISession.class ) ) ).thenReturn( client );
when( applicationContext.getClientSelector() ).thenReturn( clientSelector );
}
private static class EmptyUISessionListener implements UISessionListener {
@Override
public void beforeDestroy( UISessionEvent event ) {
}
}
private static class ContextTrackerRunnable implements Runnable {
private final AtomicReference<ServiceContext> context = new AtomicReference<ServiceContext>();
private final AtomicReference<UISession> uiSession = new AtomicReference<UISession>();
@Override
public void run() {
context.set( ContextProvider.getContext() );
uiSession.set( ContextProvider.getContext().getUISession() );
}
public ServiceContext getContext() {
return context.get();
}
public UISession getUISession() {
return uiSession.get();
}
}
}