/*******************************************************************************
* Copyright (c) 2002, 2010 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.rwt.internal.service;
import java.io.*;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.rwt.branding.AbstractBranding;
import org.eclipse.rwt.internal.RWTMessages;
import org.eclipse.rwt.internal.branding.BrandingUtil;
import org.eclipse.rwt.internal.lifecycle.*;
import org.eclipse.rwt.internal.resources.ResourceManager;
import org.eclipse.rwt.internal.theme.ThemeUtil;
import org.eclipse.rwt.internal.util.EncodingUtil;
import org.eclipse.rwt.internal.util.HTML;
import org.eclipse.rwt.resources.IResourceManager;
import org.eclipse.swt.internal.graphics.TextSizeDetermination;
public final class RWTStartupPageConfigurer
implements StartupPage.IStartupPageConfigurer
{
// private static final String PACKAGE_NAME
// = RWTStartupPageConfigurer.class.getPackage().getName();
// private final static String FOLDER = PACKAGE_NAME.replace( '.', '/' );
private final static String INDEX_TEMPLATE = "/rwt-index.html";
// TODO [fappel]: think about clusters
// cache control variables
private static int probeCount;
private static long lastModified = System.currentTimeMillis();
private static StartupPageTemplateHolder template;
private static final List registeredBrandings = new ArrayList();
private static String templateFile;
////////////////////////////////////////////////////
// ILifeCycleServiceHandlerConfigurer implementation
public RWTStartupPageConfigurer() {
templateFile = INDEX_TEMPLATE;
}
public static final void setTemplateFile( final String newTemplateFile ) {
templateFile = newTemplateFile;
}
public StartupPageTemplateHolder getTemplate() throws IOException {
readContent();
template.reset();
template.replace( StartupPageTemplateHolder.VAR_LIBRARIES, getLibraries() );
template.replace( StartupPageTemplateHolder.VAR_APPSCRIPT, getAppScript() );
applyBranding();
return template;
}
public synchronized boolean isModifiedSince() {
boolean result;
int currentProbeCount = TextSizeDetermination.getProbeCount();
if( probeCount != currentProbeCount ) {
lastModified = System.currentTimeMillis();
probeCount = currentProbeCount;
}
HttpServletRequest request = ContextProvider.getRequest();
HttpServletResponse response = ContextProvider.getResponse();
// TODO [rh] this is a preliminary fix for a behavior that was easily
// reproducible in IE but also happened in FF: when restarting a
// web app (hit return in location bar), the browser used a cached
// version of the index.html *without* sending a request to ask
// whether the cached page can be used.
// fix for bug 220733: append no-store to the Cache-Control header
response.addHeader( "Cache-Control",
"max-age=0, no-cache, must-revalidate, no-store" );
long dateHeader = request.getDateHeader( "If-Modified-Since" );
// Because browser store the date in format with seconds as smallest unit
// add one second to avoid rounding problems...
if( dateHeader + 1000 < lastModified ) {
result = true;
response.addDateHeader( "Last-Modified", lastModified );
// TODO [fappel]: Think about "expires"-header for proxy usage.
// TODO [fappel]: Seems as if Safari doesn't react to last-modified.
} else {
result = false;
response.setStatus( HttpServletResponse.SC_NOT_MODIFIED );
}
return result;
}
///////////////////////////////////////
// Helping methods to load startup page
private static void readContent() throws IOException {
if( template == null ) {
InputStream stream = loadTemplateFile();
InputStreamReader isr
= new InputStreamReader( stream, HTML.CHARSET_NAME_ISO_8859_1 );
BufferedReader reader = new BufferedReader( isr );
try {
String line = reader.readLine();
StringBuffer buffer = new StringBuffer();
while( line != null ) {
buffer.append( line );
buffer.append( "\n" );
line = reader.readLine();
}
template = new StartupPageTemplateHolder( buffer.toString() );
} finally {
reader.close();
}
}
}
private static InputStream loadTemplateFile() throws IOException {
InputStream result = null;
IResourceManager manager = ResourceManager.getInstance();
ClassLoader buffer = manager.getContextLoader();
manager.setContextLoader( RWTStartupPageConfigurer.class.getClassLoader() );
try {
result = manager.getResourceAsStream( templateFile );
if ( result == null ) {
String text = "Failed to load Browser Survey HTML Page. "
+ "Resource {0} could not be found.";
Object[] param = new Object[]{ templateFile };
String msg = MessageFormat.format( text, param );
throw new IOException( msg );
}
} finally {
manager.setContextLoader( buffer );
}
return result;
}
/////////////////////////////////////////
// Helping methods to adjust startup page
private static String getAppScript() throws IOException {
fakeWriter();
IServiceStateInfo stateInfo = ContextProvider.getStateInfo();
HtmlResponseWriter writer = stateInfo.getResponseWriter();
writer.startElement( HTML.SCRIPT, null );
writer.writeText( "safd", null );
writer.clearBody();
try {
// TODO: [fappel] this works only as long as only one display per
// session is supported...
DisplayUtil.writeAppScript( "w1" );
return getContent( writer );
} finally {
restoreWriter();
}
}
private static void fakeWriter() {
IServiceStateInfo stateInfo = ContextProvider.getStateInfo();
HtmlResponseWriter original = stateInfo.getResponseWriter();
String key = RWTStartupPageConfigurer.class.getName();
stateInfo.setAttribute( key, original );
HtmlResponseWriter fake = new HtmlResponseWriter();
stateInfo.setResponseWriter( fake );
}
private static void restoreWriter() {
IServiceStateInfo stateInfo = ContextProvider.getStateInfo();
String key = RWTStartupPageConfigurer.class.getName();
HtmlResponseWriter writer
= ( HtmlResponseWriter )stateInfo.getAttribute( key );
stateInfo.setResponseWriter( writer );
}
private static String getLibraries() throws IOException {
fakeWriter();
try {
DisplayUtil.writeLibraries();
return getContent( ContextProvider.getStateInfo().getResponseWriter() );
} finally {
restoreWriter();
}
}
private static String getContent( final HtmlResponseWriter writer ) {
StringBuffer msg = new StringBuffer();
for( int i = 0; i < writer.getBodySize(); i ++ ) {
msg.append( writer.getBodyToken( i ) );
}
return msg.toString();
}
//////////////////////////
// Branding helper methods
private static void applyBranding() throws IOException {
AbstractBranding branding = BrandingUtil.findBranding();
registerBrandingResources( branding );
HttpServletRequest request = ContextProvider.getRequest();
// TODO: [bm][rh] move into util
String entryPoint = request.getParameter( RequestParams.STARTUP );
if( entryPoint == null ) {
entryPoint = branding.getDefaultEntryPoint();
if( entryPoint == null || "".equals( entryPoint ) ) {
entryPoint = EntryPointManager.DEFAULT;
}
}
if( branding.getThemeId() != null ) {
ThemeUtil.setCurrentThemeId( branding.getThemeId() );
}
BrandingUtil.replacePlaceholder( template,
StartupPageTemplateHolder.VAR_BODY,
branding.getBody() );
BrandingUtil.replacePlaceholder( template,
StartupPageTemplateHolder.VAR_TITLE,
branding.getTitle() );
String headers = BrandingUtil.headerMarkup( branding );
BrandingUtil.replacePlaceholder( template,
StartupPageTemplateHolder.VAR_HEADERS,
headers );
String encodedEntryPoint = EncodingUtil.encodeHTMLEntities( entryPoint );
BrandingUtil.replacePlaceholder( template,
StartupPageTemplateHolder.VAR_STARTUP,
encodedEntryPoint );
String noScriptWarning = RWTMessages.getMessage( "RWT_NoScriptWarning" );
BrandingUtil.replacePlaceholder( template,
StartupPageTemplateHolder.VAR_NO_SCRIPT_MESSAGE,
noScriptWarning );
}
private static void registerBrandingResources(
final AbstractBranding branding )
throws IOException
{
synchronized( registeredBrandings ) {
if( !registeredBrandings.contains( branding ) ) {
branding.registerResources();
registeredBrandings.add( branding );
}
}
}
}