You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rave.apache.org by ja...@apache.org on 2011/11/04 12:19:31 UTC

svn commit: r1197509 - in /incubator/rave/trunk: rave-components/rave-core/src/main/java/org/apache/rave/portal/service/ rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/ rave-components/rave-core/src/test/java/org/apache/rav...

Author: jasha
Date: Fri Nov  4 11:19:31 2011
New Revision: 1197509

URL: http://svn.apache.org/viewvc?rev=1197509&view=rev
Log:
RAVE-332 move check if widget already exists to the validator and throw an exception if the WidgetService#registerNewWidget is called for an existing URL instead of returning null.

Added:
    incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/UpdateWidgetValidator.java
    incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/WidgetValidator.java
    incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/UpdateWidgetValidatorTest.java
      - copied, changed from r1197469, incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/NewWidgetValidatorTest.java
    incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/WidgetValidatorTest.java
Modified:
    incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/WidgetService.java
    incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultWidgetService.java
    incubator/rave/trunk/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultWidgetServiceTest.java
    incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/WidgetStoreController.java
    incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/admin/WidgetController.java
    incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewWidgetValidator.java
    incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/WidgetStoreControllerTest.java
    incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/admin/WidgetControllerTest.java
    incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/NewWidgetValidatorTest.java
    incubator/rave/trunk/rave-portal-resources/src/main/resources/messages.properties
    incubator/rave/trunk/rave-portal-resources/src/main/resources/messages_nl.properties

Modified: incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/WidgetService.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/WidgetService.java?rev=1197509&r1=1197508&r2=1197509&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/WidgetService.java (original)
+++ incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/WidgetService.java Fri Nov  4 11:19:31 2011
@@ -22,11 +22,11 @@ package org.apache.rave.portal.service;
 import org.apache.rave.portal.model.Widget;
 import org.apache.rave.portal.model.util.SearchResult;
 import org.apache.rave.portal.model.util.WidgetStatistics;
-
-import java.util.Map;
 import org.springframework.security.access.prepost.PostAuthorize;
 import org.springframework.security.access.prepost.PreAuthorize;
 
+import java.util.Map;
+
 /**
  * Provides widget operations
  */
@@ -113,12 +113,19 @@ public interface WidgetService {
     @PostAuthorize("hasPermission(returnObject, 'read')")
     Widget getWidgetByUrl(String widgetUrl);
 
+    /**
+     * Checks if there is already a {@link Widget} for the given url. Does not need authorization.
+     *
+     * @param widgetUrl url of a widget definition
+     * @return {@literal true} if it exists, otherwise {@literal false}
+     */
+    boolean isRegisteredUrl(String widgetUrl);
 
     /**
      * Persists a new {@link Widget} if it is not already present in the store
      *
      * @param widget new Widget to store
-     * @return Widget if it is new and can be stored, otherwise {@literal null}
+     * @return Widget if it is new and can be stored
      */        
     @PostAuthorize("hasPermission(returnObject, 'create')") 
     Widget registerNewWidget(Widget widget);

Modified: incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultWidgetService.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultWidgetService.java?rev=1197509&r1=1197508&r2=1197509&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultWidgetService.java (original)
+++ incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultWidgetService.java Fri Nov  4 11:19:31 2011
@@ -20,6 +20,7 @@
 package org.apache.rave.portal.service.impl;
 
 import org.apache.commons.lang.StringUtils;
+import org.apache.rave.exception.DuplicateItemException;
 import org.apache.rave.portal.model.Widget;
 import org.apache.rave.portal.model.WidgetStatus;
 import org.apache.rave.portal.model.util.SearchResult;
@@ -30,10 +31,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
 import java.util.Map;
-import org.springframework.transaction.annotation.Transactional;
 
 @Service
 public class DefaultWidgetService implements WidgetService {
@@ -123,12 +124,16 @@ public class DefaultWidgetService implem
     }
 
     @Override
+    public boolean isRegisteredUrl(String widgetUrl) {
+        return widgetRepository.getByUrl(widgetUrl) != null;
+    }
+
+    @Override
     @Transactional
     public Widget registerNewWidget(Widget widget) {
         if (getWidgetByUrl(widget.getUrl()) != null) {
-            logger.debug("Trying to add an existing widget for url {}", widget.getUrl());
-            return null;
-        }                      
+            throw new DuplicateItemException("Trying to add an existing widget for url " + widget.getUrl());
+        }
         return widgetRepository.save(widget);
     }
 

Modified: incubator/rave/trunk/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultWidgetServiceTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultWidgetServiceTest.java?rev=1197509&r1=1197508&r2=1197509&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultWidgetServiceTest.java (original)
+++ incubator/rave/trunk/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultWidgetServiceTest.java Fri Nov  4 11:19:31 2011
@@ -19,6 +19,7 @@
 
 package org.apache.rave.portal.service.impl;
 
+import org.apache.rave.exception.DuplicateItemException;
 import org.apache.rave.portal.model.Widget;
 import org.apache.rave.portal.model.WidgetStatus;
 import org.apache.rave.portal.model.util.SearchResult;
@@ -32,9 +33,19 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 
-import static org.easymock.EasyMock.*;
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 
 /**
  * Test for {@link DefaultWidgetService}
@@ -216,6 +227,34 @@ public class DefaultWidgetServiceTest {
     }
 
     @Test
+    public void isRegisteredWidget() {
+        final String widgetUrl =
+                "http://hosting.gmodules.com/ig/gadgets/file/112581010116074801021/hamster.xml";
+        Widget widget = new Widget();
+        widget.setUrl(widgetUrl);
+        expect(widgetRepository.getByUrl(widgetUrl)).andReturn(widget);
+        replay(widgetRepository);
+
+        boolean isExisting = widgetService.isRegisteredUrl(widgetUrl);
+        verify(widgetRepository);
+        assertTrue("Expecting existing widget for url " + widgetUrl, isExisting);
+    }
+
+    @Test
+    public void isNotRegisteredWidget_() {
+        final String widgetUrl =
+                "http://example.com/doesnotexistinrepository.xml";
+        Widget widget = new Widget();
+        widget.setUrl(widgetUrl);
+        expect(widgetRepository.getByUrl(widgetUrl)).andReturn(null);
+        replay(widgetRepository);
+
+        boolean isExisting = widgetService.isRegisteredUrl(widgetUrl);
+        verify(widgetRepository);
+        assertFalse("Not expecting widget for url " + widgetUrl, isExisting);
+    }
+
+    @Test
     public void registerNewWidget() {
         final String widgetUrl = "http://example.com/newwidget.xml";
         Widget widget = new Widget();
@@ -231,7 +270,7 @@ public class DefaultWidgetServiceTest {
         verify(widgetRepository);
     }
 
-    @Test
+    @Test(expected = DuplicateItemException.class)
     public void registerExistingWidgetAsNew() {
         final String widgetUrl =
                 "http://hosting.gmodules.com/ig/gadgets/file/112581010116074801021/hamster.xml";
@@ -240,9 +279,9 @@ public class DefaultWidgetServiceTest {
         expect(widgetRepository.getByUrl(widgetUrl)).andReturn(widget);
         replay(widgetRepository);
 
-        Widget noWidget = widgetService.registerNewWidget(widget);
-        assertNull("Widget already exists", noWidget);
+        widgetService.registerNewWidget(widget);
         verify(widgetRepository);
+        assertFalse("Expecting an exception", true);
     }
 
     @Test

Modified: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/WidgetStoreController.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/WidgetStoreController.java?rev=1197509&r1=1197508&r2=1197509&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/WidgetStoreController.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/WidgetStoreController.java Fri Nov  4 11:19:31 2011
@@ -150,16 +150,12 @@ public class WidgetStoreController {
             model.addAttribute(ModelKeys.WIDGET, widget);
             return ViewNames.ADD_WIDGET_FORM;
         }
+
         widget.setWidgetStatus(WidgetStatus.PREVIEW);
         widget.setOwner(user);
 
         final Widget storedWidget = widgetService.registerNewWidget(widget);
-        if (storedWidget == null) {
-            results.reject("page.addwidget.result.exists");
-            model.addAttribute(ModelKeys.WIDGET, widget);
-            return ViewNames.ADD_WIDGET_FORM;
-        }
-        
+
         model.addAttribute(ModelKeys.WIDGET, storedWidget);       
         model.addAttribute(ModelKeys.WIDGET_STATISTICS, widgetService.getWidgetStatistics(storedWidget.getEntityId(), user.getEntityId()));
         model.addAttribute(ModelKeys.USER_PROFILE, user);

Modified: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/admin/WidgetController.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/admin/WidgetController.java?rev=1197509&r1=1197508&r2=1197509&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/admin/WidgetController.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/admin/WidgetController.java Fri Nov  4 11:19:31 2011
@@ -25,7 +25,7 @@ import org.apache.rave.portal.model.util
 import org.apache.rave.portal.service.WidgetService;
 import org.apache.rave.portal.web.util.ModelKeys;
 import org.apache.rave.portal.web.util.ViewNames;
-import org.apache.rave.portal.web.validator.NewWidgetValidator;
+import org.apache.rave.portal.web.validator.UpdateWidgetValidator;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
@@ -57,7 +57,7 @@ public class WidgetController {
     private WidgetService widgetService;
 
     @Autowired
-    private NewWidgetValidator widgetValidator;
+    private UpdateWidgetValidator widgetValidator;
 
     @InitBinder
     public void initBinder(WebDataBinder dataBinder) {
@@ -94,7 +94,7 @@ public class WidgetController {
                 widgetstatus, offset, DEFAULT_PAGE_SIZE);
         model.addAttribute(ModelKeys.SEARCHRESULT, widgets);
         model.addAttribute(ModelKeys.SEARCH_TERM, searchTerm);
-        model.addAttribute("selectedWidgetType",widgettype);
+        model.addAttribute("selectedWidgetType", widgettype);
         model.addAttribute("selectedWidgetStatus", widgetstatus);
         return ViewNames.ADMIN_WIDGETS;
     }
@@ -135,7 +135,7 @@ public class WidgetController {
         this.widgetService = widgetService;
     }
 
-    void setWidgetValidator(NewWidgetValidator widgetValidator) {
+    void setWidgetValidator(UpdateWidgetValidator widgetValidator) {
         this.widgetValidator = widgetValidator;
     }
 

Modified: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewWidgetValidator.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewWidgetValidator.java?rev=1197509&r1=1197508&r2=1197509&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewWidgetValidator.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewWidgetValidator.java Fri Nov  4 11:19:31 2011
@@ -19,84 +19,29 @@
 
 package org.apache.rave.portal.web.validator;
 
-import org.apache.commons.lang.StringUtils;
-import org.apache.commons.validator.UrlValidator;
 import org.apache.rave.portal.model.Widget;
+import org.apache.rave.portal.service.WidgetService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.Errors;
-import org.springframework.validation.ValidationUtils;
-import org.springframework.validation.Validator;
 
 /**
  * Validator for adding a new {@link Widget}
  */
-public class NewWidgetValidator implements Validator {
-    private static final String FIELD_URL = "url";
+public class NewWidgetValidator extends WidgetValidator {
 
-    private UrlValidator validator;
+    private final WidgetService widgetService;
 
-    public NewWidgetValidator() {
+    @Autowired
+    public NewWidgetValidator(WidgetService widgetService) {
         super();
-        String[] allowedSchemes = {"http", "https"};
-        validator = new UrlValidator(allowedSchemes);
+        this.widgetService = widgetService;
     }
 
-    /**
-     * Supports {@link Widget}
-     * <p/>
-     * {@inheritDoc}
-     */
     @Override
-    public boolean supports(Class<?> clazz) {
-        return Widget.class.isAssignableFrom(clazz);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void validate(Object target, Errors errors) {
-        Widget widget = (Widget) target;
-
-        validateRequiredFields(errors);
-        validateUrlFields(widget, errors);
-    }
-
-    /**
-     * Checks if the required fields contain a value
-     *
-     * @param errors {@link Errors}
-     */
-    private void validateRequiredFields(Errors errors) {
-        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "title", "widget.title.required");
-        ValidationUtils.rejectIfEmptyOrWhitespace(errors, FIELD_URL, "widget.url.required");
-        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "type", "widget.type.required");
-    }
-
-    /**
-     * Validates fields that may contain a URL
-     *
-     * @param widget {@link Widget} to validate
-     * @param errors {@link org.springframework.validation.Errors}
-     */
-    private void validateUrlFields(Widget widget, Errors errors) {
-        String url = widget.getUrl();
-        if (StringUtils.isNotBlank(url) && !validator.isValid(url)) {
-            errors.rejectValue(FIELD_URL, "widget.url.malformed");
-        }
-
-        String screenshotUrl = widget.getScreenshotUrl();
-        if (StringUtils.isNotBlank(screenshotUrl) && !validator.isValid(screenshotUrl)) {
-            errors.rejectValue("screenshotUrl", "widget.screenshotUrl.malformed");
-        }
-
-        String thumbnailUrl = widget.getThumbnailUrl();
-        if (StringUtils.isNotBlank(thumbnailUrl) && !validator.isValid(thumbnailUrl)) {
-            errors.rejectValue("thumbnailUrl", "widget.thumbnailUrl.malformed");
-        }
-
-        String titleUrl = widget.getTitleUrl();
-        if (StringUtils.isNotBlank(titleUrl) && !validator.isValid(titleUrl)) {
-            errors.rejectValue("titleUrl", "widget.titleUrl.malformed");
+    protected final void validateIfWidgetAlreadyExists(Widget widget, Errors errors) {
+        if (widgetService.isRegisteredUrl(widget.getUrl())) {
+            errors.rejectValue(FIELD_URL, "widget.url.exists");
         }
     }
+
 }

Added: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/UpdateWidgetValidator.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/UpdateWidgetValidator.java?rev=1197509&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/UpdateWidgetValidator.java (added)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/UpdateWidgetValidator.java Fri Nov  4 11:19:31 2011
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.rave.portal.web.validator;
+
+import org.apache.rave.portal.model.Widget;
+import org.apache.rave.portal.service.WidgetService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.Errors;
+
+/**
+ * Validator for updating existing widgets. Need admin privileges to use this.
+ */
+@Component
+public class UpdateWidgetValidator extends WidgetValidator {
+
+    private final WidgetService widgetService;
+
+    @Autowired
+    public UpdateWidgetValidator(WidgetService widgetService) {
+        super();
+        this.widgetService = widgetService;
+    }
+
+    @Override
+    protected final void validateIfWidgetAlreadyExists(Widget widget, Errors errors) {
+        Widget existing = widgetService.getWidgetByUrl(widget.getUrl());
+        if (existing == null || existing.getEntityId().equals(widget.getEntityId())) {
+            return;
+        }
+        errors.rejectValue(FIELD_URL, "widget.url.exists");
+    }
+}

Added: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/WidgetValidator.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/WidgetValidator.java?rev=1197509&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/WidgetValidator.java (added)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/WidgetValidator.java Fri Nov  4 11:19:31 2011
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.rave.portal.web.validator;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.validator.UrlValidator;
+import org.apache.rave.portal.model.Widget;
+import org.springframework.validation.Errors;
+import org.springframework.validation.ValidationUtils;
+import org.springframework.validation.Validator;
+
+/**
+Abstract {@link Validator} for {@link Widget}'s
+ */
+public abstract class WidgetValidator implements Validator {
+    protected static final String FIELD_URL = "url";
+
+    private final UrlValidator urlValidator;
+
+    public WidgetValidator() {
+        super();
+        String[] allowedSchemes = {"http", "https"};
+        urlValidator = new UrlValidator(allowedSchemes);
+    }
+
+    /**
+     * Supports {@link org.apache.rave.portal.model.Widget}
+     * <p/>
+     * {@inheritDoc}
+     */
+    @Override
+    public final boolean supports(Class<?> clazz) {
+        return Widget.class.isAssignableFrom(clazz);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void validate(Object target, Errors errors) {
+        Widget widget = (Widget) target;
+
+        validateRequiredFields(errors);
+        validateIfWidgetAlreadyExists(widget, errors);
+        validateUrlFields(widget, errors);
+    }
+
+    /**
+     * Checks if a Widget already exists for this URL. 
+     * @param widget {@link Widget} to validate
+     * @param errors {@link Errors}
+     */
+    protected abstract void validateIfWidgetAlreadyExists(Widget widget, Errors errors);
+
+    /**
+     * Checks if the required fields contain a value
+     *
+     * @param errors {@link Errors}
+     */
+    private void validateRequiredFields(Errors errors) {
+        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "title", "widget.title.required");
+        ValidationUtils.rejectIfEmptyOrWhitespace(errors, FIELD_URL, "widget.url.required");
+        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "type", "widget.type.required");
+    }
+
+    /**
+     * Validates fields that may contain a URL
+     *
+     * @param widget {@link Widget} to validate
+     * @param errors {@link org.springframework.validation.Errors}
+     */
+    private void validateUrlFields(Widget widget, Errors errors) {
+        String url = widget.getUrl();
+        if (StringUtils.isNotBlank(url) && !urlValidator.isValid(url)) {
+            errors.rejectValue(FIELD_URL, "widget.url.malformed");
+        }
+
+        String screenshotUrl = widget.getScreenshotUrl();
+        if (StringUtils.isNotBlank(screenshotUrl) && !urlValidator.isValid(screenshotUrl)) {
+            errors.rejectValue("screenshotUrl", "widget.screenshotUrl.malformed");
+        }
+
+        String thumbnailUrl = widget.getThumbnailUrl();
+        if (StringUtils.isNotBlank(thumbnailUrl) && !urlValidator.isValid(thumbnailUrl)) {
+            errors.rejectValue("thumbnailUrl", "widget.thumbnailUrl.malformed");
+        }
+
+        String titleUrl = widget.getTitleUrl();
+        if (StringUtils.isNotBlank(titleUrl) && !urlValidator.isValid(titleUrl)) {
+            errors.rejectValue("titleUrl", "widget.titleUrl.malformed");
+        }
+    }
+
+
+}

Modified: incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/WidgetStoreControllerTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/WidgetStoreControllerTest.java?rev=1197509&r1=1197508&r2=1197509&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/WidgetStoreControllerTest.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/WidgetStoreControllerTest.java Fri Nov  4 11:19:31 2011
@@ -20,12 +20,11 @@
 package org.apache.rave.portal.web.controller;
 
 
-import java.util.HashMap;
-import org.apache.rave.portal.model.util.WidgetStatistics;
 import org.apache.rave.portal.model.User;
 import org.apache.rave.portal.model.Widget;
 import org.apache.rave.portal.model.WidgetStatus;
 import org.apache.rave.portal.model.util.SearchResult;
+import org.apache.rave.portal.model.util.WidgetStatistics;
 import org.apache.rave.portal.service.UserService;
 import org.apache.rave.portal.service.WidgetService;
 import org.apache.rave.portal.web.util.ModelKeys;
@@ -39,16 +38,23 @@ import org.springframework.validation.Be
 import org.springframework.validation.BindingResult;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import static junit.framework.Assert.assertEquals;
-import static org.easymock.EasyMock.*;
+import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 
 
 /**
@@ -78,7 +84,7 @@ public class WidgetStoreControllerTest {
         UserService userService = createMock(UserService.class);        
         expect(userService.getAuthenticatedUser()).andReturn(validUser);
         replay(userService);
-        NewWidgetValidator widgetValidator = new NewWidgetValidator();
+        NewWidgetValidator widgetValidator = new NewWidgetValidator(widgetService);
         controller = new WidgetStoreController(widgetService, widgetValidator, userService);
     }
 
@@ -172,6 +178,7 @@ public class WidgetStoreControllerTest {
         final BindingResult errors = new BeanPropertyBindingResult(widget, "widget");
 
         expect(widgetService.registerNewWidget(widget)).andReturn(widget);
+        expect(widgetService.isRegisteredUrl(widgetUrl)).andReturn(false);
         expect(widgetService.getWidgetStatistics(WIDGET_ID, validUser.getEntityId())).andReturn(widgetStatistics);
         replay(widgetService);
         String view = controller.viewAddWidgetResult(widget, errors, model);
@@ -188,13 +195,20 @@ public class WidgetStoreControllerTest {
     public void doAddWidget_existing() {
         final String widgetUrl = "http://example.com/existingwidget.xml";
         final Model model = new ExtendedModelMap();
+
+        final Widget existingWidget = new Widget();
+        existingWidget.setEntityId(123L);
+        existingWidget.setTitle("Widget title");
+        existingWidget.setUrl(widgetUrl);
+        existingWidget.setType("OpenSocial");
+
         final Widget widget = new Widget();
         widget.setTitle("Widget title");
         widget.setUrl(widgetUrl);
         widget.setType("OpenSocial");
         final BindingResult errors = new BeanPropertyBindingResult(widget, "widget");
 
-        expect(widgetService.registerNewWidget(widget)).andReturn(null);        
+        expect(widgetService.isRegisteredUrl(widgetUrl)).andReturn(true);
         replay(widgetService);
         String view = controller.viewAddWidgetResult(widget, errors, model);
         verify(widgetService);

Modified: incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/admin/WidgetControllerTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/admin/WidgetControllerTest.java?rev=1197509&r1=1197508&r2=1197509&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/admin/WidgetControllerTest.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/admin/WidgetControllerTest.java Fri Nov  4 11:19:31 2011
@@ -24,7 +24,7 @@ import org.apache.rave.portal.model.util
 import org.apache.rave.portal.service.WidgetService;
 import org.apache.rave.portal.web.util.ModelKeys;
 import org.apache.rave.portal.web.util.ViewNames;
-import org.apache.rave.portal.web.validator.NewWidgetValidator;
+import org.apache.rave.portal.web.validator.UpdateWidgetValidator;
 import org.junit.Before;
 import org.junit.Test;
 import org.springframework.ui.ExtendedModelMap;
@@ -57,7 +57,7 @@ public class WidgetControllerTest {
 
     private WidgetController controller;
     private WidgetService service;
-    private NewWidgetValidator validator;
+    private UpdateWidgetValidator widgetValidator;
     private String validToken;
 
     @Test
@@ -115,13 +115,15 @@ public class WidgetControllerTest {
 
     @Test
     public void updateWidget_valid() {
-        Widget widget = new Widget(123L, "http://example.com/widget");
+        final String widgetUrl = "http://example.com/widget";
+        Widget widget = new Widget(123L, widgetUrl);
         widget.setTitle("Widget title");
         widget.setType("OpenSocial");
         BindingResult errors = new BeanPropertyBindingResult(widget, "widget");
         SessionStatus sessionStatus = createMock(SessionStatus.class);
         ModelMap modelMap = new ExtendedModelMap();
 
+        expect(service.getWidgetByUrl(widgetUrl)).andReturn(widget);
         service.updateWidget(widget);
         sessionStatus.setComplete();
         expectLastCall();
@@ -172,8 +174,8 @@ public class WidgetControllerTest {
         controller = new WidgetController();
         service = createMock(WidgetService.class);
         controller.setWidgetService(service);
-        validator = new NewWidgetValidator();
-        controller.setWidgetValidator(validator);
+        widgetValidator = new UpdateWidgetValidator(service);
+        controller.setWidgetValidator(widgetValidator);
         validToken = AdminControllerUtil.generateSessionToken();
     }
 

Modified: incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/NewWidgetValidatorTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/NewWidgetValidatorTest.java?rev=1197509&r1=1197508&r2=1197509&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/NewWidgetValidatorTest.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/NewWidgetValidatorTest.java Fri Nov  4 11:19:31 2011
@@ -20,6 +20,7 @@
 package org.apache.rave.portal.web.validator;
 
 import org.apache.rave.portal.model.Widget;
+import org.apache.rave.portal.service.WidgetService;
 import org.junit.Before;
 import org.junit.Test;
 import org.springframework.validation.BindException;
@@ -28,7 +29,10 @@ import org.springframework.validation.Er
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
 
 /**
  * Test class for {@link NewWidgetValidator}
@@ -40,12 +44,8 @@ public class NewWidgetValidatorTest {
     private static final String VALID_TYPE = "OpenSocial";
     private static final String WIDGET = "widget";
 
-    private NewWidgetValidator newWidgetValidator;
-
-    @Test
-    public void testSupports() throws Exception {
-        assertTrue("Supports org.apache.rave.portal.model.Widget", newWidgetValidator.supports(Widget.class));
-    }
+    private NewWidgetValidator widgetValidator;
+    private WidgetService widgetService;
 
     @Test
     public void testValidateValidFormData() throws Exception {
@@ -55,7 +55,11 @@ public class NewWidgetValidatorTest {
         widget.setType(VALID_TYPE);
         Errors errors = new BindException(widget, WIDGET);
 
-        newWidgetValidator.validate(widget, errors);
+        expect(widgetService.isRegisteredUrl(VALID_URL)).andReturn(false);
+        replay(widgetService);
+        widgetValidator.validate(widget, errors);
+        verify(widgetService);
+
         assertFalse("No validation errors", errors.hasErrors());
     }
 
@@ -64,12 +68,37 @@ public class NewWidgetValidatorTest {
         Widget widget = new Widget();
         Errors errors = new BindException(widget, WIDGET);
 
-        newWidgetValidator.validate(widget, errors);
+        widgetValidator.validate(widget, errors);
 
         assertEquals(3, errors.getErrorCount());
     }
 
     @Test
+    public void testValidationFailsOnDuplicateUrl() {
+        final String existingUrl = "http://example.com/existing_widget.xml";
+
+        Widget widget = new Widget();
+        widget.setEntityId(123L);
+        widget.setTitle(VALID_TITLE);
+        widget.setType(VALID_TYPE);
+        widget.setUrl(existingUrl);
+
+        Widget newWidget = new Widget();
+        newWidget.setTitle(VALID_TITLE);
+        newWidget.setType(VALID_TYPE);
+        newWidget.setUrl(existingUrl);
+        Errors errors = new BindException(newWidget, WIDGET);
+
+        expect(widgetService.isRegisteredUrl(existingUrl)).andReturn(true);
+        replay(widgetService);
+
+        widgetValidator.validate(newWidget, errors);
+        verify(widgetService);
+        assertEquals(1, errors.getErrorCount());
+        assertNotNull("Field error for duplicate url", errors.getFieldError("url"));
+    }
+
+    @Test
     public void testValidationFailsOnInvalidUrl() {
         Widget widget = new Widget();
         widget.setTitle(VALID_TITLE);
@@ -80,7 +109,7 @@ public class NewWidgetValidatorTest {
         widget.setTitleUrl("titleUrl");
         Errors errors = new BindException(widget, WIDGET);
 
-        newWidgetValidator.validate(widget, errors);
+        widgetValidator.validate(widget, errors);
         assertEquals(4, errors.getErrorCount());
         assertNotNull("Field error on url", errors.getFieldError("url"));
         assertNotNull("Field error on screenshot url", errors.getFieldError("screenshotUrl"));
@@ -90,6 +119,7 @@ public class NewWidgetValidatorTest {
 
     @Before
     public void setup() {
-        newWidgetValidator = new NewWidgetValidator();
+        widgetService = createMock(WidgetService.class);
+        widgetValidator = new NewWidgetValidator(widgetService);
     }
 }

Copied: incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/UpdateWidgetValidatorTest.java (from r1197469, incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/NewWidgetValidatorTest.java)
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/UpdateWidgetValidatorTest.java?p2=incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/UpdateWidgetValidatorTest.java&p1=incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/NewWidgetValidatorTest.java&r1=1197469&r2=1197509&rev=1197509&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/NewWidgetValidatorTest.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/UpdateWidgetValidatorTest.java Fri Nov  4 11:19:31 2011
@@ -7,7 +7,7 @@
  * "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
+ *   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
@@ -20,6 +20,7 @@
 package org.apache.rave.portal.web.validator;
 
 import org.apache.rave.portal.model.Widget;
+import org.apache.rave.portal.service.WidgetService;
 import org.junit.Before;
 import org.junit.Test;
 import org.springframework.validation.BindException;
@@ -28,34 +29,38 @@ import org.springframework.validation.Er
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
 
 /**
- * Test class for {@link NewWidgetValidator}
+ * Test class for {@link org.apache.rave.portal.web.validator.UpdateWidgetValidator}
  */
-public class NewWidgetValidatorTest {
+public class UpdateWidgetValidatorTest {
 
     private static final String VALID_TITLE = "My widget";
     private static final String VALID_URL = "http://example.com/widget.xml";
     private static final String VALID_TYPE = "OpenSocial";
     private static final String WIDGET = "widget";
 
-    private NewWidgetValidator newWidgetValidator;
-
-    @Test
-    public void testSupports() throws Exception {
-        assertTrue("Supports org.apache.rave.portal.model.Widget", newWidgetValidator.supports(Widget.class));
-    }
+    private UpdateWidgetValidator widgetValidator;
+    private WidgetService widgetService;
 
     @Test
     public void testValidateValidFormData() throws Exception {
         Widget widget = new Widget();
+        widget.setEntityId(123L);
         widget.setTitle(VALID_TITLE);
         widget.setUrl(VALID_URL);
         widget.setType(VALID_TYPE);
         Errors errors = new BindException(widget, WIDGET);
 
-        newWidgetValidator.validate(widget, errors);
+        expect(widgetService.getWidgetByUrl(VALID_URL)).andReturn(widget);
+        replay(widgetService);
+        widgetValidator.validate(widget, errors);
+        verify(widgetService);
+
         assertFalse("No validation errors", errors.hasErrors());
     }
 
@@ -64,12 +69,37 @@ public class NewWidgetValidatorTest {
         Widget widget = new Widget();
         Errors errors = new BindException(widget, WIDGET);
 
-        newWidgetValidator.validate(widget, errors);
+        widgetValidator.validate(widget, errors);
 
         assertEquals(3, errors.getErrorCount());
     }
 
     @Test
+    public void testValidationFailsOnDuplicateUrl() {
+        final String existingUrl = "http://example.com/existing_widget.xml";
+
+        Widget widget = new Widget();
+        widget.setEntityId(123L);
+        widget.setTitle(VALID_TITLE);
+        widget.setType(VALID_TYPE);
+        widget.setUrl(existingUrl);
+
+        Widget newWidget = new Widget();
+        newWidget.setTitle(VALID_TITLE);
+        newWidget.setType(VALID_TYPE);
+        newWidget.setUrl(existingUrl);
+        Errors errors = new BindException(newWidget, WIDGET);
+
+        expect(widgetService.getWidgetByUrl(existingUrl)).andReturn(widget);
+        replay(widgetService);
+
+        widgetValidator.validate(newWidget, errors);
+        verify(widgetService);
+        assertEquals(1, errors.getErrorCount());
+        assertNotNull("Field error for duplicate url", errors.getFieldError("url"));
+    }
+
+    @Test
     public void testValidationFailsOnInvalidUrl() {
         Widget widget = new Widget();
         widget.setTitle(VALID_TITLE);
@@ -80,7 +110,7 @@ public class NewWidgetValidatorTest {
         widget.setTitleUrl("titleUrl");
         Errors errors = new BindException(widget, WIDGET);
 
-        newWidgetValidator.validate(widget, errors);
+        widgetValidator.validate(widget, errors);
         assertEquals(4, errors.getErrorCount());
         assertNotNull("Field error on url", errors.getFieldError("url"));
         assertNotNull("Field error on screenshot url", errors.getFieldError("screenshotUrl"));
@@ -90,6 +120,7 @@ public class NewWidgetValidatorTest {
 
     @Before
     public void setup() {
-        newWidgetValidator = new NewWidgetValidator();
+        widgetService = createMock(WidgetService.class);
+        widgetValidator = new UpdateWidgetValidator(widgetService);
     }
 }

Added: incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/WidgetValidatorTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/WidgetValidatorTest.java?rev=1197509&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/WidgetValidatorTest.java (added)
+++ incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/validator/WidgetValidatorTest.java Fri Nov  4 11:19:31 2011
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.rave.portal.web.validator;
+
+import org.apache.rave.portal.model.Widget;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.validation.Errors;
+
+import static junit.framework.Assert.assertTrue;
+
+/**
+ * Test for {@link WidgetValidator}
+ */
+public class WidgetValidatorTest {
+    WidgetValidator widgetValidator;
+
+    @Before
+    public void setUp() throws Exception {
+        widgetValidator = new MockWidgetValidator();
+
+    }
+
+    @Test
+    public void testSupports() throws Exception {
+        assertTrue("Supports org.apache.rave.portal.model.Widget", widgetValidator.supports(Widget.class));
+    }
+
+    private class MockWidgetValidator extends WidgetValidator {
+
+        @Override
+        protected void validateIfWidgetAlreadyExists(Widget widget, Errors errors) {
+        }
+    }
+}

Modified: incubator/rave/trunk/rave-portal-resources/src/main/resources/messages.properties
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-portal-resources/src/main/resources/messages.properties?rev=1197509&r1=1197508&r2=1197509&view=diff
==============================================================================
--- incubator/rave/trunk/rave-portal-resources/src/main/resources/messages.properties (original)
+++ incubator/rave/trunk/rave-portal-resources/src/main/resources/messages.properties Fri Nov  4 11:19:31 2011
@@ -170,6 +170,7 @@ widget.type.required=Choose a widget typ
 widget.url=Location (URL)
 widget.url.malformed=URL is malformed
 widget.url.required=URL is required
+widget.url.exists=A Widget already exists for this URL
 widget.widgetStatus=Status
 widget.widgetStatus.PREVIEW=This widget is waiting for approval by the site administrator.
 widget.widgetStatus.PUBLISHED=

Modified: incubator/rave/trunk/rave-portal-resources/src/main/resources/messages_nl.properties
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-portal-resources/src/main/resources/messages_nl.properties?rev=1197509&r1=1197508&r2=1197509&view=diff
==============================================================================
--- incubator/rave/trunk/rave-portal-resources/src/main/resources/messages_nl.properties (original)
+++ incubator/rave/trunk/rave-portal-resources/src/main/resources/messages_nl.properties Fri Nov  4 11:19:31 2011
@@ -170,6 +170,7 @@ widget.type.required=Kies een widget typ
 widget.url=Locatie (URL)
 widget.url.malformed=URL is niet correct
 widget.url.required=URL is verplicht
+widget.url.exists=Er bestaat al een widget met deze URL
 widget.widgetStatus=Status
 widget.widgetStatus.PREVIEW=Deze widget wacht op goedkeuring door de sitebeheerder
 widget.widgetStatus.PUBLISHED=
@@ -181,4 +182,4 @@ widget.menu.editprefs=Pas voorkeursinste
 widget.menu.maximize=Maximaliseer
 widget.menu.movethiswidget=Verplaats deze widget naar pagina:
 widget.menu.movetopage=Verplaats naar Pagina
-widget.menu.title=Widget Acties
\ No newline at end of file
+widget.menu.title=Widget Acties