Skip to content

Commit

Permalink
Merge branch 'master' of github.com:micronaut-projects/micronaut-core
Browse files Browse the repository at this point in the history
  • Loading branch information
graemerocher committed May 31, 2018
2 parents e5a550d + 796f731 commit b407a10
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package io.micronaut.core.convert;

import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationMetadataProvider;
import io.micronaut.core.type.Argument;

/**
Expand All @@ -25,10 +27,15 @@
* @author Graeme Rocher
* @since 1.0
*/
public interface ArgumentConversionContext<T> extends ConversionContext {
public interface ArgumentConversionContext<T> extends ConversionContext, AnnotationMetadataProvider {

/**
* @return The {@link Argument} being converted
*/
Argument<T> getArgument();

@Override
default AnnotationMetadata getAnnotationMetadata() {
return getArgument().getAnnotationMetadata();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2017-2018 original authors
*
* 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 io.micronaut.inject.field.nullableinjection;

public interface A {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2017-2018 original authors
*
* 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 io.micronaut.inject.field.nullableinjection;

import javax.annotation.Nullable;
import javax.inject.Inject;

public class B {

@Inject @Nullable protected A a;

public A getA() {
return this.a;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2017-2018 original authors
*
* 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 io.micronaut.inject.field.nullableinjection;

import javax.inject.Inject;

public class C {

@Inject protected A a;

public A getA() {
return this.a;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2017-2018 original authors
*
* 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 io.micronaut.inject.field.nullableinjection

import io.micronaut.context.BeanContext
import io.micronaut.context.DefaultBeanContext
import io.micronaut.context.exceptions.DependencyInjectionException
import spock.lang.Specification

class FieldNullableInjectionSpec extends Specification {

void "test nullable injection with field"() {
given:
BeanContext context = new DefaultBeanContext()
context.start()

when:"A bean is obtained that has a constructor with @Inject"
B b = context.getBean(B)

then:"The implementation is not injected, but null is"
b.a == null
}

void "test normal injection still fails"() {
given:
BeanContext context = new DefaultBeanContext()
context.start()

when:"A bean is obtained that has a constructor with @Inject"
context.getBean(C)

then:"The bean is not found"
thrown(DependencyInjectionException)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.micronaut.inject.value.nullablevalue;

import io.micronaut.context.annotation.Value;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;

@Singleton
public class A {

public final String nullConstructorArg;
public final String nonNullConstructorArg;
public String nullMethodArg;
public String nonNullMethodArg;
public @Value("${doesnt.exist}") @Nullable String nullField;
public @Value("${exists.x}") String nonNullField;


public A(@Value("${doesnt.exist}") @Nullable String nullConstructorArg,
@Value("${exists.x}") String nonNullConstructorArg) {
this.nullConstructorArg = nullConstructorArg;
this.nonNullConstructorArg = nonNullConstructorArg;
}

@Inject
void injectedMethod(@Value("${doesnt.exist}") @Nullable String nullMethodArg,
@Value("${exists.x}") String nonNullMethodArg) {
this.nullMethodArg = nullMethodArg;
this.nonNullMethodArg = nonNullMethodArg;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.micronaut.inject.value.nullablevalue

import io.micronaut.context.ApplicationContext
import spock.lang.Specification

class NullableValueSpec extends Specification {

void "test value with nullable"() {
given:
ApplicationContext context = ApplicationContext.run(
["exists.x":"fromConfig"], "test"
)

when:
A a = context.getBean(A)

then:
a.nullField == null
a.nonNullField == "fromConfig"
a.nullConstructorArg == null
a.nonNullConstructorArg == "fromConfig"
a.nullMethodArg == null
a.nonNullMethodArg == "fromConfig"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -748,9 +748,10 @@ protected final Object getValueForMethodArgument(BeanResolutionContext resolutio
} else {
if (value.isPresent()) {
return value.get();
} else if (!Iterable.class.isAssignableFrom(argumentType) && !Map.class.isAssignableFrom(argumentType)) {
throw new DependencyInjectionException(resolutionContext, injectionPoint, conversionContext, valString);
} else {
if (argument.getDeclaredAnnotation(Nullable.class) != null) {
return null;
}
throw new DependencyInjectionException(resolutionContext, injectionPoint, conversionContext, valString);
}
}
Expand Down Expand Up @@ -1134,7 +1135,14 @@ protected final Object getValueForField(BeanResolutionContext resolutionContext,
if (fieldType == Optional.class) {
return resolveOptionalObject(value);
} else {
return value.orElseThrow(() -> new DependencyInjectionException(resolutionContext, injectionPoint, "Error resolving field value [" + valString + "]. Property doesn't exist or cannot be converted"));
if (value.isPresent()) {
return value.get();
} else {
if (fieldArgument.getDeclaredAnnotation(Nullable.class) != null) {
return null;
}
throw new DependencyInjectionException(resolutionContext, injectionPoint, "Error resolving field value [" + valString + "]. Property doesn't exist or cannot be converted");
}
}
}
} else {
Expand Down Expand Up @@ -1288,6 +1296,10 @@ protected final Object getBeanForField(BeanResolutionContext resolutionContext,
path.pop();
return bean;
} catch (NoSuchBeanException e) {
if (injectionPoint.getDeclaredAnnotation(Nullable.class) != null) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, injectionPoint, e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import org.reactivestreams.Publisher
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification
import spock.util.concurrent.PollingConditions

import javax.inject.Singleton

Expand All @@ -62,8 +63,9 @@ class EventListenerSpec extends Specification {

then:
thrown(HttpClientResponseException)
embeddedServer.applicationContext.getBean(LoginFailedEventListener).events.size() ==
old(embeddedServer.applicationContext.getBean(LoginFailedEventListener).events.size()) + 1
new PollingConditions().eventually {
embeddedServer.applicationContext.getBean(LoginFailedEventListener).events.size() == 1
}
}

def "successful login publishes LoginSuccessfulEvent"() {
Expand All @@ -72,8 +74,9 @@ class EventListenerSpec extends Specification {
client.toBlocking().exchange(request)

then:
embeddedServer.applicationContext.getBean(LoginSuccessfulEventListener).events.size() ==
old(embeddedServer.applicationContext.getBean(LoginSuccessfulEventListener).events.size()) + 1
new PollingConditions().eventually {
embeddedServer.applicationContext.getBean(LoginSuccessfulEventListener).events.size() == 1
}
}

def "accessing a secured endpoints, validates Basic auth token and triggers TokenValidatedEvent"() {
Expand All @@ -82,8 +85,9 @@ class EventListenerSpec extends Specification {
client.toBlocking().exchange(request)

then:
embeddedServer.applicationContext.getBean(TokenValidatedEventListener).events.size() ==
old(embeddedServer.applicationContext.getBean(TokenValidatedEventListener).events.size()) + 1
new PollingConditions().eventually {
embeddedServer.applicationContext.getBean(TokenValidatedEventListener).events.size() == 1
}
}

def "invoking logout triggers LogoutEvent"() {
Expand All @@ -93,9 +97,10 @@ class EventListenerSpec extends Specification {

then:
thrown(HttpClientResponseException)
embeddedServer.applicationContext.getBean(LogoutEventListener).events.size() ==
old(embeddedServer.applicationContext.getBean(LogoutEventListener).events.size()) + 1
(embeddedServer.applicationContext.getBean(LogoutEventListener).events*.getSource() as List<Authentication>).find { it.name == 'user'}
new PollingConditions().eventually {
embeddedServer.applicationContext.getBean(LogoutEventListener).events.size() == 1
(embeddedServer.applicationContext.getBean(LogoutEventListener).events*.getSource() as List<Authentication>).any { it.name == 'user'}
}
}

@Requires(property = "spec.name", value = "eventlistener")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public ArgumentBinder.BindingResult<Session> bind(ArgumentConversionContext<Sess
return () -> existing;
} else {
// create a new session store it in the attribute
if (context.getAnnotation(Nullable.class) == null) {
if (!context.isAnnotationPresent(Nullable.class)) {
Session newSession = sessionStore.newSession();
attrs.put(HttpSessionFilter.SESSION_ATTRIBUTE, newSession);
return () -> Optional.of(newSession);
Expand Down

0 comments on commit b407a10

Please sign in to comment.