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    }