use of com.webobjects.appserver.WOContext in project wonder-slim by undur.
the class JSONRequestHandler method handleRequest.
@SuppressWarnings("unchecked")
@Override
public WOResponse handleRequest(WORequest request) {
WOApplication application = WOApplication.application();
application.awake();
try {
WOContext context = application.createContextForRequest(request);
WOResponse response = application.createResponseInContext(context);
Object output;
try {
String inputString = request.contentString();
JSONObject input = new JSONObject(inputString);
String sessionIdKey = WOApplication.application().sessionIdKey();
String sessionId = request.cookieValueForKey(sessionIdKey);
if (sessionId == null) {
ERXMutableURL url = new ERXMutableURL();
url.setQueryParameters(request.queryString());
sessionId = url.queryParameter(sessionIdKey);
if (sessionId == null && input.has(sessionIdKey)) {
sessionId = input.getString(sessionIdKey);
}
}
context._setRequestSessionID(sessionId);
WOSession session = null;
if (context._requestSessionID() != null) {
session = WOApplication.application().restoreSessionWithID(sessionId, context);
}
if (session != null) {
session.awake();
}
try {
JSONComponentCallback componentCallback = null;
WODynamicURL url = request._uriDecomposed();
String requestHandlerPath = url.requestHandlerPath();
JSONRPCBridge jsonBridge;
if (requestHandlerPath != null && requestHandlerPath.length() > 0) {
String componentNameAndInstance = requestHandlerPath;
String componentInstance;
String componentName;
int slashIndex = componentNameAndInstance.indexOf('/');
if (slashIndex == -1) {
componentName = componentNameAndInstance;
componentInstance = null;
} else {
componentName = componentNameAndInstance.substring(0, slashIndex);
componentInstance = componentNameAndInstance.substring(slashIndex + 1);
}
if (session == null) {
session = context.session();
}
String bridgesKey = (componentInstance == null) ? "_JSONGlobalBridges" : "_JSONInstanceBridges";
Map<String, JSONRPCBridge> componentBridges = (Map<String, JSONRPCBridge>) session.objectForKey(bridgesKey);
if (componentBridges == null) {
int limit = ERXProperties.intForKeyWithDefault((componentInstance == null) ? "er.ajax.json.globalBacktrackCacheSize" : "er.ajax.json.backtrackCacheSize", WOApplication.application().pageCacheSize());
componentBridges = new LRUMap<>(limit);
session.setObjectForKey(componentBridges, bridgesKey);
}
jsonBridge = componentBridges.get(componentNameAndInstance);
if (jsonBridge == null) {
Class componentClass = _NSUtilities.classWithName(componentName);
JSONComponent component;
if (JSONComponent.class.isAssignableFrom(componentClass)) {
component = (JSONComponent) _NSUtilities.instantiateObject(componentClass, new Class[] { WOContext.class }, new Object[] { context }, true, false);
} else {
throw new SecurityException("There is no JSON component named '" + componentName + "'.");
}
jsonBridge = createBridgeForComponent(component, componentName, componentInstance, componentBridges);
}
componentCallback = new JSONComponentCallback(context);
jsonBridge.registerCallback(componentCallback, WOContext.class);
} else {
jsonBridge = _sharedBridge;
}
try {
output = jsonBridge.call(new Object[] { request, response, context }, input);
} finally {
if (componentCallback != null) {
jsonBridge.unregisterCallback(componentCallback, WOContext.class);
}
}
if (context._session() != null) {
WOSession contextSession = context._session();
// If this is a new session, then we have to force it to be a cookie session
if (sessionId == null) {
boolean storesIDsInCookies = contextSession.storesIDsInCookies();
try {
contextSession.setStoresIDsInCookies(true);
contextSession._appendCookieToResponse(response);
} finally {
contextSession.setStoresIDsInCookies(storesIDsInCookies);
}
} else {
contextSession._appendCookieToResponse(response);
}
}
response.appendContentString(output.toString());
response._finalizeInContext(context);
response.disableClientCaching();
} finally {
try {
if (session != null) {
session.sleep();
}
} finally {
if (context._session() != null) {
WOApplication.application().saveSessionForContext(context);
}
}
}
} catch (NoSuchElementException e) {
e.printStackTrace();
output = new JSONRPCResult(JSONRPCResult.CODE_ERR_NOMETHOD, null, JSONRPCResult.MSG_ERR_NOMETHOD);
} catch (JSONException e) {
e.printStackTrace();
output = new JSONRPCResult(JSONRPCResult.CODE_ERR_PARSE, null, JSONRPCResult.MSG_ERR_PARSE);
} catch (Throwable t) {
t.printStackTrace();
output = new JSONRPCResult(JSONRPCResult.CODE_ERR_PARSE, null, t.getMessage());
}
return response;
} finally {
application.sleep();
}
}
use of com.webobjects.appserver.WOContext in project wonder-slim by undur.
the class ERXResourceManager method urlForResourceNamed.
@Override
public String urlForResourceNamed(String name, String bundleName, NSArray<String> languages, WORequest request) {
String completeURL = null;
if (request == null || request.isUsingWebServer() && !WOApplication.application()._rapidTurnaroundActiveForAnyProject()) {
completeURL = _cachedURLForResource(name, bundleName, languages, request);
} else {
URL url = pathURLForResourceNamed(name, bundleName, languages);
String fileURL = null;
if (url == null) {
fileURL = "ERROR_NOT_FOUND_framework_" + (bundleName == null ? "*null*" : bundleName) + "_filename_" + (name == null ? "*null*" : name);
} else {
fileURL = url.toString();
cacheDataIfNotInCache(fileURL);
}
String encoded = WOURLEncoder.encode(fileURL);
String key = WOApplication.application().resourceRequestHandlerKey();
if (WOApplication.application()._rapidTurnaroundActiveForAnyProject() && WOApplication.application().isDirectConnectEnabled()) {
key = "_wr_";
}
WOContext context = (WOContext) request.valueForKey("context");
String wodata = _NSStringUtilities.concat("wodata", "=", encoded);
if (context != null) {
completeURL = context.urlWithRequestHandlerKey(key, null, wodata);
} else {
StringBuilder sb = new StringBuilder(request.applicationURLPrefix());
sb.append('/');
sb.append(key);
sb.append('?');
sb.append(wodata);
completeURL = sb.toString();
}
// AK: TODO get rid of regex
int offset = completeURL.indexOf("?wodata=file%3A");
if (offset >= 0) {
completeURL = completeURL.replaceFirst("\\?wodata=file%3A", "/wodata=");
if (completeURL.indexOf("/wodata=") > 0) {
completeURL = completeURL.replaceAll("%2F", "/");
// SWK: On Windows we have /C%3A/ changed to /C:
completeURL = completeURL.replaceAll("%3A", ":");
}
}
}
completeURL = _versionManager.versionedUrlForResourceNamed(completeURL, name, bundleName, languages, request);
completeURL = _postprocessURL(completeURL, bundleName);
return completeURL;
}
use of com.webobjects.appserver.WOContext in project wonder-slim by undur.
the class ERXAjaxSession method savePage.
/**
* Overridden so that Ajax requests are not saved in the page cache. Checks both the
* response userInfo and the response headers if the DONT_STORE_PAGE key is present. The value doesn't matter.
* <p>
* Page Replacement cache is specifically designed to support component actions in Ajax updates. The problem with
* component actions in Ajax is that if you let them use the normal page cache, then after only 30 (or whatever your backtrack
* cache is set to) updates from Ajax, you will fill your backtrack cache. Unfortunately for the user, though, the backtrack cache
* filled up with background ajax requests, so when the user clicks on a component action on the FOREGROUND page, the
* foreground page has fallen out of the cache, and the request cannot be fulfilled (because its context is gone). If you simply
* turn off backtrack cache entirely for a request, then you can't have component actions inside of an Ajax updated area, because
* the context of the Ajax update that generated the link will never get stored, and so you will ALWAYS get a backtrack error.
* <p>
* Enter page replacement cache. If you look at the behavior of Ajax, it turns out that what you REALLY want is a hybrid page cache. You
* want to keep the backtrack of just the LAST update for a particular ajax component -- you don't care about its previous 29 states
* because the user can't use the back button to get to them anyway, but if you have the MOST RECENT cached version of the page
* then you can click on links in Ajax updated areas. Page Replacement cache implements this logic. For each Ajax component on
* your page that is updating, it keeps a cache entry of its most recent backtrack state (note the difference between this and the
* normal page cache. The normal page cache contains one entry per user-backtrackable-request. The replacement cache contains
* one entry per Ajax component*, allowing up to replacement_page_cache_size many components per page). Each time the Ajax area
* refreshes, the most recent state is replaced*. When a restorePage request comes in, the replacement cache is checked first. If
* the replacement cache can service the page, then it does so. If the replacement cache doesn't contain the context, then it
* passes up to the standard page cache. If you are not using Ajax, no replacement cache will exist in your session, and all the code
* related to it will be skipped, so it should be minimally invasive under those conditions.
* <p>
* <b>*</b> It turns out that we have to keep the last TWO states, because of a race condition in the scenario where the replacement page
* cache replaces context 2 with the context 3 update, but the user's browser hasn't been updated yet with the HTML from
* context 3. When the user clicks, they are clicking the context 2 link, which has now been removed from the replacement cache.
* By keeping the last two states, you allow for the brief period where that transition occurs.
* <p>
* Random note (that I will find useful in 2 weeks when I forget this again): The first time through savePage, the request is saved
* in the main cache. It's only on a subsequent Ajax update that it uses page replacement cache. So even though the cache
* is keyed off of context ID, the explanation of the cache being components-per-page-sized works out because each component
* is requesting in its own thread and generating their own non-overlapping context ids.
*/
@Override
public void savePage(WOComponent page) {
WOContext context = context();
if (ERXAjaxApplication.shouldNotStorePage(context)) {
if (log.isDebugEnabled())
log.debug("Considering pageReplacementCache for {} with contextID {}", context.request().uri(), context.contextID());
WORequest request = context.request();
WOResponse response = context.response();
String pageCacheKey = null;
if (response != null) {
pageCacheKey = response.headerForKey(ERXAjaxSession.PAGE_REPLACEMENT_CACHE_LOOKUP_KEY);
}
if (pageCacheKey == null && request != null) {
pageCacheKey = request.headerForKey(ERXAjaxSession.PAGE_REPLACEMENT_CACHE_LOOKUP_KEY);
}
// A null pageCacheKey should mean an Ajax request that is not returning a content update or an expliclty not cached non-Ajax request
if (pageCacheKey != null) {
log.debug("Will use pageCacheKey {}", pageCacheKey);
String originalContextID = context.request().headerForKey(ERXAjaxSession.ORIGINAL_CONTEXT_ID_KEY);
pageCacheKey = originalContextID + "_" + pageCacheKey;
LinkedHashMap pageReplacementCache = (LinkedHashMap) objectForKey(ERXAjaxSession.PAGE_REPLACEMENT_CACHE_KEY);
if (pageReplacementCache == null) {
pageReplacementCache = new LinkedHashMap();
setObjectForKey(pageReplacementCache, ERXAjaxSession.PAGE_REPLACEMENT_CACHE_KEY);
}
// Remove the oldest entry if we're about to add a new one and that would put us over the cache size ...
// We do a CACHE_SIZE*2 here because for every page, we have to potentially store its previous contextid to prevent
// race conditions, so there technically can be 2x cache size many pages in the cache.
boolean removedCacheEntry = cleanPageReplacementCacheIfNecessary(pageCacheKey);
if (!removedCacheEntry && pageReplacementCache.size() >= ERXAjaxSession.MAX_PAGE_REPLACEMENT_CACHE_SIZE * 2) {
Iterator entryIterator = pageReplacementCache.entrySet().iterator();
Map.Entry oldestEntry = (Map.Entry) entryIterator.next();
entryIterator.remove();
if (log.isDebugEnabled())
log.debug("{} pageReplacementCache too large, removing oldest entry = {}", pageCacheKey, ((TransactionRecord) oldestEntry.getValue()).key());
}
TransactionRecord pageRecord = new TransactionRecord(page, context, pageCacheKey);
pageReplacementCache.put(context.contextID(), pageRecord);
log.debug("{} new context = {}", pageCacheKey, context.contextID());
log.debug("{} = {}", pageCacheKey, pageReplacementCache.keySet());
ERXAjaxApplication.cleanUpHeaders(response);
} else {
// A null pageCacheKey should mean an Ajax request that is not returning a content update or an explicitly not cached non-Ajax request
log.debug("Not caching as no pageCacheKey found");
}
} else {
log.debug("Calling super.savePage for contextID {}", context.contextID());
super.savePage(page);
}
}
use of com.webobjects.appserver.WOContext in project wonder-slim by undur.
the class ERXWORepetition method appendToResponse.
@Override
public void appendToResponse(WOResponse woresponse, WOContext wocontext) {
WOComponent wocomponent = wocontext.component();
Context context = createContext(wocomponent);
int count = _count(context, wocomponent);
boolean checkHashCodes = checkHashCodes(wocomponent);
log.debug("appendToResponse: {}", wocontext.elementID());
for (int index = 0; index < count; index++) {
_prepareForIterationWithIndex(context, index, wocontext, wocomponent, checkHashCodes);
appendChildrenToResponse(woresponse, wocontext);
}
if (count > 0) {
_cleanupAfterIteration(count, wocontext, wocomponent);
}
}
use of com.webobjects.appserver.WOContext in project wonder-slim by undur.
the class ERXWORepetition method takeValuesFromRequest.
@Override
public void takeValuesFromRequest(WORequest worequest, WOContext wocontext) {
WOComponent wocomponent = wocontext.component();
Context context = createContext(wocomponent);
int count = _count(context, wocomponent);
boolean checkHashCodes = checkHashCodes(wocomponent);
if (log.isDebugEnabled()) {
log.debug("takeValuesFromRequest: {} - {}", wocontext.elementID(), wocontext.request().formValueKeys());
}
for (int index = 0; index < count; index++) {
_prepareForIterationWithIndex(context, index, wocontext, wocomponent, checkHashCodes);
super.takeValuesFromRequest(worequest, wocontext);
}
if (count > 0) {
_cleanupAfterIteration(count, wocontext, wocomponent);
}
}
Aggregations