001 /**
002 * Copyright (C) 2009-2011 the original author or authors.
003 * See the notice.md file distributed with this work for additional
004 * information regarding copyright ownership.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package org.fusesource.restygwt.client.dispatcher;
020
021 import java.util.logging.Logger;
022
023 import org.fusesource.restygwt.client.Dispatcher;
024 import org.fusesource.restygwt.client.Method;
025 import org.fusesource.restygwt.client.cache.CacheKey;
026 import org.fusesource.restygwt.client.cache.QueueableCacheStorage;
027 import org.fusesource.restygwt.client.cache.ComplexCacheKey;
028 import org.fusesource.restygwt.client.cache.UrlCacheKey;
029 import org.fusesource.restygwt.client.callback.CallbackFactory;
030 import org.fusesource.restygwt.client.callback.FilterawareRequestCallback;
031
032 import com.google.gwt.core.client.Scheduler;
033 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
034 import com.google.gwt.http.client.RequestBuilder;
035 import com.google.gwt.http.client.RequestCallback;
036 import com.google.gwt.http.client.Response;
037 import com.google.gwt.logging.client.LogConfiguration;
038
039 public class CachingDispatcherFilter implements DispatcherFilter {
040
041 /**
042 * one instance of {@link QueueableCacheStorage}
043 */
044 private QueueableCacheStorage cacheStorage;
045
046 /**
047 * where to get a callback from. gives us the ability to use
048 * customized {@link FilterawareRequestCallback}
049 */
050 private CallbackFactory callbackFactory;
051
052 /**
053 * the one and only constructor
054 * @param cacheStorage
055 * @param cf
056 */
057 public CachingDispatcherFilter(final QueueableCacheStorage cacheStorage,
058 final CallbackFactory cf) {
059 this.cacheStorage = cacheStorage;
060 this.callbackFactory = cf;
061 }
062
063 protected CacheKey cacheKey(RequestBuilder builder) {
064 if (RequestBuilder.GET.toString().equalsIgnoreCase(
065 builder.getHTTPMethod())) {
066 return new ComplexCacheKey(builder);
067 } else {
068 return null;
069 }
070 }
071
072 /**
073 * main filter method for a dispatcherfilter.
074 *
075 * @return continue filtering or not
076 */
077 public boolean filter(final Method method, final RequestBuilder builder) {
078 final CacheKey cacheKey = cacheKey(builder);
079
080 if (cacheKey != null) {
081 final Response cachedResponse = cacheStorage.getResultOrReturnNull(cacheKey);
082 if (cachedResponse != null) {
083 //case 1: we got a result in cache => return it...
084 if (LogConfiguration.loggingIsEnabled()) {
085 Logger.getLogger(Dispatcher.class.getName())
086 .info("already got a cached response for: " + builder.getHTTPMethod() + " "
087 + builder.getUrl());
088 }
089 // onResponseReceived can be time consuming and can manipulate the DOM
090 // deferring the command keeps the async behaviour of this method call
091 Scheduler.get().scheduleDeferred(new ScheduledCommand() {
092
093 @Override
094 public void execute() {
095 builder.getCallback().onResponseReceived(null, cachedResponse);
096 }
097 });
098 return false;
099 } else {
100 final RequestCallback callback = callbackFactory.createCallback(method);
101
102 //case 2: => no cache in result => queue it....
103 if (!cacheStorage.hasCallback(cacheKey)) {
104 //case 2.1 => first callback => make a new one and execute...
105 cacheStorage.addCallback(cacheKey, builder.getCallback());
106
107 if (LogConfiguration.loggingIsEnabled()) {
108 Logger.getLogger(Dispatcher.class.getName())
109 .info("Sending *caching* http request: " + builder.getHTTPMethod() + " "
110 + builder.getUrl());
111 }
112
113 // important part:
114 builder.setCallback(callback);
115 return true;
116 } else {
117 //case 2.2 => a callback already in progress => queue to get response when back
118 if (LogConfiguration.loggingIsEnabled()) {
119 Logger.getLogger(Dispatcher.class.getName())
120 .info("request in progress, queue callback: " + builder.getHTTPMethod() + " "
121 + builder.getUrl());
122 }
123 cacheStorage.addCallback(cacheKey, callback);
124 return false;
125 }
126 }
127 } else {
128 // non cachable case
129 if (LogConfiguration.loggingIsEnabled()) {
130 String content = builder.getRequestData();
131 Logger.getLogger(Dispatcher.class.getName())
132 .info("Sending *non-caching* http request: " + builder.getHTTPMethod() + " "
133 + builder.getUrl() + " (Content: `" + content + "ยด)");
134 }
135
136 // /*
137 // * add X-Request-Token to all non-caching calls (!= GET) if we have some
138 // */
139 // builder.setHeader("X-Testing", "Bude");
140
141 builder.setCallback(callbackFactory.createCallback(method));
142 return true;
143 }
144 }
145 }