How to inject a collection with provider - java

My application has a class called Gateway and a json file with a set of these gateways. I already parsed this json, giving me a Set object. Now I want to create a Multibinder to inject this set throughout my code. So far, I've created this provider:
public class GatewaysProvider implements Provider<Set<Gateway>> {
#Override
public Set<Gateway> get() {
try {
File file = new File(getClass().getResource("/gateways.json").toURI());
Type listType = new TypeToken<Set<Gateway>>(){}.getType();
return new Gson().fromJson(new FileReader(file), listType);
} catch (URISyntaxException | FileNotFoundException e) {
e.printStackTrace();
}
return new HashSet<>();
}
}
What more should I do to be able to inject this set anywhere in my code, like this:
Set<Gateways> gateways;
#Inject
public AppRunner(Set<Gateway> gateways) {
this.gateways = gateways;
}

What you need is the implementation of dependency injection mechanism.
You can do it yourself, but I'd suggest you to use existent DI library, like EasyDI
Please proceed, with steps below:
Add EasyDI to your classpath. With Maven it would be:
<dependency>
<groupId>eu.lestard</groupId>
<artifactId>easy-di</artifactId>
<version>0.3.0</version>
</dependency>
Add wrapper type for your Gateway set, and adjust Provider correspondingly:
public class GatewayContainer {
Set<Gateway> gateways;
public void setGateways(Set<Gateway> gateways) {
this.gateways = gateways;
}
}
public class GatewayProvider implements Provider<GatewayContainer> {
#Override
public GatewayContainer get() {
try {
File file = new File(getClass().getResource("/gateways.json").toURI());
Type listType = new TypeToken<Set<Gateway>>() {
}.getType();
Set<Gateway> set = new Gson().fromJson(new FileReader(file), listType);
GatewayContainer container = new GatewayContainer();
container.setGateways(set);
return container;
} catch (URISyntaxException | FileNotFoundException e) {
e.printStackTrace();
}
return new GatewayContainer();
}
}
Configure and use your context:
public class AppRunner {
GatewayContainer container;
public AppRunner(GatewayContainer container) {
this.container = container;
}
public static void main(String[] args) {
EasyDI context = new EasyDI();
context.bindProvider(GatewayContainer.class, new GatewayProvider());
final AppRunner runner = context.getInstance(AppRunner.class);
}
}
Afterwards, you will get the AppRunner with all the injected dependencies.
Note: There is no usage of any sort of #Inject(or similar) annotation, because EasyDI not requires it by default

Related

How do I build a custom CheckBoxGroup field in Magnolia 6.2 CMS?

When I create the CustomCheckBoxGroup field, all options of the field are displayed. But if I select one or more options and then save them, they are not saved in Magnolia. I suspect that wrong dataProvider is initialized (JcrDatasource instead of OptionListProvider).
form:
properties:
checkboxGroup:
$type: customCheckBoxGroupField
What is wrong here?
public class CustomCheckBoxGroupFieldFactory<T> extends AbstractOptionGroupFieldFactory<CustomCheckBoxGroupFieldDefinition<T>, Set<T>> {
private final static String STORES = "stores";
public CustomCheckBoxGroupFieldFactory(
CustomCheckBoxGroupFieldDefinition<T> definition,
ComponentProvider componentProvider,
SelectFieldSupport<Set<T>> selectFieldSupport) {
super(definition, componentProvider, selectFieldSupport);
}
#Override
public CheckBoxGroup<T> createFieldComponent() {
CheckBoxGroup<T> items = new CheckBoxGroup<T>("Test");
items.setItems((Collection)(this.selectStores()));
items.setItemCaptionGenerator(
item -> ((Option) item).getName() //((Option) item).getLabel()
);
return items;
}
public HasValue<Set<T>> createField() {
return this.createFieldComponent();
}
private Collection<Option> selectStores() {
List<Option> options = new ArrayList<>();
try {
Session session = MgnlContext.getJCRSession(STORES);
Node parent = session.getNode("/");
for (Node storeNode : NodeUtil.getNodes(parent, NodeTypes.Content.NAME)) {
if (storeNode.hasProperty(PROPERTY_NAME_DISPLAY_NAME)) {
Option option = new Option();
option.setValue(storeNode.getIdentifier());
option.setLabel(storeNode.getProperty(PROPERTY_NAME_DISPLAY_NAME).getString());
option.setName(storeNode.getProperty(PROPERTY_NAME_DISPLAY_NAME).getString());
options.add(option);
}
}
} catch (RepositoryException e) {
log.error("Cannot preselect already configured workspaces.", e);
}
return options;
}
}

How to see and add to logs in libraries which are referenced in java spring boot project?

I have a spring boot application that uses the libraries: SimpleMessageListenerContainer (https://docs.spring.io/spring-amqp/docs/current/api/org/springframework/amqp/rabbit/listener/SimpleMessageListenerContainer.html) and SimpleMessageListenerContainerFactory (https://www.javadoc.io/static/org.springframework.cloud/spring-cloud-aws-messaging/2.2.0.RELEASE/org/springframework/cloud/aws/messaging/config/SimpleMessageListenerContainerFactory.html). The application uses ASW SQS and Kafka but I'm experiencing some out of order data and trying to investigate why. Is there a way to view logging from the libraries? I know I cannot edit them directly but when I create the bean, I want to be able to see the logs from those two libraries and if possible to add to them.
Currently I'm setting up the bean in this way:
#ConditionalOnProperty(value = "application.listener-mode", havingValue = "SQS")
#Component
public class SqsConsumer {
private final static Logger logger = LoggerFactory.getLogger(SqsConsumer.class);
#Autowired
private ConsumerMessageHandler consumerMessageHandler;
#Autowired
private KafkaProducer producer;
#PostConstruct
public void init() {
logger.info("Loading SQS Listener Bean");
}
#SqsListener("${application.aws-iot.sqs-url}")
public void receiveMessage(String message) {
byte[] decodedValue = Base64.getDecoder().decode(message);
consumerMessageHandler.handle(decodedValue, message);
}
#Bean
public SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory(AmazonSQSAsync amazonSqs) {
SimpleMessageListenerContainerFactory factory = new SimpleMessageListenerContainerFactory();
factory.setAmazonSqs(amazonSqs);
factory.setMaxNumberOfMessages(10);
factory.setWaitTimeOut(20);
logger.info("Created simpleMessageListenerContainerFactory");
logger.info(factory.toString());
return factory;
}
}
For reference, this is a method in the SimpleMessageListenerContainer. It is these logs which I would like to investigate and potentially add to:
#Override
public void run() {
while (isQueueRunning()) {
try {
ReceiveMessageResult receiveMessageResult = getAmazonSqs()
.receiveMessage(
this.queueAttributes.getReceiveMessageRequest());
CountDownLatch messageBatchLatch = new CountDownLatch(
receiveMessageResult.getMessages().size());
for (Message message : receiveMessageResult.getMessages()) {
if (isQueueRunning()) {
MessageExecutor messageExecutor = new MessageExecutor(
this.logicalQueueName, message, this.queueAttributes);
getTaskExecutor().execute(new SignalExecutingRunnable(
messageBatchLatch, messageExecutor));
}
else {
messageBatchLatch.countDown();
}
}
try {
messageBatchLatch.await();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
catch (Exception e) {
getLogger().warn(
"An Exception occurred while polling queue '{}'. The failing operation will be "
+ "retried in {} milliseconds",
this.logicalQueueName, getBackOffTime(), e);
try {
// noinspection BusyWait
Thread.sleep(getBackOffTime());
}
catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
}
SimpleMessageListenerContainer.this.scheduledFutureByQueue
.remove(this.logicalQueueName);
}
How would I be able to see all of that logging from where I create the bean?
Any help would be much appreciated!

SolrHealthIndicator without deprecated CompositeHealthIndicator

I've tried to upgrade Spring Boot to 2.2.4.RELEASE version. Everzthing if fine exept problem with CompositeHealthIndicator which is deprecated.
I have this bean method
#Autowired
private HealthAggregator healthAggregator;
#Bean
public HealthIndicator solrHealthIndicator() {
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
composite.addHealthIndicator("solr1", createHealthIndicator(firstHttpSolrClient()));
composite.addHealthIndicator("solr2", createHealthIndicator(secondHttpSolrClient()));
composite.addHealthIndicator("querySolr", createHealthIndicator(queryHttpSolrClient()));
return composite;
}
private CustomSolrHealthIndicator createHealthIndicator(SolrClient source) {
try {
return new CustomSolrHealthIndicator(source);
} catch (Exception ex) {
throw new IllegalStateException("Unable to create helthCheckIndicator for solr client instance.", ex);
}
}
That registers HealthIndicator for 3 instances of SOLR (2 indexing, 1 for query). Everything worked fine until Spring Boot update. After update the method CompositeHealthIndicator.addHealthIndicator is not present, the whole class is marked as Deprecated.
The class which is created in createHealthIndicator is like this:
public class CustomSolrHealthIndicator extends SolrHealthIndicator {
private final SolrClient solrClient;
public CustomSolrHealthIndicator(SolrClient solrClient) {
super(solrClient);
this.solrClient = solrClient;
}
#Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
if (!this.solrClient.getClass().isAssignableFrom(HttpSolrClient.class)) {
super.doHealthCheck(builder);
}
HttpSolrClient httpSolrClient = (HttpSolrClient) this.solrClient;
if (StringUtils.isBlank(httpSolrClient.getBaseURL())) {
return;
}
super.doHealthCheck(builder);
}
}
Is there any easy way to transform the old way how to register the instances of SOLR i want to check if they are up or down at Spring Boot version 2.2.X?
EDIT:
I have tried this:
#Bean
public CompositeHealthContributor solrHealthIndicator() {
Map<String, HealthIndicator> solrIndicators = Maps.newLinkedHashMap();
solrIndicators.put("solr1", createHealthIndicator(firstHttpSolrClient()));
solrIndicators.put("solr2", createHealthIndicator(secondHttpSolrClient()));
solrIndicators.put("querySolr", createHealthIndicator(queryHttpSolrClient()));
return CompositeHealthContributor.fromMap(solrIndicators);
}
private CustomSolrHealthIndicator createHealthIndicator(SolrClient source) {
try {
return new CustomSolrHealthIndicator(source);
} catch (Exception ex) {
throw new IllegalStateException("Unable to create healthCheckIndicator for solr client instance.", ex);
}
}
The CustomSolrHealthIndicator has no changes against start state.
But I cannot create that bean. When calling createHealthIndicator I am getting NoClassDefFoundError
Does anyone know where the problem is?
Looks like you can just use CompositeHealthContributor. It's not much different from what you have already. It appears something like this would work. You could override the functionality to add them one at a time if you'd like, also, which might be preferable if you have a large amount of configuration. Shouldn't be any harm with either approach.
#Bean
public HealthIndicator solrHealthIndicator() {
Map<String, HealthIndicator> solrIndicators;
solrIndicators.put("solr1", createHealthIndicator(firstHttpSolrClient()));
solrIndicators.put("solr2", createHealthIndicator(secondHttpSolrClient()));
solrIndicators.put("querySolr", createHealthIndicator(queryHttpSolrClient()));
return CompositeHealthContributor.fromMap(solrIndicators);
}
Instead of deprecated CompositeHealthIndicator#addHealthIndicator use constructor with map:
#Bean
public HealthIndicator solrHealthIndicator() {
Map<String, HealthIndicator> healthIndicators = new HashMap<>();
healthIndicators.put("solr1", createHealthIndicator(firstHttpSolrClient()));
healthIndicators.put("solr2", createHealthIndicator(secondHttpSolrClient()));
healthIndicators.put("querySolr", createHealthIndicator(queryHttpSolrClient()));
return new CompositeHealthIndicator(this.healthAggregator, healthIndicators);
}

Testing a method by overriding a private class variable as an initial step before refactoring

What is the best way of writing a unit test for a method, such as my setProperties (see below), that uses a private configuration variable (config). I tried but failed to override it using reflection and Makito, but without success. I realize that changing the design to make the code easier to test is best, but I want to created some unit tests before I refactor the code.
public class MainClass {
private final java.lang.String config = "app.properties";
public TestClass() {
try {
setProperties();
} catch (Exception e) {
e.printStackTrace();
}
}
public void setProperties() throws Exception {
try {
InputStream input = new BufferedInputStream(new FileInputStream(config));
..
..
} catch (Exception exception) {
throw exception;
}
}
}
Do refactor a tiny bit by extracting a method with a parameter that takes an input stream. Call this new method (probably package-protected) from the old one. Write tests against the new method. Then do more refactorings.
This is an indication of a broken design; don't hard-code things like this. Better yet, determine what the appropriate responsibility for this class is, and, in decreasing order of preference:
pass in an object with the configuration properties, strongly typed
pass in a Map with the configuration properties
pass in an InputStream for the properties file
As File objects are never available from a jar, you shouldn't ever make interfaces like this more specific than InputStream or Reader, so that you can always pass in streams from your jar classpath.
So you can use Properties class in Java for this. Please have a look at this code.
public class PropertyUtil {
private static Properties prop;
private static Logger logger = Logger.getLogger(PropertyUtil.class);
private PropertyUtil() {
}
public void setProperty() {
String filePath = System.getenv("JAVA_HOME") + "/lib" + "/my_file.properties";
prop = new Properties();
try (InputStream input = new FileInputStream(filePath)) {
prop.load(input);
} catch (IOException ex) {
logger.error("Error while reading property file " + ex);
}
}
public static String getProperty(String key) {
if (prop.containsKey(key)) {
return prop.getProperty(key);
} else {
return null;
}
}
public static <T> T getProperty(String key, Class<T> claz) {
if (claz.getName().equals(Integer.class.getName())) {
return claz.cast(Integer.parseInt(prop.getProperty(key)));
}
if (claz.getName().equals(Long.class.getName())) {
return claz.cast(Long.parseLong(prop.getProperty(key)));
}
if (claz.getName().equals(Boolean.class.getName())) {
return claz.cast(Boolean.parseBoolean(prop.getProperty(key)));
}
if (claz.getName().equals(Double.class.getName())) {
return claz.cast(Double.parseDouble(prop.getProperty(key)));
}
if (claz.getName().equals(String.class.getName())) {
return claz.cast(prop.getProperty(key));
}
return null;
}

Cannot find a matching 0-argument function named {NAME}METHOD() in built-in template rule

I registered ExtensionFunctionDefinition without parameters, but can not call it.
What is wrong and how this can be fixed?
Looks like function is unregistered.
Here is the code:
Saxon
...<saxon.he.version>9.7.0-3</saxon.he.version>...
<groupId>net.sf.saxon</groupId>
<artifactId>Saxon-HE</artifactId>...
Exception
Error at char 29 in xsl:value-of/#select on line 23 column 71
XTDE1425: Cannot find a matching 0-argument function named {http://date.com}getFormattedNow()
in built-in template rule
XSLT
<xsl:stylesheet ...
xmlns:dateService="http://date.com"
exclude-result-prefixes="dateService" version="1.0">
...
<xsl:value-of select="dateService:getFormattedNow()"/>
ExtensionFunctionDefinition
public class DateExtensionFunction extends ExtensionFunctionDefinition {
public StructuredQName getFunctionQName() {
return new StructuredQName("", "http://date.com", "getFormattedNow");
}
public SequenceType[] getArgumentTypes() {
return new SequenceType[]{SequenceType.OPTIONAL_STRING};
}
public SequenceType getResultType(SequenceType[] sequenceTypes) {
return SequenceType.SINGLE_STRING;
}
public boolean trustResultType() {
return true;
}
public int getMinimumNumberOfArguments() {
return 0;
}
public int getMaximumNumberOfArguments() {
return 1;
}
public ExtensionFunctionCall makeCallExpression() {
return new ExtensionFunctionCall() {
public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
return new StringValue("TEST");
}
};
}
}
Transformer
Processor processor = new Processor(false);
Configuration configuration = new Configuration();
TransformerFactoryImpl transformerFactory = new TransformerFactoryImpl();
processor.registerExtensionFunction(new DateExtensionFunction());
configuration.setProcessor(processor);
transformerFactory.setConfiguration(configuration);
//...newTransformer
The relationship between your Processor, Configuration, and TransformerFactory are wrong.
It's best to think of the Configuration as holding all the significant data, and the Processor and TransformerFactory as API veneers on top of the Configuration.
When you create a Processor, it creates its own Configuration underneath. Ditto for the TransformerFactoryImpl. So you have three Configuration objects here, the two that Saxon created, and the one that you created. The extension function is registered with the Configuration that underpins the (s9api) processor, which has no relationship with the one that you are using with the JAXP TransformerFactory.
I would recommend that you either use JAXP or s9api, but avoid mixing them. If you want to use JAXP, do:
TransformerFactoryImpl transformerFactory = new TransformerFactoryImpl();
Configuration config = transformerFactory.getConfiguration();
config.registerExtensionFunction(new DateExtensionFunction());
Note that from Saxon 9.7, the JAXP interface is implemented as a layer on top of the s9api interface.
Here is some code that works (tested under Saxon 9.7 HE). I don't know why yours doesn't: please put together a complete program that illustrates the problem.
import ....;
public class ExtensionTest extends TestCase {
public class DateExtensionFunction extends ExtensionFunctionDefinition {
public StructuredQName getFunctionQName() {
return new StructuredQName("", "http://date.com", "getFormattedNow");
}
public net.sf.saxon.value.SequenceType[] getArgumentTypes() {
return new net.sf.saxon.value.SequenceType[]{net.sf.saxon.value.SequenceType.OPTIONAL_STRING};
}
public net.sf.saxon.value.SequenceType getResultType(net.sf.saxon.value.SequenceType[] sequenceTypes) {
return net.sf.saxon.value.SequenceType.SINGLE_STRING;
}
public boolean trustResultType() {
return true;
}
public int getMinimumNumberOfArguments() {
return 0;
}
public int getMaximumNumberOfArguments() {
return 1;
}
public ExtensionFunctionCall makeCallExpression() {
return new ExtensionFunctionCall() {
public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
return new StringValue("TEST");
}
};
}
}
public void testIntrinsicExtension() {
try {
TransformerFactoryImpl factory = new TransformerFactoryImpl();
factory.getConfiguration().registerExtensionFunction(new DateExtensionFunction());
String xsl = "<e xsl:version='3.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' " +
"result='{Q{http://date.com}getFormattedNow()}'/>";
Templates t = factory.newTemplates(new StreamSource(new StringReader(xsl)));
StringWriter sw = new StringWriter();
t.newTransformer().transform(new StreamSource(new StringReader("<a/>")), new StreamResult(sw));
System.err.println(sw.toString());
} catch (TransformerConfigurationException tce) {
tce.printStackTrace();
fail();
} catch (TransformerException e) {
e.printStackTrace();
fail();
}
}
}
The output is:
<?xml version="1.0" encoding="UTF-8"?><e result="TEST"/>
Solution (works only for Saxon 9.4):
#Override
public ExtensionFunctionCall makeCallExpression() {
return new ExtensionFunctionCall() {
#Override
#SuppressWarnings("unchecked")
public SequenceIterator call(SequenceIterator[] arguments, XPathContext context) throws XPathException {
return SingletonIterator.makeIterator(StringValue.makeStringValue("TEST"));
}
};
}

Categories

Resources