/*==========================================================================*\
| $Id: WCResourceManager.java,v 1.1 2011/10/25 12:51:37 stedwar2 Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2006-2011 Virginia Tech
|
| This file is part of Web-CAT.
|
| Web-CAT is free software; you can redistribute it and/or modify
| it under the terms of the GNU Affero General Public License as published
| by the Free Software Foundation; either version 3 of the License, or
| (at your option) any later version.
|
| Web-CAT 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 General Public License for more details.
|
| You should have received a copy of the GNU Affero General Public License
| along with Web-CAT; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package org.webcat.woextensions;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Map;
import com.webobjects.appserver.*;
import com.webobjects.foundation.*;
import er.extensions.appserver.ERXResourceManager;
import er.extensions.foundation.ERXMutableURL;
import org.webcat.core.Application;
import org.webcat.core.Subsystem;
import org.webcat.core.SubsystemManager;
import org.apache.log4j.Logger;
// -------------------------------------------------------------------------
/**
* A drop-in replacement for {@link WOResourceManager} that fixes a bug
* in WODeploymentBundle's urlForResource() method. Unfortunately,
* WODeploymentBundle "standardizes" the URL the same way it would a
* local path name, and accidentally converts the double-slash at the
* start of an absolute URL to just a single slash, thereby changing
* its meaning. This subclass looks for and restores such broken URLs,
* allowing absolute URLs to be used in a deployed application's
* frameworksBaseURL() setting.
*
* @author Stephen Edwards
* @author Latest changes by: $Author: stedwar2 $
* @version $Revision: 1.1 $, $Date: 2011/10/25 12:51:37 $
*/
public class WCResourceManager
extends ERXResourceManager
{
//~ Constructors ..........................................................
// ----------------------------------------------------------
/**
* Default constructor.
*/
public WCResourceManager()
{
super();
// This provides support for resource-manager-based URL generation
// for HTML resources during development mode, even when no
// request parameter is provided. During deployment, this is
// always disabled, since the frameworks base URL is always set
// to something other than the default in that case.
wantExemplar = Application.application().frameworksBaseURL().equals(
"/WebObjects/Frameworks");
}
//~ Public Methods ........................................................
// ----------------------------------------------------------
@SuppressWarnings("unchecked")
public String urlForResourceNamed(
String aResourceName,
String aFrameworkName,
NSArray aLanguageList,
WORequest aRequest)
{
if (wantExemplar && exemplarRequest == null && aRequest != null)
{
exemplarRequest = aRequest;
log.debug("exemplar = " + exemplarRequest);
log.debug("frameworks base = "
+ Application.application().frameworksBaseURL());
}
if (aFrameworkName == null)
{
int pos = aResourceName.indexOf(FRAMEWORK_SUFFIX);
if (pos >= 0)
{
aFrameworkName = aResourceName.substring(0, pos);
aResourceName = aResourceName.substring(
pos + FRAMEWORK_SUFFIX.length());
}
}
return super.urlForResourceNamed(
aResourceName, aFrameworkName, aLanguageList, aRequest);
}
// ----------------------------------------------------------
public static String resourceURLFor(
String aResourceName,
String aFrameworkName,
NSArray<?> aLanguageList,
WORequest aRequest)
{
if (aRequest == null)
{
aRequest = exemplarRequest;
}
return ((WCResourceManager)Application.application()
.resourceManager()).urlForResourceNamed(
aResourceName, aFrameworkName, aLanguageList, aRequest);
}
// ----------------------------------------------------------
public static String versionlessResourceURLFor(
String aResourceName,
String aFrameworkName,
NSArray<?> aLanguageList,
WORequest aRequest)
{
String result = resourceURLFor(
aResourceName, aFrameworkName, aLanguageList, aRequest);
if (result != null)
{
int pos = result.lastIndexOf('?');
if (pos >= 0)
{
result = result.substring(0, pos);
}
}
return result;
}
// ----------------------------------------------------------
public static String resourceURLFor(
String aResourceName, WORequest aRequest)
{
if (aRequest == null)
{
aRequest = exemplarRequest;
}
return ((WCResourceManager)Application.application()
.resourceManager()).urlForResourceNamed(
aResourceName, null, null, aRequest);
}
// ----------------------------------------------------------
/**
* Controls resource version numbers via version build datestamps
* stored for each subsystem, and appends the query parameter
* "?xxx" to WebServerResource URLs.
*/
public static class WCVersionManager
implements IVersionManager
{
// ----------------------------------------------------------
/**
* Returns the variant of the given resource URL adjusted to include
* version information.
* @param resourceUrl
* the original resource URL
* @param name
* the name of the resource being loaded
* @param bundleName
* the name of the bundle that contains the resource
* @param languages
* the languages requested
* @param request
* the request
* @return a versioned variant of the resourceUrl
*/
@SuppressWarnings("unchecked")
public String versionedUrlForResourceNamed(
String resourceUrl,
String name,
String bundleName,
NSArray languages,
WORequest request)
{
resourceUrl = standardizeURL(resourceUrl);
String version = versionFor(bundleName);
if (version != null)
{
try
{
ERXMutableURL url = new ERXMutableURL(resourceUrl);
url.addQueryParameter("", version);
resourceUrl = url.toExternalForm();
}
catch (MalformedURLException e)
{
log.error("Failed to construct URL from '"
+ resourceUrl + "'.", e);
}
}
return resourceUrl;
}
// ----------------------------------------------------------
private String defaultVersion()
{
if (defaultVersion == null)
{
defaultVersion = Application.wcApplication().version();
int pos = defaultVersion.lastIndexOf('.');
if (pos >= 0)
{
defaultVersion = defaultVersion.substring(pos + 1);
}
}
return defaultVersion;
}
// ----------------------------------------------------------
private String versionFor(String bundleName)
{
if (bundleName == null
|| Application.wcApplication().needsInstallation())
{
return defaultVersion();
}
String version = bundleVersion.get(bundleName);
if (version == null)
{
SubsystemManager manager = Application.wcApplication()
.subsystemManager();
Subsystem bundle = manager.subsystem(bundleName);
if (bundle == null)
{
// Could be a framework that is nested inside a subsystem
for (Subsystem sub : manager.subsystems())
{
// First, cache the version for this subsystem, if
// we haven't already
if (bundleVersion.get(sub.name()) == null)
{
bundleVersion.put(
sub.name(), sub.descriptor().versionDate());
}
// Now check for dependencies
String alsoContains = sub.descriptor()
.getProperty("alsoContains");
if (alsoContains != null)
{
alsoContains =
alsoContains.replace(".framework", "");
String[] contains = alsoContains.split(",\\s*");
for (String subsub : contains)
{
bundleVersion.put(
subsub, sub.descriptor().versionDate());
if (bundleName.equals(subsub))
{
bundle = sub;
}
}
}
if (bundle != null)
{
break;
}
}
}
if (bundle != null)
{
version = bundle.descriptor().versionDate();
bundleVersion.put(bundleName, version);
}
}
if (version == null)
{
version = defaultVersion();
bundleVersion.put(bundleName, version);
}
return version;
}
//~ Instance/static variables .........................................
private String defaultVersion;
private Map<String, String> bundleVersion =
new HashMap<String, String>();
}
//~ Private Methods .......................................................
// ----------------------------------------------------------
private static String standardizeURL(String url)
{
String result = url;
int pos = result.indexOf(':');
if (pos > 0
&& pos < result.length() - 2
&& result.charAt( pos + 1 ) == '/'
&& result.charAt( pos + 2 ) != '/')
{
result = result.substring(0, pos + 1) + "/"
+ result.substring(pos + 1);
}
return result.replaceAll("[/]([A-Za-z])%3A","$1:")
.replaceAll("[+][%]26[+]", " & ");
}
//~ Instance/static variables .............................................
private static final String FRAMEWORK_SUFFIX =
".framework/WebServerResources/";
private static WORequest exemplarRequest;
private static boolean wantExemplar = false;
static Logger log = Logger.getLogger(WCResourceManager.class);
}