/* * Copyright (c) 2010-2011 Lockheed Martin Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.eurekastreams.web.client.model; import java.io.Serializable; import java.util.HashMap; import org.eurekastreams.commons.exceptions.ExecutionException; import org.eurekastreams.commons.exceptions.GeneralException; import org.eurekastreams.commons.exceptions.ValidationException; import org.eurekastreams.web.client.events.ExceptionResponseEvent; import org.eurekastreams.web.client.events.ShowNotificationEvent; import org.eurekastreams.web.client.events.data.ValidationExceptionResponseEvent; import org.eurekastreams.web.client.ui.Session; import org.eurekastreams.web.client.ui.common.notifier.Notification; import com.google.gwt.user.client.rpc.AsyncCallback; /** * Base model. Gets you free CRUD. */ public abstract class BaseModel { /** * Cached requests objects, by action key. */ private final HashMap<String, Serializable> cachedRequests = new HashMap<String, Serializable>(); /** * Cached response objects, by action key. */ private final HashMap<String, Serializable> cachedData = new HashMap<String, Serializable>(); /** * onSuccess command. * * @param <T> * response type. */ public interface OnSuccessCommand<T extends Serializable> { /** * onSuccess. * * @param response * response. */ void onSuccess(T response); } /** * Command to execute on failure. */ public interface OnFailureCommand { /** * Action to perform on failure. * * @param ex * Exception recieved. */ void onFailure(Throwable ex); } /** * Call read action. * * @param actionKey * the action. * @param request * the request. * @param successCommand * the success command. * @param useClientCacheIfAvailable * use the cache. */ protected void callReadAction(final String actionKey, final Serializable request, final OnSuccessCommand successCommand, final boolean useClientCacheIfAvailable) { callReadAction(actionKey, request, successCommand, null, useClientCacheIfAvailable); } /** * Call read action. * * @param actionKey * the action. * @param request * the request. * @param successCommand * the success command. * @param failureCommand * Executed on failure. * @param useClientCacheIfAvailable * use the cache. */ protected void callReadAction(final String actionKey, final Serializable request, final OnSuccessCommand successCommand, final OnFailureCommand failureCommand, final boolean useClientCacheIfAvailable) { if (useClientCacheIfAvailable) { Serializable cachedResponse = cachedData.get(actionKey); if (cachedResponse != null && areRequestsEqual(cachedRequests.get(actionKey), request)) { successCommand.onSuccess(cachedResponse); return; } } cachedRequests.put(actionKey, request); callAction(actionKey, request, successCommand, failureCommand, useClientCacheIfAvailable); } /** * Synchronously returns the cached value for a given request (if available). * * @param actionKey * the name of the action. * @param request * the request. * @param <T> * Type of the response. * @return The cached response if available, else null. */ protected <T extends Serializable> T getCachedResponse(final String actionKey, final Serializable request) { T cachedResponse = (T) cachedData.get(actionKey); return (cachedResponse != null && areRequestsEqual(cachedRequests.get(actionKey), request)) ? cachedResponse : null; } /** * Call write action. * * @param actionKey * the action key. * @param request * the request. * @param successCommand * successcommand. */ protected void callWriteAction(final String actionKey, final Serializable request, final OnSuccessCommand successCommand) { callWriteAction(actionKey, request, successCommand, null); } /** * Call write action. * * @param actionKey * the action key. * @param request * the request. * @param successCommand * successcommand. * @param failureCommand * Executed on failure. */ protected void callWriteAction(final String actionKey, final Serializable request, final OnSuccessCommand successCommand, final OnFailureCommand failureCommand) { cachedData.clear(); callAction(actionKey, request, successCommand, failureCommand, false); } /** * Call an action. * * @param actionKey * action key. * @param request * request. * @param successCommand * on successs command. * @param failureCommand * Executed on failure. * @param useClientCacheIfAvailable * use the cache. */ private void callAction(final String actionKey, final Serializable request, final OnSuccessCommand successCommand, final OnFailureCommand failureCommand, final boolean useClientCacheIfAvailable) { final BaseModel thisBuffered = this; Session.getInstance().getActionProcessor().makeRequest(actionKey, request, new AsyncCallback<Serializable>() { public void onFailure(final Throwable caught) { cachedRequests.remove(actionKey); cachedData.remove(actionKey); if (failureCommand != null) { failureCommand.onFailure(caught); } else if (caught instanceof ValidationException) { Session.getInstance().getEventBus() .notifyObservers(new ValidationExceptionResponseEvent((ValidationException) caught)); } else if (caught instanceof ExecutionException || caught instanceof GeneralException) { Session.getInstance() .getEventBus() .notifyObservers( new ShowNotificationEvent(new Notification( "Error occurred, please refresh and try again."))); Session.getInstance().getEventBus() .notifyObservers(new ExceptionResponseEvent(caught, thisBuffered, request)); } } public void onSuccess(final Serializable result) { cachedData.put(actionKey, result); if (successCommand != null) { successCommand.onSuccess(result); } } }); } /** * Calls the native comparer. * * @param request1 * the request. * @param request2 * the request. * @return whether this request is equal to the last fetch request. */ private boolean areRequestsEqual(final Serializable request1, final Serializable request2) { return nativeCompareRequest(request1, request2); } /** * Clear cache for this model. */ public void clearCache() { cachedData.clear(); } /** * Native comparer of objects. Dropping down to javascript here because I don't have reflection available. * * @param request1 * first request. * @param request2 * second request. * @return isEqual. */ public static native boolean nativeCompareRequest(final Serializable request1, final Serializable request2) /*-{ if (!$wnd.es_compareRequests) { $wnd.es_compareRequests = function(r1,r2) { if (typeof r1 == 'object' && typeof r2 == 'object') { for(var field in r2) { if (!$wnd.es_compareRequests(r1[field],r2[field])) return false; } return true; } else return r1 == r2; } } return $wnd.es_compareRequests(request1, request2); }-*/; }