Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing problem overriding extractControllerNameFrom method from RoutesParser. #678

Closed
wants to merge 8 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,17 @@ protected String[] getURIsFor(Method javaMethod, Class<?> type) {
"You should specify paths either in @Path(\"/path\") or @Get(\"/path\") (or @Post, @Put, @Delete), not both at %s", javaMethod);

fixURIs(type, uris);

if(isSubClassAndHasDeclaredMethodExtractControllerNameFrom()) {
String[] urisWithoutControllerName = new String[uris.length];

for(int i = 0; i < uris.length; i++) {
urisWithoutControllerName[i] = extractControllerNameFrom(null) + uris[i];
}

return urisWithoutControllerName;
}

return uris;
}
String[] uris = getUris(javaMethod);
Expand Down Expand Up @@ -213,7 +224,14 @@ private String fixLeadingSlash(String uri) {
* controller name, given a type
*/
protected String extractControllerNameFrom(Class<?> type) {
String prefix = extractPrefix(type);
String prefix = "";

if(type == null) {
return prefix;
}

prefix = extractPrefix(type);

if (isNullOrEmpty(prefix)) {
String baseName = StringUtils.lowercaseFirst(type.getSimpleName());
if (baseName.endsWith("Controller")) {
Expand All @@ -238,4 +256,21 @@ private Predicate<Annotation> instanceOfMethodAnnotation() {
return or(instanceOf(Get.class), instanceOf(Post.class), instanceOf(Put.class), instanceOf(Delete.class), instanceOf(Options.class), instanceOf(Patch.class));
}

protected boolean isSubClassAndHasDeclaredMethodExtractControllerNameFrom() {
boolean hasMethod = false;

try {
this.getClass().getDeclaredMethod("extractControllerNameFrom", Class.class);
hasMethod = true;
} catch (NoSuchMethodException e) {
hasMethod = false;
}

if(PathAnnotationRoutesParser.class.isAssignableFrom(this.getClass().getSuperclass()) && hasMethod) {
return true;
}

return false;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/***
* Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
* All rights reserved.
*
* 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 br.com.caelum.vraptor.http.route;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

import java.util.List;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import br.com.caelum.vraptor.Controller;
import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.controller.DefaultBeanClass;
import br.com.caelum.vraptor.core.Converters;
import br.com.caelum.vraptor.http.EncodingHandler;
import br.com.caelum.vraptor.http.ParameterNameProvider;
import br.com.caelum.vraptor.http.ParanamerNameProvider;
import br.com.caelum.vraptor.proxy.JavassistProxifier;
import br.com.caelum.vraptor.proxy.Proxifier;

/**
* Tests for Overridable component
*
* @author Denilson Telaroli
*/
public class PathAnnotationRoutesParserOverridableTest {

static class OverridePathAnnotationRoutesParser extends PathAnnotationRoutesParser {
public OverridePathAnnotationRoutesParser(Router router) {
super(router);
}
@Override
protected String extractControllerNameFrom(Class<?> type) {
return "/prefix" + super.extractControllerNameFrom(type);
}
}

@Controller
static class ConventionController {
public void conventionMethod() {
}
@Path("/pathMethod")
public void withPathMethod() {
}
}

@Controller @Path("/path")
static class PathController {
public void conventionMethod() {
}
@Path("/pathMethod")
public void withPathMethod() {
}
}

private Proxifier proxifier;
private @Mock Converters converters;
private NoTypeFinder typeFinder;
private @Mock Router router;
private ParameterNameProvider nameProvider;
private @Mock EncodingHandler encodingHandler;
private OverridePathAnnotationRoutesParser parser;
private DefaultBeanClass pathController;
private DefaultBeanClass conventionController;
private List<Route> rulesForPathController;
private List<Route> rulesForConventionController;

@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);

this.proxifier = new JavassistProxifier();
this.typeFinder = new NoTypeFinder();
this.nameProvider = new ParanamerNameProvider();

when(router.builderFor(anyString())).thenAnswer(new Answer<DefaultRouteBuilder>() {
@Override
public DefaultRouteBuilder answer(InvocationOnMock invocation) throws Throwable {
return new DefaultRouteBuilder(proxifier, typeFinder, converters, nameProvider, new JavaEvaluator(),
(String) invocation.getArguments()[0],encodingHandler);
}
});

parser = new OverridePathAnnotationRoutesParser(router);

conventionController = new DefaultBeanClass(ConventionController.class);
pathController = new DefaultBeanClass(PathController.class);

rulesForPathController = parser.rulesFor(pathController);
rulesForConventionController = parser.rulesFor(conventionController);
}

private Route getRouteMatching(List<Route> routes, String uri) {
for (Route route : routes) {
if (route.canHandle(uri)) {
return route;
}
}
return null;
}

@Test
public void shouldExtractControllerNameWithPrefixFromConvention() {
Class<?> type = conventionController.getType();
assertThat(parser.extractControllerNameFrom(type), equalTo("/prefix/convention"));
}

@Test
public void shouldReturnControllerNameAndMethodNameWithPrefixFromConvention() {
Route route = getRouteMatching(rulesForConventionController, "/prefix/convention/conventionMethod");

assertThat(route, canHandle(ConventionController.class, "conventionMethod"));
}

@Test
public void shouldReturnMethodNameWithPrefixFromPathMethod() {
Route route = getRouteMatching(rulesForConventionController, "/prefix/pathMethod");

assertThat(route, notNullValue());
assertThat(route, canHandle(ConventionController.class, "withPathMethod"));
}

@Test
public void shouldExtractControllerNameWithPrefixFromPathAnnotation() {
Class<?> type = pathController.getType();

assertThat(parser.extractControllerNameFrom(type), equalTo("/prefix/path"));
}

@Test
public void shouldReturnControllerNameAndMethodNameWithPrefixFromPathAnnotation() {
Route route = getRouteMatching(rulesForPathController, "/prefix/path/conventionMethod");

assertThat(route, canHandle(PathController.class, "conventionMethod"));
}

@Test
public void shouldReturnControllerNameAndMethodNameWithPrefixFromPathAnnotationMethod() {
Route route = getRouteMatching(rulesForPathController, "/prefix/path/pathMethod");

assertThat(route, notNullValue());
assertThat(route, canHandle(PathController.class, "withPathMethod"));
}

private Matcher<Route> canHandle(final Class<?> type, final String method) {
return new TypeSafeMatcher<Route>() {

@Override
protected void describeMismatchSafely(Route item, Description mismatchDescription) {
}

@Override
protected boolean matchesSafely(Route item) {
try {
return item.canHandle(type, type.getDeclaredMethod(method));
} catch (Exception e) {
throw new RuntimeException(e);
}
}

@Override
public void describeTo(Description description) {
description.appendText("a route which can handle ").appendValue(type).appendText(".").appendValue(method);
}
};
}
}