Liferay Configuration Action Class - Spring dependency Injection - java

In Liferay, the Configuration Action class is defined in liferay-portlet.xml
The problem is, if I use any spring dependency injection, it's not working.
<portlet>
<portlet-name>search</portlet-name>
<icon>/icon.png</icon>
<configuration-action-class>com.mypack.MyConfigurationAction</configuration-action-class>
<header-portlet-css>/css/main.css</header-portlet-css>
<footer-portlet-javascript>/js/main.js</footer-portlet-javascript>
<css-class-wrapper>search-portlet</css-class-wrapper>
<add-default-resource>true</add-default-resource>
</portlet>
Action Class implementation
public class MyConfigurationAction extends DefaultConfigurationAction {
private #Value("${test.property1}") String property1;
private #Value("${test.property2}") String property2;
}
How do I inject these properties into this Action class, without using ClassPathXmlApplicationContext and hard coding spring.xml file in the class

There are two ways to save preferences in portlet development[in liferay],
Through liferay specific way, which uses liferay-portlet.xml entry . cant be managed with spring.
JSR-286[portal agnostic], portlet EDIT mode.
While developing portlet with Spring MVC framework, its advisable to use portlet EDIT mode.
In Spring MVC portlet framework, you can map portlet requests by portlet mode.
For Example: Create controller class as below which will map to EDIT mode requests.
#Controller
#RequestMapping("EDIT")
public class PreferencesController
with two methods, one method with annotation #RenderMapping, responsible for view and other method with annotation #ActionMapping/#RequestMapping responsible for storing preferences.
Hope this would help.

Try this
portlet.xml
<supports>
.....
<portlet-mode>edit</portlet-mode>
</supports>
Controller class
#Controller
#RequestMapping(value = "EDIT")
public class XYZ{
}
HTH

First of all, "Configuration" is NOT "Edit" mode. If you enable Edit mode (as suggested by others), you'll get "Preferences" button in your portlet menu. It is a Liferay feature that you can override as per your requirement.
I have Not tried this myself but you can try to use #Autowired to AutoWire your MyConfigurationAction class (and possibly use #Required annotation if needed?). Don't forget to put <context:annotation-config/> in your applicationContext.xml file, if not already done.

Related

Can I use #Profile Tags on Components without creating an #Configuration class in Spring Boot or a Web.xml

I've managed to avoid developing any xml files so far for my Spring Boot Maven project (apart from the pom) with them all being generated on compile and I was hoping to keep this way by defining my profiles within the run commands as specified here.
By simply using #ComponentScan my main class to enable the scanning of components and tagging my DAO as #Repository I have successfully managed to autowire my UserDAOmySQLImpl class (which inherits UserDAO).
#Autowired
private UserDAO userDAO;
Then looking forward to add a second DAO for when in Live where we use a db8 implementation I need the application to figure out which UserDAO implementation needs to be used. Profiles seemed to be the answer.
After some reading, it seemed that mostly I need to add in some sort of configuration classes and external xml to manage these profiles and components, though this seems messy and I am hoping unnecessary.
I have tagged by two implementations as so:
#Repository
#Profile("dev")
public class UserDAOmySQLImpl implements UserDAO {...
-
#Repository
#Profile("dev")
public class UserDAOdb8Impl implements UserDAO {...
And then set the active profile through the Maven goals as specified here in hope that this would be a nice clean solution so I could specify the active profile within the goal for our dev and live build scripts respectively.
My current goal for testing is:
spring-boot:run -Drun.profiles=dev
However, when building I receive an error that both beans are being picked up.
*No qualifying bean of type [com.***.studyplanner.integration.UserDAO] is defined: expected single matching bean but found 2: userDAOdb8Impl,userDAOmySQLImpl*
Questions
Is the profile set in the Maven Goal the one being checked against when using the #Profile tag?
Why is Spring picking up both implementations, when if the profile isn't being set properly surely neither implementation should be selected?
Any suggestions on a nice clean way to achieve what I'm looking for?
Bonus
I would really like to set the profile within the app, as it would be easy to simply check whether an environment file exists to decide which profile to use, though I'm struggling to replicate this (found within Spring Profiles Examples
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//Enable a "live" profile
context.getEnvironment().setActiveProfiles("live");
context.register(AppConfig.class);
context.refresh();
((ConfigurableApplicationContext) context).close();
}
in to the unusual main class in the application I am working on which I am hesitant to play around with.
public class StudyPlannerApplication {
...
public static void main(String[] args) {
SpringApplication.run(StudyPlannerApplication.class, args);
}
...
}
Any help would much appreciated.
Cheers,
Scott.
Silly me, proof reading the question I noticed that a bad copy & paste job meant that both DAO implementations had the #profile set to "Dev". After changing the db8 DAO to #Profile("live") the above works fine.
So to choose your repository based on profile is actually quite easy when using maven and spring boot.
1) Ensure your main class has the #ComponentScan annotation
#ComponentScan
public class StudyPlannerApplication {
2) Tag your components with the #Profile tags according to which profile you would like them sectected for
#Repository
#Profile("dev")
public class UserDAOdb8Impl implements UserDAO {...
-
#Repository
#Profile("live")
public class UserDAOdb8Impl implements UserDAO {...
3)
Send your profile in with your maven goal
spring-boot:run -Drun.profiles=dev
Hopefully this will help others, as I haven't seen a full example of using the autowiring profiles as I have done elsewhere on the web

<context:componentscan> how does it work?

Can "context: componentscan" scan custom annotations? If so, where does it store the scanned beans in application context after scanning? How can I access the results?
We register beans or components in XML configuration file. So Spring can detect those beans, components. Spring also support to auto scan, detect and instantiate beans from pre-defined project package via the annotation. So we don't have to declare in the configuration anymore. For example:
<context:component-scan base-package="abc.controller, abc.service" />
in your Controller or Service, you just have to add annotation like:
#Controller
public class SampleController
#Service
public class SampleService
Spring will know that your SampleController and SamplerService and you can use it as you want.
Some detail here: http://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch06s02.html

ViewScoped is working like ApplicationScoped

Hi all please before mark my question as duplicated I want to tell you that I made a huge research including those questions
Unexpected survival of a #ViewScoped bean
ViewScope not destroy
ViewScope beans behaves like it has application scope
But I´m asking because I´m using a diferent version of the tools and don´t found any possible solution to my problem.
This is the situation
I´m using primefaces 5.1 Spring 3.1.0.RELEASE, Spring security 3.1.1.RELEASE,JSF 2.2.8 and Apache 8 .
I enter to my app and start to work, but if other person in other machine with different browser and different session id enter to the same xhtml view, he will see the data that I wrote. So looks like all the controllers of my aplication are in #ApplicationScoped but all of them are #ViewScoped
In the investigation process I did a debug and in the first acces to a view, that view call the controller, but when other person acces to the same view the controller is not called, I don´t know how the view gets the data if doesn´t call the controller.
This is one of my controllers
import javax.faces.bean.ViewScoped;
#ViewScoped
#ManagedBean
#Controller
public class CreateRepoController {
#Autowired
private IRepoFacade repositorioFacade;
#Autowired
private ISecureFacade secureFacade;
//Methods
}
Note I also tried with import org.omnifaces.cdi.ViewScoped; instead the javax viewScoped
the view is called from a menu
<p:submenu label="Repo">
<p:menuitem value="Create" url="/secured/createRepo.xhtml" />
</p:submenu>
All the application have the same behavior is not just one view
Thanks in advance for your time and answers
You have posted very few details. So I'll give you some hints on how to troubleshoot.
test your beans with session scope instead of view scope and check if you get a different behavior. view scope is sometimes tricky and may not work as expected. if you get a different request per user then investigate what do you have in your bean that is trigering the wrong behavior when switched to view scope.
avoid mixing CDI annotations with JSF annotations. I've had interoperativity problems when merging them.
Omnifaces ViewScope is a CDI annotation for JSF 2.0/2.1. In JSF 2.2 there is a standard annotation for CDI as part of Java EE 7 in javax.faces.view package.
try switching to all CDI if ypu still have problems. Instead of #ManagedBean use #Named and #ViewScoped from javax.faces.view.
Try do remove #Controller annotation and extend SpringBeanAutowiringSupport to enable the injections.
#ManagedBean
#ViewScoped
public class CreateRepoController extends SpringBeanAutowiringSupport implements Serializable {
#Autowired
private IRepoFacade repositorioFacade;
#Autowired
private ISecureFacade secureFacade;
//Methods
}
It works with with Spring 4.0.6 and PF 5.1.

Failing to #AutoWire a member in a #WebServlet

I can't seem to get my servlet's fields to #AutoWire; they end up null. I have a pure annotation-configured webapp (no XML files). My servlet looks like this:
#WebServlet("/service")
public
class
SatDBHessianServlet
extends HttpServlet
{
#Autowired protected NewsItemDAO mNewsItemDAO;
}
Other #AutoWired things seem to work fine, both #Service objects and #Repository objects. But not this one, and I can't figure out why. I even tried adding its package to the ComponentScan(basePackages) list for my other classes.
Additional Info:
I added the following to my servlet’s init() method, and everything seemed to wire up properly, but I'm confused as to why Spring can't wire it up without that.
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, inConfig.getServletContext());
Servlet are web components that are not being created by Spring container, based on that lifecycle is not managed by the container and a lot of stuff that spring provides such as autowired or aspect can not run from them.
You need to indicate to the spring container that a component was created outside of IoC container and need to be part of it.
And as the API said SpringBeanAutowiringSupport is for:
Convenient base class for self-autowiring classes that gets
constructed within a Spring-based web application
This generic servlet base class has no dependency on the Spring ApplicationContext concept.
There is another way to indicate servlets being created by spring container using an interface
Spring MVC uses the DispatcherServlet for handling all requests in a Servlet environment.
The DispatcherServlet accordingly forwards the requests to the appropriate #Controller class (if any) based on the #RequestMapping on that class and/or it's methods.
What is happening in your Servlet is that it is not managed by Spring (but by the Servlet container) and there for no injection of dependencies is occurring.

How to resolve Spring #Value annotations in a web servlet?

I have a servlet packaged in an ear file, and I cannot get it to resolve #Value annotated properties.
The app is in two parts: a servlet packaged in a war file, which is then included by different applications packaged in an ear file. The application supplies the implementation of a class (ResourceManager) defined by an interface in the servlet, and also the property file containing the value of the property for the #Value annotated field.
The war file contains:
web.xml:
<servlet>
<servlet-class>... extends MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/spring-ws.xml
classpath:/spring-app.xml
</param-value>
</init-param>
</servlet>
spring-ws.xml:
<context:annotation-config/>
<context:property-placeholder location="classpath:/spring-ws.properties"/>
The values in spring-ws.properties are referenced directly from spring-ws.xml, and all work fine.
The servlet class contains:
public class MyServlet extends MessageDispatcherServlet
#Autowired private ResourceManager resourceManager; // original annotation - got this to work
#Value("${app.name:}") private String appName; // added later - cannot get this to work
The app.name is optional, hence the trailing ":".
The ear file adds (packaged in a jar file in the ear lib directory):
spring-app.xml:
<context:property-placeholder location="classpath:/spring-app.properties"/>
<bean class="ResourceManagerImpl">
...
</bean>
spring-app.properties:
app.name=myApp
My first problem was with the #Autowired annotation: not sure I ever got to the bottom of that correctly, but I managed to get it to work by adding this code to the servlet:
#Override
protected void initFrameworkServlet() throws ServletException {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(getWebApplicationContext().getAutowireCapableBeanFactory());
bpp.processInjection(this);
}
This may not be the best way to fix this, but it was the first I found and it worked, so I used it and moved on. Then I added a #Value annotated field, and I can't find a way to make this work. The javadocs for AutowiredAnnotationBeanPostProcessor say that it should process #Value annotations as well, but it doesn't appear to: the appName property is always blank (presumably from the default).
I do have a workaround, using beans:
#Autowired #Qualifier("appName") private String appName;
<bean id="appName" class="java.lang.String">
<constructor-arg value="myApp"/>
</bean>
But I'm sure there must be a better way of doing this. I've read this post Difference between applicationContext.xml and spring-servlet.xml in Spring Framework, but I'm not sure it applies: I don't think I even have an application context here, just a servlet context defined in the web.xml for the (single) servlet class.
I've also seen this: Spring 3.0.5 doesn't evaluate #Value annotation from properties, which looks like the same problem, but the solution there appeared to be to move the "<context:property-placeholder>" from the application context to the servlet context, whereas as I said before, I don't think I even have an application context here...
Finally, I've had a quick go at simplifying the problem, removing the separate app and packaging everything in a single war, but I don't think that is the problem (although could obviously be wrong...): spring can resolve the class path for the app.xml file so it must be able to resolve the class path for the app.properties file.
Any help much appreciated, but I guess at this point it is a bit of an academic interest, as I have a workable solution (using beans) and the people that pay the bills don't really care how it works as long as it works! I'd just like to know so next time I don't have to copy (what I think is) a flawed workaround.
Using spring-ws-core 2.1.4, which pulls in spring 3.2.4 via maven dependencies.
Thanks
Update - Solved
Turns out the key to fixing this was M Deinum's comment that "You also have a duplicate element...". I had two separate PropertyPlaceholderConfigurers in the same context, so the second was ignored and its properties never used. There was no problem with the context itself (which is a servlet context not a root context), or the annotation processing.
So the fix was simply to remove the property placeholder from the spring-app.xml, and combine both property files in the spring-ws.xml
<context:property-placeholder
location="classpath:/spring-ws.properties,
classpath:/spring-app.properties"/>
TL;DR's answer here Spring: namespace vs contextConfigLocation init parameters in web.xml is also an awesome explanation of how the different contexts are processed!
Cheers,
Your servlet isn't a spring managed bean it is managed by the Servlet container. Hence the reason why #Autowired doesn't work in the first place. Spring will only process beans it knows.
Both the ContextLoaderListener and your servlet (basically any servlet that extends FrameworkServlet) have there own instance of an ApplicationContext. The one from the ContextLoaderListener is used as a parent by the context constructed in the servlet. So you indeed have an ApplicationContext in your servlet.
Now for this #Value to resolve properly you need to have a PropertyPlaceHolderConfigurer to resolve the placeholders. As PropertyPlaceHolderConfigurer is a BeanFactoryPostProcessor it only operates on beans in the same context, so the one loaded/configured by the ContextLoaderListener doesn't do anything for beans related to the context from your servlet. (Hence you need to add <context:property-placeholder ... /> element to the servlet context).
You also have a duplicate element in general you want to avoid that kind of situation as it will lead to problems at same time, either one overriding the other or you will get exceptions that placeholders cannot be resolved by either one of the PropertyPlaceHolderConfigurer
Your init code is also overly complex and you don't need the AutowiredAnnotationBeanPostProcessor simply use the available methods.
getWebApplicationContext().getAutowireCapableBeanFactory().autowireBean(this);
Which also does more then what only the AutowiredAnnotationBeanPostProcessor does.
for #Autowired annotation, you just need to have a class with those annotations:
#Configuration
#PropertySource("classpath:myFile.properties")
#ComponentScan("com.company.package")
The #Configuration is used by Spring to tell it's a class for configuration (it replaces the xml files that you talked about, xml conf file is Spring 2, annotation is new with Spring 3 and 4)
The #ComponentScan manage the IOC (#Autowire)
The #PropertySource load the prop file
Then the class to initialized at startup with the prop file:
#Component("myFileProperties")
public class MyFileProperties{
#Value("${app.name}")
private String appName;
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName= appName;
}
}
Take a look here if you want it can inspire you (a conf file in a web context that I developped): https://github.com/ebrigand/RSSLiker/blob/master/src/main/java/com/mrm/rss/init/WebAppConfig.java
There is a workaround for this.
You can create a static field in a java class to store the value in.
public class SystemPropertyHelper {
public static String propertyValue;
}
And create a java class annotated with #Service or #Component, which you can inject the property in, and pass this value to the static field.
#Component
public class SpringBeanClass {
#Value("${propertyName}")
private String propertyValue;
#PostConstruct
public void init() {
SystemPropertyHelper.propertyValue = propertyValue;
}
}
This way you can always access the property value through the static field SystemPropertyHelper.propertyValue

Categories