Having a maven module that declares a Qute template and REST endpoint to render it, I wanted to include this module on another maven project. The problem is, at it seems, the destination module does not compile since it does not have / find the template in it's resources/templates location (the template is included in the jar of the included module).
Is there any way to instruct Qute (at build time) to read templates from other locations or disable this build check (since the template is in the classpath at the correct location?
The only way I can make it work roght now is to copy my template to the destination project at resources/templates but it doesn't seem the right solution.
Thanks in advance
Yes, by default only the templates located in src/main/resources/templates are validated and can be injected.
You can parse any template content manually via Engine.parse() or even add a custom template locator via io.quarkus.qute.EngineBuilder.addLocator(), e.g. something like:
import io.quarkus.qute.EngineBuilder;
class MyEngineConfig {
void configureEngine(#Observes EngineBuilder builder) {
builder.addLocator(path -> Optional.of(new TemplateLocation() {
#Override
public Reader read() {
return new BufferedReader(new InputStreamReader(FlowChartResource.class.getResourceAsStream(path)));
}
#Override
public Optional<Variant> getVariant() {
return Optional.empty();
}
}));
}
}
The disadvantage is that Quarkus can't validate/inject such templates.
See also related issues https://github.com/quarkusio/quarkus/issues/12084 and https://github.com/quarkusio/quarkus/issues/10376.
Ok, I found another solution, using "low-level" engine instance and parsing the template "manualy":
engine.parse(new BufferedReader(new InputStreamReader(FlowChartResource.class.getResourceAsStream([PATH to My Template]))).lines().collect(Collectors.joining("\n")))
Related
I am building an app that mostly provide REST services, nothing fancy. since my data consumed by the app can have multiple languages I thought about using the bundle files.
I created 3 files, one with the default file name and another two with specific languages. The files created using intellij IDE I am using.
I followed this guide https://www.baeldung.com/java-resourcebundle however on each run I am getting:
MissingResourceException: Can't find bundle for base name tp_app_strings, locale en_US
I tried numerous articles but none of them seems to resolve the issue.
One fun fact is that if I am using the #Value("classpath:tp_app_strings.properties") on a 'Resource' field I am able to get a reference to that file, so it spring is able to find it.
Additional thing that I tried was to create a WEB-INF directory and place the files there (read it in some article) but still no positive affect
The project structure is quite straight forward:
Spring boot version 2.2 running tomcat.
Any suggeestions would be highly appriciated
You can load the .properties file to the application context using #PropertySource annotation instead using #Value to load the .properties file to a org.springframework.core.io.Resource instance.
The usage;
#Configuration
#PropertySource("classpath:tp_app_strings.properties")
public class DefaultProperties {
#Value("${property1.name}") // Access properties in the above file here using SpringEL.
private String prop1;
#Value("${property2.name}")
private String prop2;
}
You wouldn't need java.util.ResourceBundle access properties this way. Use different or same class to load other .properties files as well.
Update 1:
In order to have the functionality of java.util.ResourceBundle, you can't just use org.springframework.core.io.Resource class. This class or non of it sub-classes don't provide functions to access properties by its name java.util.ResourceBundle whatsoever.
However, if you want a functionality like java.util.ResourceBundle, you could implement something custom like this using org.springframework.core.io.Resource;
#Configuration
public class PropertyConfig {
#Value("classpath:tp_app_strings.properties")
private Resource defaultProperties;
#Bean("default-lang")
public java.util.Properties getDefaultProperties() throws IOException {
Properties props = new Properties();
props.load(defaultProperties.getInputStream());
return props;
}
}
Make sure to follow correct naming convention when define the property file as java.util.Properties#load(InputStream) expect that.
Now you can #Autowire and use this java.util.Properties bean wherever you want just like with java.util.ResourceBundle using java.util.Properties#getProperty(String) or its overloaded counterpart.
I think it's problem of you properties file naming convention. use underline "_" for specifying locale of file like
filename_[languageCode]_[regionCode]
[languageCode] and [regionCode] are two letters standard code that [regionCode] section is optional
about code abbrivation standard take a look on this question
in your case change file name to tp_app_strings_en_US.properties
I'm currently using swagger codegen using yml to generate my models However I have one field, that is a List<Object> that needs to have the #JacksonXmlElementWrapper(useWrapping = false). I can see the #JacksonXmlElementWrapper in POJO.mustache but not in model.mustache. Does anyone know what to add in the yaml file or anywhere else so that field gets generated with that annotation? Thanks. I'm using spring-java language with gradle. I need this to be generated during build. so minimal changes are preferred.
According to the readme on their git, Swagger Codegen mention ways to do this:
https://github.com/swagger-api/swagger-codegen/blob/master/README.md
since you're using gradle:
We can use a custom template for the code generation as follows:
// build.gradle
swaggerSources {
inputFile = file('{name of your file}.yaml')
xyz {
language = 'spring'
// template directory path
templateDir = file('templates/{name of your custom directory}')
}
}
inside templates/{name of your custom directory}, you can store your custom mustache files, as in your case, all you have to do is download the required spring templates from git (e.g. this link is for pojo.mustache)
and add in the required changes to the template, along with the project and libraries folder. Run gradle build and it should generate.
There is a much simpler workaround however, where you can just use your own pojo class and add this in your existing config.json and remove the earlier entry for that model in your yaml file (of course):
"importMappings" : {
"{replace with className}": "{replace with packageName}.{replace with className}"
}
Hope this helps.
I have an empty project created using GGTS, my app runs fine, I can see the views and navigate through them fine.
I need con call a REST api and consume the data provided, I read and added the necessary plug in to the BuildConfig file under the dependencies and then under the plugins section, none of them seem to work.
compile ":rest-client-builder:2.0.0"
I've created a controller, I'm not sure where to add the import here, so I've tried:
package myapp
class UserController {
def index() {
import grails.plugins.rest.client.RestBuilder
String url = "https://foo.com/batch/$id"
def resp = new RestBuilder().get(url) { header 'Authorization', 'Basic base64EncodedUsername&Password' }
render resp
}
but I get the error: unable to resolve class....
The API is returning JSON data, what I;ve done so far is just to create a new Grails project, add controller, add view and then the dependency.
I've cleaned an built the project several times but the error remains.
Thanks for your help.
Import goes at the top
After the package line
And before the class line
Also, depending on your version of grails, you might want to move of ggts... Not sure it's supported any more...
I think that the problem is in your build.gradle dependency definition.
Insert the next line to your build.gradle file and I think that will be helpful:
compile "org.grails.plugins:rest-client-builder:2.1.1"
For more information follow the grails plugin documentation.
Please be aware that all the business logic should be placed in the service layer and not in the controller. It is a best practice to keep your controller lean as possible.
Another best practice is to define the following line:
String url = "https://foo.com/batch/$id"
in the beggining of the controller as static final String
static final String url = "https://foo.com/batch/"
and add the $id when needed in the code.
Here is how that should be called inside the method:
String url = "${url}/{$id}"
I'm in the process of building a library which provides an opinionated configuration for applications which use our Spring Cloud Config/Eureka setup. The idea is to deliver this configuration as a custom starter with little or no spring cloud-related boilerplate in individual microservice apps.
At this point, the majority of the shared configuration that I want to put in this library consists of stuff in bootstrap.yml. I'd like to provide bootstrap.yml in my custom starter, but applications using the library still need to be able to provide their own bootstrap.yml, even if only so they can set their spring.application.name properly.
Due to the way bootstrap.yml is loaded from the classpath, Spring seems to ignore the one in the shared lib if the application has its own bootstrap.yml. I can't even use an ApplicationContextInitializer to customize the Environment because of the special way the bootstrap context treats ApplicationContextInitializers.
Does anyone have any recommendations for an approach that would work here? I want to provide a drop-in lib that makes our opinionated bootstrap config work without having to duplicate a boilerplate bootstrap.yml in all of our projects.
You can add a PropertySource in a shared library to the bootstrap properties by using the org.springframework.cloud.bootstrap.BootstrapConfiguration key in the META-INF/spring.factories file.
For example, you can create a library containing the following:
src/main/java/com/example/mylib/MyLibConfig.java
package com.example.mylib;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource("classpath:mylib-config.properties")
public class MyLibConfig {
}
src/main/resources/mylib-config.properties
eureka.instance.public=true
# or whatever...
src/main/resources/META-INF/spring.factories
org.springframework.cloud.bootstrap.BootstrapConfiguration=com.example.mylib.MyLibConfig
More details: http://projects.spring.io/spring-cloud/spring-cloud.html#_customizing_the_bootstrap_configuration
I was able to find a solution to this. The goals of this solution are:
Load the values from a yaml file in a shared library.
Allow applications using the library to introduce their own bootstrap.yml that is also loaded into the Environment.
Values in the bootstrap.yml should override values in the shared yaml.
The main challenge is to inject some code at the appropriate point in the application lifecycle. Specifically, we need to do it after the bootstrap.yml PropertySource is added to the environment (so that we can inject our custom PropertySource in the correct order relative to it), but also before the application starts configuring beans (as our config values control behavior).
The solution I found was to use a custom EnvironmentPostProcessor
public class CloudyConfigEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
private YamlPropertySourceLoader loader;
public CloudyConfigEnvironmentPostProcessor() {
loader = new YamlPropertySourceLoader();
}
#Override
public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) {
//ensure that the bootstrap file is only loaded in the bootstrap context
if (env.getPropertySources().contains("bootstrap")) {
//Each document in the multi-document yaml must be loaded separately.
//Start by loading the no-profile configs...
loadProfile("cloudy-bootstrap", env, null);
//Then loop through the active profiles and load them.
for (String profile: env.getActiveProfiles()) {
loadProfile("cloudy-bootstrap", env, profile);
}
}
}
private void loadProfile(String prefix, ConfigurableEnvironment env, String profile) {
try {
PropertySource<?> propertySource = loader.load(prefix + (profile != null ? "-" + profile: ""), new ClassPathResource(prefix + ".yml"), profile);
//propertySource will be null if the profile isn't represented in the yml, so skip it if this is the case.
if (propertySource != null) {
//add PropertySource after the "applicationConfigurationProperties" source to allow the default yml to override these.
env.getPropertySources().addAfter("applicationConfigurationProperties", propertySource);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
public int getOrder() {
//must go after ConfigFileApplicationListener
return Ordered.HIGHEST_PRECEDENCE + 11;
}
}
This custom EnvironmentPostProcessor can be injected via META-INF/spring.factories:
#Environment PostProcessors
org.springframework.boot.env.EnvironmentPostProcessor=\
com.mycompany.cloudy.bootstrap.autoconfig.CloudyConfigEnvironmentPostProcessor
A couple things to note:
The YamlPropertySourceLoader loads yaml properties by profile, so if you are using a multi-document yaml file you need to actually load each profile from it separately, including the no-profile configs.
ConfigFileApplicationListener is the EnvironmentPostProcessor responsible for loading bootstrap.yml (or application.yml for the regular context) into the Environment, so in order to position the custom yaml properties correctly relative to the bootstrap.yml properties precedence-wise, you need to order your custom EnvironmentPostProcessor after ConfigFileApplicationListener.
Edit: My initial answer did not work. I'm replacing it with this one, which does.
I'm working on CQ5 based app, which is a whole new area for me as I was mainly working on Spring based web-apps before.
The app is maven project based on Blue-prints archetype(http://www.cqblueprints.com/xwiki/bin/view/Blue+Prints/The+CQ+Project+Maven+Archetype).
Now I have a question, what is a standard approach to add some properties, that would normally go to config.properties (or alike) file in standard web-app. Properties that contain things like hostNames, accountNumbers and alike.
Cheers.
I'm not familiar with blueprints, but as I understand that's just a way to generate your CQ project structure, so I assume it doesn't have any real impact on how you manage configuration parameters.
CQ5 is based on Apache Sling, which uses the OSGi ConfigAdmin service for configurable parameters, and provides a few tools to make this easier.
You can see an example of that in the PathBasedDecorator Sling component, which uses the #Component annotation to declare itself as an OSGi component:
#Component(metatype=true, ...)
and later uses an #Property annotation to declare a multi-value configurable parameter, with default values:
#Property(value={"/content:2", "/sling-test-pbrt:2"}, unbounded=PropertyUnbounded.ARRAY)
private static final String PROP_PATH_MAPPING = "path.mapping";
That value is then read in the component's activate() method:
final Dictionary<?, ?> properties = componentContext.getProperties();
final String[] mappingList = (String[]) properties.get(PROP_PATH_MAPPING);
and the OSGi bundle that contains that component provides a metatype.properties file to define the name and label of the configurable parameter.
That's it - with this, Sling and the OSGi framework generate a basic config UI for the component, that you can access from /system/console/config, and manage your component's activation and reactivation automatically if configuration parameters change.
Those configurations can also come from the JCR repository, thanks to the Sling installer which picks them up there, you can find a number of those in folders named "config" under /libs and /apps in your CQ5 repository.
Another option is to use JCR content directly, depending on how your configurable parameters are used. You could tell your component that its config is under /apps/foo/myparameters in the repository (and make just that value configurable), and add JCR properties and child nodes under that node as needed, that your component can read. The disadvantage is that your #Component won't be restarted automatically when parameters change, as happens when using OSGi configurations directly.
Long explanation...hope this helps ;-)
Thanks a lot to Bertrand, your answer really pointed me in the right direction.
What I did was I created .ConfigService.xml for each of my ran modes, which looks like that:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:OsgiConfig"
myconfig.config="{String}My Value"/>
Then in my ConfigService looked like that:
#Component(immediate = true, metatype = true)
#Service(ConfigService.class)
public class ConfigService {
private Dictionary<String, String> properties;
#SuppressWarnings("unchecked")
protected void activate(ComponentContext context) {
properties = context.getProperties();
}
protected void deactivate(ComponentContext context) {
properties = null;
}
public String getProperty(String key) {
return properties.get(key);
}
}
Than I just use ConfigService if I need to get a config property accessing it using #Reference.
I hope that can help someone!
ConfigService example may not be the best approach since the ComponentContext should only be depended upon during component activation and deactivation.