/*******************************************************************************
* Copyright (c) 2007, 2009 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
* Ralf Zahn (ARS) - browser history support (Bug 283291)
******************************************************************************/
package org.eclipse.rwt;
import java.lang.reflect.*;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.rwt.internal.lifecycle.LifeCycleFactory;
import org.eclipse.rwt.internal.lifecycle.RWTLifeCycle;
import org.eclipse.rwt.internal.resources.ResourceManager;
import org.eclipse.rwt.internal.service.*;
import org.eclipse.rwt.internal.widgets.BrowserHistory;
import org.eclipse.rwt.lifecycle.ILifeCycle;
import org.eclipse.rwt.resources.IResourceManager;
import org.eclipse.rwt.service.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.widgets.Display;
/**
* This class provides access to aspects of RWT which are not
* part of the SWT API as RAP needs some additions regarding
* the server and client communication. It is responsible for
* providing access to the {@link ISessionStore} and the
* {@link HttpServletRequest}.
*
* @since 1.0
* @see ILifeCycle
* @see ISessionStore
* @see IResourceManager
* @see HttpServletRequest
* @see HttpServletResponse
*/
public final class RWT {
private static final String LOCALE = RWT.class.getName() + ".LOCALE";
/**
* <p>This utility class helps to provide a similar approach for compile safe
* native language support than {@link org.eclipse.osgi.util.NLS NLS} does.
* We can not use the original approach though, due to the nature of
* server side environments, that have to deal with different locales
* per user session or even requests.</p>
*
* <p>
* Usage:
* <pre>
* public class FooMessages {
* private static final String BUNDLE_NAME = "foo.bar.messages";
*
* public String MyMessage;
*
* public static FooMessages get() {
* return ( FootMessages )RWT.NLS.getISO8859_1Encoded( BUNDLE_NAME, FooMessages.class );
* }
* }
* </pre>
*
* BUNDLE_NAME contains the name of a properties file (without file extension)
* that follows the conventions of standard {@link ResourceBundle} property
* files. For each field (in the example 'MyMessage') there has to be a
* key entry in the localization property file. Use the
* <code>FooMessages</code> like this in the application code:
*
* <pre>
* Label label = ...;
* label.setText( FooMessages.get().MyMessage );
* </pre>
* </p>
*/
public static final class NLS {
private final static Map map = new HashMap();
/**
* Returns a NLS object for the given bundle and type. See
* class description for usage information.
* The resource bundles read by this method have to be ISO 8859-1 encoded.
* This is according to the {@link java.util.Properties Properties} file
* specification.
*
* @param bundleName the bundle to load.
* @param clazz the class of the NLS object to load.
*/
public static Object getISO8859_1Encoded( final String bundleName,
final Class clazz )
{
ClassLoader loader = clazz.getClassLoader();
ResourceBundle bundle
= ResourceBundle.getBundle( bundleName, getLocale(), loader );
return internalGet( bundle, clazz );
}
/**
* Returns a NLS object for the given bundle and type. See
* class description for usage information.
* The resource bundles read by this method have to be UTF-8 encoded. Note
* that this is not according to the {@link java.util.Properties Properties}
* file specification and meant for a more convenient use.
*
* @param bundleName the bundle to load.
* @param clazz the class of the NLS object to load.
*/
public static Object getUTF8Encoded( final String bundleName,
final Class clazz )
{
ClassLoader loader = clazz.getClassLoader();
ResourceBundle bundle
= Utf8ResourceBundle.getBundle( bundleName, getLocale(), loader );
return internalGet( bundle, clazz );
}
private static Object internalGet( final ResourceBundle bundle,
final Class clazz )
{
Object result;
synchronized( map ) {
result = map.get( bundle );
if( result == null ) {
try {
Constructor constructor = clazz.getDeclaredConstructor( null );
constructor.setAccessible( true );
result = constructor.newInstance( null );
} catch( final Exception ex ) {
throw new IllegalStateException( ex.getMessage() );
}
Field[] fields = clazz.getDeclaredFields();
for( int i = 0; i < fields.length; i++ ) {
String fieldName = fields[ i ].getName();
try {
if( String.class.isAssignableFrom( fields[ i ].getType() )
&& Modifier.isPublic( fields[ i ].getModifiers() )
&& !Modifier.isStatic( fields[ i ].getModifiers() ) )
{
try {
String value = bundle.getString( fieldName );
if( value != null ) {
fields[ i ].setAccessible( true );
fields[ i ].set( result, value );
}
} catch( final MissingResourceException mre ) {
fields[ i ].setAccessible( true );
fields[ i ].set( result, "" );
throw mre;
}
}
} catch( final Exception ex ) {
String msg
= "Failed to load localized message for: "
+ clazz.getName()
+ "#"
+ fieldName;
ServletLog.log( msg, ex );
}
}
map.put( bundle, result );
}
}
return result;
}
}
private static final IServiceManager serviceManager = new IServiceManager() {
public void registerServiceHandler( final String id,
final IServiceHandler serviceHandler )
{
ServiceManager.registerServiceHandler( id, serviceHandler );
}
public void unregisterServiceHandler( final String id )
{
ServiceManager.unregisterServiceHandler( id );
}
};
/**
* Returns the instance of the life cycle which is currently processed.
*
* @return instance of {@link ILifeCycle}
*/
public static ILifeCycle getLifeCycle() {
return LifeCycleFactory.getLifeCycle();
}
/**
* Returns the instance of the currently available
* {@link IResourceManager}
*
* @return instance of {@link IResourceManager}
*/
public static IResourceManager getResourceManager() {
return ResourceManager.getInstance();
}
/**
* Returns a manager to add and remove {@link IServiceHandler}s.
*
* @return the {@link IServiceManager}
*/
public static IServiceManager getServiceManager() {
return serviceManager;
}
/**
* Returns the setting store instance for this session.
* @return a {@link ISettingStore}; never <code>null</code>
* @since 1.1
*/
public static ISettingStore getSettingStore() {
return SettingStoreManager.getStore();
}
/**
* Returns the {@link IServiceStore} that is mapped
* to the currently processed request.
*
* @return {@link IServiceStore}
*/
public static IServiceStore getServiceStore() {
return ContextProvider.getStateInfo();
}
/**
* Returns the <code>ISessionStore</code> of the <code>HttpSession</code>
* to which the currently processed request belongs.
*
* @return instance of {@link ISessionStore}
*/
public static ISessionStore getSessionStore() {
return ContextProvider.getSession();
}
/**
* Returns the <code>HttpServletRequest</code> that is currently
* processed.
*
* @return instance of {@link HttpServletRequest}
*/
public static HttpServletRequest getRequest() {
return ContextProvider.getRequest();
}
/**
* Returns the <code>HttpServletResponse</code> that is mapped
* to the currently processed request.
*
* @return instance of {@link HttpServletResponse}
*/
public static HttpServletResponse getResponse() {
return ContextProvider.getResponse();
}
/**
* Returns the preferred <code>Locale</code> that the client will accept
* content in. This is eihter the <code>Locale</code> that was set in
* session-scope using the {@link #setLocale(Locale)} method or the locale
* based on the <code>Accept-Language</code> HTTP header of the current
* request. If neither the <code>Locale</code> was set programmatically, nor
* the client request provides an <code>Accept-Language</code> header, this
* method returns the default locale for the server.
*
* @return the preferred <code>Locale</code> for the client.
*
* @see #setLocale(Locale)
*/
public static Locale getLocale() {
ISessionStore session = ContextProvider.getSession();
Locale result = ( Locale )session.getAttribute( LOCALE );
if( result == null ) {
result = ContextProvider.getRequest().getLocale();
}
if( result == null ) {
result = Locale.getDefault();
}
return result;
}
/**
* Sets the preferred <code>Locale</code> that the client will accept
* content in to current session. The value set can be retrieved with
* the {@link #getLocale()} method.
*
* @see #getLocale()
*/
public static void setLocale( final Locale locale ) {
ISessionStore session = ContextProvider.getSession();
session.setAttribute( LOCALE, locale );
}
/**
* Returns an instance if <code>IBrowserHistory</code> that provides support
* for the browser's history.
*
* @return the browser history support implementation
* @see IBrowserHistory
* @since 1.3
*/
public static IBrowserHistory getBrowserHistory() {
Object instance = SessionSingletonBase.getInstance( BrowserHistory.class );
return ( IBrowserHistory )instance;
}
/**
* Executes the run method of the given <code>runnable</code> on the
* request thread. This method may only be called from the UI thread.
* <p>
* <strong>NOTE:</strong> This API is provisional and may change without
* further notice.
* </p>
* @param runnable the code to be executed on the request thread
* @throws SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the UI thread</li>
* </ul>
* @since 1.3
*/
public static void requestThreadExec( final Runnable runnable ) {
Display display = RWTLifeCycle.getSessionDisplay();
if( display == null || display.getThread() != Thread.currentThread() ) {
SWT.error( SWT.ERROR_THREAD_INVALID_ACCESS );
}
RWTLifeCycle.requestThreadExec( runnable );
}
private RWT() {
// prevent instantiation
}
}