I have a Spring Boot application where an interface has a constraint:
#Constraint(
validatedBy = {
MyValidator.class
})
public #interface MyInterface {
...
}
I'm using Togglz to enable/disable some features and one class where I want to implement some Togglz code is in MyValidator.
public class MyValidator
implements MyInterface<
MyInterface, TDLDetails> {
private FeatureManager featureManager;
public static final Feature FEATURE_ONE =
new NamedFeature("FEATURE_ONE ");
public MyValidator(FeatureManager featureManager) {
this.featureManager = featureManager;
}
#Override
public void initialize(MyInterface arg0) {}
#Override
public boolean isValid(TDLDetails tdlDetails, ConstraintValidatorContext ctx)
{
if (!featureManager.isActive(FEATURE_ONE)) {
if (tdlDetails.getType().equals(TDLType.ANA)) {
return (tdlDetails.getPlaceOfIssue() != null);
}
}
return true;
}
}
Am I wrong to have the parameterized constructor? It seems I need it for Togglz but I'm not sure how it should be used by #Constraint if it takes a parameter. What's the correct way to do this?
Related
again a small problem by understanding "how tapestry works".
I've got a Tapestry component (in this case a value encoder):
public class EditionEncoder implements ValueEncoder<Edition>, ValueEncoderFactory<Edition> {
#Inject
private IEditionManager editionDao;
public EditionEncoder(IEditionManager editionDao) {
this.editionManager = editionDao;
}
#Override
public String toClient(Edition value) {
if(value == null) {
return "";
}
return value.getName();
}
#Override
public Edition toValue(String clientValue) {
if(clientValue.equals("")) {
return null;
}
return editionManager.getEditionByName(clientValue);
}
#Override
public ValueEncoder<Edition> create(Class<Edition> type) {
return this;
}
}
Injecting the the Manager is not working, because the Encoder is created within a page like that:
public void create() {
editionEncoder = new EditionEncoder();
}
casued by this, i'm forced to use this ugly solution:
#Inject
private IEditionManager editionmanager;
editionEncoder = new EditionEncoder(editionManager);
Is there a better way to inject components during runtime or is there a better solution in general for it?
Thanks for your help in advance,
As soon as you use "new" then tapestry-ioc is not involved in object creation and can't inject. You should inject everything and never use "new" for singleton services. This is true for all ioc containers, not just tapestry-ioc.
Also if you put #Inject on a field then you don't also need a constructor to set it. Do one or the other, never both.
You should do something like this:
public class MyAppModule {
public void bind(ServiceBinder binder) {
binder.bind(EditionEncoder.class);
}
}
Then in your page/component/service
#Inject EditionEncoder editionEncoder;
If you wanted to put your own instantiated objects in there you can do
public class MyServiceModule {
public void bind(ServiceBinder binder) {
binder.bind(Service1.class, Service1Impl.class);
binder.bind(Service2.class, Service2Impl.class);
}
public SomeService buildSomeService(Service1 service1, Service2 service2, #AutoBuild Service3Impl service3) {
Date someDate = new Date();
return new SomeServiceImpl(service1, service2, service3, someDate);
}
}
Thymeleaf has a number of useful utilities like #strings.capitalize(...) or #lists.isEmpty(...). I'm trying to add a custom one but have no idea how to register this.
Have made a custom Util class:
public class LabelUtil {
public String[] splitDoubleWord(String str) {
return str.split("[A-Z]", 1);
}
}
Now I'm going to use it like this:
<span th:each="item : ${#labels.splitDoubleWord(name)}" th:text="${item}"></span>
Of course, it won't work because I haven't registered the Util and defined #labels var.
So, the question is how and where to do it?
This answer is for thymeleaf 2.x.
If you use thymeleaf 3.x or later, please see other answers.
public class MyDialect extends AbstractDialect implements IExpressionEnhancingDialect {
public MyDialect() {
super();
}
#Override
public String getPrefix() {
// #see org.thymeleaf.dialect.IDialect#getPrefix
return "xxx";
}
#Override
public boolean isLenient() {
return false;
}
#Override
public Map<String, Object> getAdditionalExpressionObjects(IProcessingContext ctx) {
Map<String, Object> expressions = new HashMap<>();
expressions.put("labels", new LabelUtil());
return expressions;
}
}
and register your dialect.
#Configuration
public class ThymeleafConfig {
#Bean
public MyDialect myDialect() {
return new MyDialect();
}
}
thymeleaf-extras-java8time source code is good reference for creating custom thymeleaf expressions.
The API for registering a custom expression object has changed in Thymeleaf 3, for example:
public class MyDialect extends AbstractDialect implements IExpressionObjectDialect {
MyDialect() {
super("My Dialect");
}
#Override
public IExpressionObjectFactory getExpressionObjectFactory() {
return new IExpressionObjectFactory() {
#Override
public Set<String> getAllExpressionObjectNames() {
return Collections.singleton("myutil");
}
#Override
public Object buildObject(IExpressionContext context,
String expressionObjectName) {
return new MyUtil();
}
#Override
public boolean isCacheable(String expressionObjectName) {
return true;
}
};
}
}
I'm using Cucumber with Guice as DI.
I've encountered following problem:
I've got one step i.e.
class MyStep() {
#Inject
private MyService myService;
#Given("Some acction happen")
public void sthHappen() {
myService.doSth();
}
}
And I've got this class to run it as JUnit test
#RunWith(Cucumber.class)
#CucumberOptions(...)
public class MyTest {
}
There is a
class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(MyService.class).to(MyFirstService.class);
}
}
which is used by my MyInjectorSource
I define cucumber.properties where I define guice.injector-source=MyInjectorSource;
There is also a feature file with scenario.
Everything is working for now.
And no i would like to run MyStep step with other MyService implementation (of course I don't wont to duplicate code of MyStep)
I define a new feature file with new scenarios, and new Test class
#RunWith(Cucumber.class)
#CucumberOptions(...)
public class MyOtherTest {
}
And now I've tried to create another InjectorSource but I was not able to configure it.
Solution which I've found is using custom Junit4 runner inheriting from original Cucumber runner and changing its createRuntime method.
Latest cucumber-guice 1.2.5 uses few stages to create injector and unfortunately it uses global variable cucumber.runtime.Env.INSTANCE. This variable is populated from cucumber.properties and System.getProperties.
Flow is:
Cucumber runner scans available backends (in my setup it is cucumber.runtime.java.JavaBackend)
One of JavaBackend constructor loads available ObjectFactory (in my setup it is cucumber.runtime.java.guice.impl.GuiceFactory)
GuiceFactory via InjectorSourceFactory checks Env.INSTANCE, it will create custom InjectorSource or default injector
Ideally cucumber should pass its 'RuntimeOptions` created at start to backend and InjectorSource but unfortunately it doesn't and uses global variable. It is not easy create patch like this one so my solution simplifies this approach and directly create InjectorSource in custom runner by reading new annotation.
public class GuiceCucumberRunner extends Cucumber {
public GuiceCucumberRunner(Class<?> clazz) throws InitializationError, IOException {
super(clazz);
}
#Override
protected Runtime createRuntime(ResourceLoader resourceLoader, ClassLoader classLoader, RuntimeOptions runtimeOptions) throws InitializationError, IOException {
Runtime result = new Runtime(resourceLoader, classLoader, Arrays.asList(createGuiceBackend()), runtimeOptions);
return result;
}
private JavaBackend createGuiceBackend() {
GuiceCucumberOptions guiceCucumberOptions = getGuiceCucumberOptions();
InjectorSource injectorSource = createInjectorSource(guiceCucumberOptions.injectorSource());
ObjectFactory objectFactory = new GuiceFactory(injectorSource.getInjector());
JavaBackend result = new JavaBackend(objectFactory);
return result;
}
private GuiceCucumberOptions getGuiceCucumberOptions() {
GuiceCucumberOptions guiceCucumberOptions = getTestClass().getJavaClass().getAnnotation(GuiceCucumberOptions.class);
if (guiceCucumberOptions == null) {
String message = format("Suite class ''{0}'' is missing annotation GuiceCucumberOptions", getTestClass().getJavaClass());
throw new CucumberException(message);
}
return guiceCucumberOptions;
}
private InjectorSource createInjectorSource(Class<? extends InjectorSource> clazz) {
try {
return clazz.newInstance();
} catch (Exception e) {
String message = format("Instantiation of ''{0}'' failed. InjectorSource must have has a public zero args constructor.", clazz);
throw new InjectorSourceInstantiationFailed(message, e);
}
}
static class GuiceFactory implements ObjectFactory {
private final Injector injector;
GuiceFactory(Injector injector) {
this.injector = injector;
}
#Override
public boolean addClass(Class<?> clazz) {
return true;
}
#Override
public void start() {
injector.getInstance(ScenarioScope.class).enterScope();
}
#Override
public void stop() {
injector.getInstance(ScenarioScope.class).exitScope();
}
#Override
public <T> T getInstance(Class<T> clazz) {
return injector.getInstance(clazz);
}
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE })
public #interface GuiceCucumberOptions {
Class<? extends InjectorSource> injectorSource();
}
#RunWith(GuiceCucumberRunner.class)
#GuiceCucumberOptions(injectorSource = MyInjector.class)
#CucumberOptions(
...
)
public class Suite {
}
I needed to copy GuiceFactory because it doesn't exposes normal constructor (!)
I have Neo4j unmanaged extension. I want some services to be created as singletons and be available via #Context in my resources.
Something like this:
#Path("/example")
public class ExampleResource {
public ExampleResource(#Context CostlyService costlyService) { // <<---
// use it here
}
}
How this can be achieved?
Neo4j has PluginLifecycle interface that give us possibility to hook into Neo4j server lifecycle and provide our own services for injection blog post.
So, we have service. Let's take this one as example:
public interface CostlyService {
}
public class CostlyServiceImpl implements CostlyService {
public CostlyService() {
// a LOT of work done here
}
//...
}
Now we need to make our own PluginLifecycle implementation:
public class ExamplePluginLifecycle implements PluginLifecycle {
#Override
public Collection<Injectable<?>> start(GraphDatabaseService graphDatabaseService,
Configuration config) {
final List<Injectable<?>> injectables = new ArrayList<>();
return injectables;
}
#Override
public void stop() {
}
}
As you see, injectable list is empty for now. We will add our service there soon.
Important: you must register your PluginLifecycle implementation, so it will be available via SPI:
// file: META-INF/services/org.neo4j.server.plugins.PluginLifecycle
my.company.extension.ExamplePluginLifecycle
This will make your PluginLifecycle discoverable by Neo4j server.
Now we need to create actual injectable. Let's write implementation for Injectable interface:
public final class TypedInjectable<T> implements Injectable<T> {
private final T value;
private final Class<T> type;
private TypedInjectable(final T value, final Class<T> type) {
this.value = value;
this.type = type;
}
public static <T> TypedInjectable<T> injectable(final T value, final Class<T> type) {
return new TypedInjectable<>(value, type);
}
#Override
public T getValue() {
return value;
}
#Override
public Class<T> getType() {
return type;
}
}
This will serve as simple container for our service. Usage:
import static my.company.extension.TypedInjectable.injectable;
injectable(new CostlyServiceImpl(), CostlyService.class);
Now we can add our injectable into PluginLifecycle.
#Override
public Collection<Injectable<?>> start(GraphDatabaseService graphDatabaseService,
Configuration config) {
final List<Injectable<?>> injectables = new ArrayList<>();
injectables.add(injectable(new CostlyServiceImpl, CostlyService.class)); // <<---
return injectables;
}
After this change our CostlyService will be available for our resources via #Context:
#Path("/example")
public class ExampleResource {
public ExampleResource(#Context CostlyService costlyService) {
// use it here
}
// ...
}
Tip: keep your PluginLifecycle's in same package or in subpackage with your resources.
I have managed to successfully implement a custom injection annotation with target PARAMETER. I do not understand how I make my the annotation support target METHOD as well though?
Here is my sample code:
Hello annotation:
#Retention(RUNTIME)
#Target({METHOD, PARAMETER})
public #interface Hello {
}
Hello annotation resolver:
#Singleton
public class HelloResolver {
public static class HelloInjectionResolver extends ParamInjectionResolver<Hello> {
public HelloInjectionResolver() {
super(HelloValueFactoryProvider.class);
}
}
#Singleton
public static class HelloValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
public HelloValueFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider,
final ServiceLocator injector) {
super(extractorProvider, injector, UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(final Parameter parameter) {
if (!String.class.equals(parameter.getRawType())) return null;
if (parameter.getAnnotation(Hello.class) == null) return null;
return new AbstractContainerRequestValueFactory<String>() {
#Override
public String provide() {
final DateTime now = DateTime.now();
if (22 < now.getHourOfDay() || now.getHourOfDay() < 6) {
throw new WebApplicationException(FORBIDDEN);
} else {
return format("Hello, it is %s o'clock so I am awake! :)", forPattern("HH:mm").print(now));
}
}
};
}
}
public static class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(HelloValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
bind(HelloInjectionResolver.class).to(
new TypeLiteral<InjectionResolver<Hello>>() {
}
).in(Singleton.class);
}
}
}
Hello resources:
#Path("hello")
public class HelloResource {
#GET
#Path("method")
#Produces(APPLICATION_JSON)
#Hello
public String method() {
return "Hello!";
}
#GET
#Path("param")
#Produces(APPLICATION_JSON)
public String param(#Hello final String hello) {
return hello;
}
}
When I hit
http://localhost:8080/hello/method
I get a Hello! back no matter if the hour is within the forbidden interval.
I am not sure this will work, but you could try this:
public static class HelloInjectionResolver extends ParamInjectionResolver<Hello> {
public HelloInjectionResolver() {
super(HelloValueFactoryProvider.class);
}
public boolean isMethodParameterIndicator() {
return true;
}
}
Warning: I have not tried this myself but in theory that should allow your resolver to work as a parameter in the method.