My meta listener code
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#KafkaListener(containerFactory = "myListenerContainerFactory", autoStartup = "false")
public #interface mylistener1 {
#AliasFor(annotation = KafkaListener.class, attribute = "topics")
String[] topics();
String myattr() default "";
}
Consume method:
#Service
Class ConsumerService(){
#mylistener1(topics = "new.topic",myattr="new.myatr.topic")
public void consume(String message) {
LOG.info("consumer-> " + message);
}
}
I tried to get value from ApplicationContext, but it was not getting the listener.
#Autowired
ApplicationContext ctx;
Map<String, Object> allBeansWithNames = ctx.getBeansWithAnnotation(mylistener1.class);
allBeansWithNames - 0, and I am not getting class list which is having #mylistener1 annotation`your text`
I want to implement beanpostprocessor to check myattr at runtime and use it to send message`
getBeansWithAnnotation() will only find beans with classes that are so annotated (or #Bean factory methods). It doesn't look at the class methods.
Take a look at KafkaListenerAnnotationBeanPostProcessor for an example of a BPP that examines annotations and their attributes.
I've been using '#KafkaListener' at method level to create consumers. Now, I'm trying to create my own custom annotation by extending '#KafkaListener' and limit the no of attributes (for example, because of some reasons, I don't want to expose attributes like 'errorHandler' 'containerGroup' etc ). Now my question is, to implement this, is there any option to extend the existing '#KafkaListener' ? Please suggest.
Yes, it's quite easy.
#SpringBootApplication
public class So61684460Application {
public static void main(String[] args) {
SpringApplication.run(So61684460Application.class, args);
}
#MyListener(topics = "so61684460", id = "so61684460")
public void listen(String in) {
System.out.println(in);
}
#Bean
public NewTopic topic() {
return TopicBuilder.name("so61684460").partitions(3).replicas(1).build();
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
#KafkaListener(concurrency = "${my.concurrency}")
#interface MyListener {
#AliasFor(annotation = KafkaListener.class, attribute = "id")
String id();
#AliasFor(annotation = KafkaListener.class, attribute = "topics")
String[] topics() default "";
}
As you can see, as well as restricting the visibility of some attributes, you can make them required (id above), change the default value, or set hard-coded or parameterized values in the invisible attributes (concurrency above).
This is described in the documentation.
Below is my code. I try to convert below code from asp.net to asp.net core. But in asp.net core in last line ConvertTo is showing error because Get(key) does not have definition of ConvertTo. Don't know what is the problem.
I am unable to find any solution how can i write below code in asp.net core?
public static T Get<T>(string key)
{
if (!Exists(key))
{
throw new ArgumentException(String.Format("No such key in the AppSettings: '{0}'", key));
}
return ConfigurationManager.AppSettings.Get(key).ConvertTo<T>(new CultureInfo("en-US"));
}
Thanks in advance.
I suggest you carefully reading the documentation first. In .NET Core the way how we work with configuration is changed significantly ( different source using, mapping to POCO objects , etc).
In your case you may simply use ConfigurationBinder’s GetValue<T> extension method instead of implementing own method for value conversion:
IConfiguration.GetValue - extracts the value with the specified key
and converts it to type T.
Configuration in .net Core is now built on top of POCO's or IOptions for the most part. You don't get individual keys but instead you build up settings classes. Previously you had to either build a CustomConfiguration class or you would prefix AppSettings to "group them". Not anymore! If you take the approach of using IOptions it works something like the following.
You have your appSettings.json look like the following :
{
"myConfiguration": {
"myProperty": true
}
}
You then make up a POCO that matches your configuration. Something like this :
public class MyConfiguration
{
public bool MyProperty { get; set; }
}
Then in your startup.cs you need to load your configuration into an options object. It will end up looking pretty similar to the following.
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyConfiguration>(Configuration.GetSection("myConfiguration"));
}
}
Then the DI is all set up to inject an IOptions objects. You would then inject it into a controller like so :
public class ValuesController : Controller
{
private readonly MyConfiguration _myConfiguration;
public ValuesController(IOptions<MyConfiguration> myConfiguration)
{
_myConfiguration = myConfiguration.Value;
}
}
There is other ways to do this that don't use the IOptions object and you only inject in the POCO to your controllers. Some people (including me) prefer this method. You can read more here : http://dotnetcoretutorials.com/2016/12/26/custom-configuration-sections-asp-net-core/
And of course the documentation link for the official docs are here : https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration
I am using this annotation within a Controller's method in one Spring Boot app.
#RequestMapping(value="/{x}/{y}/{filename:.*}", method = RequestMethod.GET)
All is working good and the last parameter can be any filename.
The problem is with urls where that filename ends with ".ico"...Spring is not sending the request to this method...my guess it is that it thinks a favicon itself.
How can I avoid this kind of conflict?
Thanks.
Have a look at Spring MVC #PathVariable with dot (.) is getting truncated, especially one of the latest answers regarding Spring 4.x
I found the solution. I just need to disable this setting inside the application.properties file
spring.mvc.favicon.enabled=false
This way the FaviconConfiguration bean from WebMvcAutoConfiguration does not satisfies the constraint, thus is not created:
#Configuration
#ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
public static class FaviconConfiguration implements ResourceLoaderAware {
private ResourceLoader resourceLoader;
#Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Integer.MIN_VALUE + 1);
/**THIS WAS THE CONFLICTIVE MAPPING IN MY CASE**/
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico", faviconRequestHandler()));
return mapping;
}
#Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
#Bean
public ResourceHttpRequestHandler faviconRequestHandler() {
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
requestHandler.setLocations(getLocations());
return requestHandler;
}
private List<Resource> getLocations() {
List<Resource> locations = new ArrayList<Resource>(CLASSPATH_RESOURCE_LOCATIONS.length + 1);
for (String location : CLASSPATH_RESOURCE_LOCATIONS) {
locations.add(this.resourceLoader.getResource(location));
}
locations.add(new ClassPathResource("/"));
return Collections.unmodifiableList(locations);
}
}
Source: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java
I am trying to add some functionality to json processing for some nodes. So I wrote custom servlet extended from SlingSafeMethodsServlet which I need to be executed when user makes GET for the following url : /data/events/any_sequence/any_sequence.json or /data/events/any_sequence/any_sequence.infinity.json or for example /data/events/any_sequence/any_sequence.2.json where any_sequence of course means any valid sequence of symbols.
The problem is that I cannot find in the sling docs how to map this template like urls.
I've been trying to set properties like this:
#Component
#Service
#Properties({
#Property(name = "sling.servlet.resourceTypes", value = "data/events/-/-"),
#Property(name = "sling.servlet.extensions", value = "json"),
#Property(name = "sling.servlet.methods", value = "GET"),
#Property(name = "service.description", value = "JSON advanced renderer")
})
But it didn't help. I checked felix console and found out that my service had started and running, so the problem is how to set url mappings. So my question is how to set url mapping in my case to invoke doGet of my custom servlet ?
Thanks.
As far as I understand CQ5 does not provide ability to map custom servlets on wildcard urls. The only way to accomplish goal similiar to one I needed is to use some unique for this servlet selector like this:
#Component
#Service
#Properties({
#Property(name = "sling.servlet.resourceTypes", value = "sling/servlet/default"),
#Property(name = "sling.servlet.extensions", value = "json"),
#Property(name = "sling.servlet.selectors", value = "advanced"),
#Property(name = "sling.servlet.methods", value = "GET"),
#Property(name = "service.description", value = "JSON advanced renderer")
})
This code means that if I'll try to make GET on some node with *.advanced.json selector and extension then request will be forwarded to my custom servlet.
See http://apache-sling.73963.n3.nabble.com/Register-servlet-for-subtree-td84106.html
I've solved this, exactly as the original poster was hoping. All other answers are effectively "it can't be done" or "here's a way to do it if you're willing to dirty up your clean RESTful API with selectors"
If you want to keep the clean API you envisioned, here's how. This works also for Apis without extensions, like /myservice/mythings/123123 , where 123123 is some dynamic ID
Create Two Files:
ResourceProvider
Servlet
The ResourceProvider
The purpose of this is only to listen to all requests at /data/events and then produce a "Resource" at that virtual path, which doesn't actually exist in the JCR.
#Component
#Service(value=ResourceProvider.class)
#Properties({
#Property(name = ResourceProvider.ROOTS, value = "data/events"),
#Property(name = ResourceProvider.OWNS_ROOTS, value = "true")
})
public class ImageResourceProvider implements ResourceProvider {
#Override
public Resource getResource(ResourceResolver resourceResolver, String path) {
AbstractResource abstractResource;
abstractResource = new AbstractResource() {
#Override
public String getResourceType() {
return TypeServlet.RESOURCE_TYPE;
}
#Override
public String getResourceSuperType() {
return null;
}
#Override
public String getPath() {
return path;
}
#Override
public ResourceResolver getResourceResolver() {
return resourceResolver;
}
#Override
public ResourceMetadata getResourceMetadata() {
return new ResourceMetadata();
}
};
return abstractResource;
}
#Override
public Resource getResource(ResourceResolver resourceResolver, HttpServletRequest httpServletRequest, String path) {
return getResource(resourceResolver , path);
}
#Override
public Iterator<Resource> listChildren(Resource resource) {
return null;
}
}
The Servlet
Now you just write a servlet which handles any of the resources coming from that path - but this is accomplished by handling any resources with the resource type which is produced by the ResourceProvider listening at that path.
#SlingServlet(
resourceTypes = TypeServlet.RESOURCE_TYPE,
methods = {"GET" , "POST"})
public class TypeServlet extends SlingAllMethodsServlet {
static final String RESOURCE_TYPE = "mycompany/components/service/myservice";
#Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
final String [] pathParts = request.getResource().getPath().split("/");
final String id = pathParts[pathParts.length-1];
response.setContentType("text/html");
PrintWriter out = response.getWriter();
try {
out.print("<html><body>Hello, received this id: " + id + "</body></html>");
} finally {
out.close();
}
}
}
This is the syntax I have used to accomplish similar tasks:
#Component(immediate = true, description = "JSON advanced renderer")
#Service(value = javax.servlet.Servlet.class)
#Properties(value = {
#Property(name = "sling.servlet.extensions", value = { "json" }),
#Property(name = "sling.servlet.methods", value = { "GET" }),
#Property(name = "sling.servlet.paths", value = {
"/data/events/any_sequence/any_sequence",
"/data/events/any_sequence/any_sequence.infinity",
"/data/events/any_sequence/any_sequence.2"
})
})
I had a similar problem and I needed wildcards I used
#Service
#Component
#Properties({
#Property(name = "sling.servlet.paths", value = "/bin/resolver/gb"),
#Property(name = "sling.servlet.extensions", value = "*")
})
public class Test extends SlingAllMethodsServlet {
#Override
public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.print("The path you used is:" + request.getPathInfo());
}
}
Call is [server]:[port]/bin/resolver/gb.[wildcard]
So what can be done is : [server]:[port]/bin/resolver/gb.en/something
Everything after "." is considered an extension so need to be handled in the servlet, but helped me achieve my requirement
It seems that the path of your servlet is same.Just selectors are varying. When used with path, other things are ignored in SlingServlet. So using something like this should serve the purpose:
#SlingServlet(paths = " /data/events/any_sequence/any_sequence", extensions = "json")
You would need to add /data in execution paths from Felix console(/system/console/configMgr) as is it not there by default in Apache Sling Servlet Resolver property
This can be accomplished using the desired external URI pattern by constructing a Sling mapping or Apache rewrite to effectively move the JSON extension to just after "data" in the URI, so the single servlet at "/data" ends up receiving the arbitrary path via the suffix of the request. If you are also using data from selectors, you'll need to move them along with the extension.