We have beans implementing a interface, lets say MyServicesInterface which we can autowire in java as a list using
#Autowired
List{MyServicesInterface} myServices;
I would like to do this in a application context using sudo code like below.
<beans>
<util:list id="servicesList" class="ArrayList" autowire-interface="com.MyServicesInterface" />
<for-each service:services>
<bean id="{/remote + service.getname}" class="org....HttpInvoker">
<property name="serviceInterface" class="{#service.getInterface()}"
</bean>
</for-each>
<beans>
This kind of dynamic for-each bean of type {Interface} create a exporter bean would be a great pattern for exporting beans. I know this can be done in java but having some difficulties create a HttpInvoker in java for each beans. I doubt this can be done completely in a application context but perhaps there is a approach i am overlooking.
Any comments or suggests would be great.
Use a BeanDefinitionRegistryPostProcessor to create the BeanDefinitions for your HttpInvokerServiceExporters. Use an annotation to mark the services and define the interface you want to export.
e.g
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
for (String name : registry.getBeanDefinitionNames()) {
try {
BeanDefinition definition = registry.getBeanDefinition(name);
String beanClassName = defintition.getBeanClassName();
cls = classLoader.loadClass(beanClassName);
if(cls.isAnnotationPresent(ExportableService.class)){
//Get annotation and pull out serviceInterface
GenericBeanDefinition beanDef = new GenericBeanDefinition();
beanDef.setBeanClass(HttpInvokerServiceExporter.class);
MutablePropertyValues values = new MutablePropertyValues();
values.addPropertyValue("service", new RuntimeBeanReference(name));
values.addPropertyValue("serviceInterface", "service interface from annotation>);
beanDef.setPropertyValues(values);
// Bean name here should be e.g. /myService so its picked up by the BeanNameUrlHandlerMapping (if you so desire)
registry.registerBeanDefinition(<beanName>, beanDef);
}
}
} catch(ClassNotFoundException e){
// Handle exception
}
}
I doubt U can do it with xml context, but With java it is simple.
So In java I would do like that:
List<MyServicesInterface> mylist = applicationContext.getBeansOfType(MyServicesInterface.class).values();
ServiceInterface si = applicationContext.getBean(ServiceInterface.class);
for(MyServicesInterface mi: mylist){
si.callSomething(mi);
}
That how I would it in java.
Related
Main question:
Could someone advise how I use Spring to retrieve a value in my Java code to get values out of whatever properties file is specified by the context:property-placeholder tag in the applicationContext.xml?
Detailed description:
I am very new to Spring. Trying to use it to retreive configurable properties for my application to connect to a (configurable) JMS queue.
I have a Java/J2EE web application, using Spring.
In the src/main/resources/applicationContext.xml I have the following line:
<context:property-placeholder location="classpath:myapp.properties" />
Then in the src/main/resources/myapp.properties file I have the following lines:
myservice.url=tcp://someservice:4002
myservice.queue=myqueue.service.txt.v1.q
The problem that I am having, is that, for the life of me, I cannot figure out how to get the value of myservice.url that is defined in myapp.properties into my running java code.
I have tried a static function [to be called around the application]:
public static String getProperty(String propName)
{
WebApplicationContext ctx =
FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
Environment env = ctx.getEnvironment();
retVal = env.getProperty(propName);
return retVal;
}
However, while it returns a populated Environment object "env", the env.getProperty(propName) method returns null.
Ok - many, many thanks to Rustam who gave me the clues I needed. The way that I resolved this was thus:
In the src/main/resources/applicationContext.xml I have the following line:
<context:property-placeholder location="classpath:myapp.properties" />
Then in the src/main/resources/myapp.properties file I have the following lines:
myservice.url=tcp://someservice:4002
myservice.queue=myqueue.service.txt.v1.q
Then I have a class as follows:
package my.app.util;
import org.springframework.beans.factory.annotation.Value;
public class ConfigInformation
{
public ConfigInformation()
{
// Empty constructor needed to instantiate beans
}
#Value("${myservice.url}")
private String _myServiceUrl;
public String getMyServiceUrl()
{
return _myServiceUrl;
}
#Value("${myservice.queue}")
private String _myServiceQueue;
public String getMyServiceQueue()
{
return _myServiceQueue;
}
}
Then, I have the following declaration in my applicationContext.xml:
<bean name="configInformation" class="my.app.util.ConfigInformation">
</bean>
After I have done this, I am able to use the following lines in my code:
WebApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
ConfigInformation configInfo = (ConfigInformation) ctx.getBean("configInformation");
And consequently, the configInfo object, is populated with the values assigned to "myservice.url" and "myservice.queue" which can be retrieved with the method calls:
configInfo.getMyServiceUrl()
and
configInfo.getMyServiceQueue()
I want to call services dynamically so the service name will get as a string value, we can list all the services names in the grails project by using the code below.
import org.codehaus.groovy.grails.plugins.metadata.GrailsPlugin
for (type in ['service']) {
for (artifactClass in ctx.grailsApplication."${type}Classes") {
def clazz = artifactClass.clazz
def annotation = clazz.getAnnotation(GrailsPlugin)
if (annotation) {
println "$type $clazz.name from plugin '${annotation.name()}'"
}
else {
println "$type $clazz.name from application"
}
}
}
Here we will get artifactClass of the service.Is there any option to call the service by using this idea.Please help me.
You can get the bean for the service from the applicationContext
//inject application context bean
def applicationContext
//to use
applicationContext."${yourServiceName}".serviceMethod()
You can get the bean of your service this way:
import grails.util.Holders
...
YourService yourService =
(YourService)Holders.grailsApplication.mainContext["yourService"]
I have a Java application that parses an xml file of the following format.
<x:foo xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.hello.org/x/foo"
xsi:schemaLocation="http://www.hello.org/x/foo http://schemas.hello.org/x/foo-1.0.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<x:myBeans>
<bean id="my-bean-1" class="org.hello.sample.SampleClass1">
<!-- some properties goes here -->
</bean>
<bean id="my-bean-2" class="org.hello.sample.SampleClass2">
<!-- some properties goes here -->
</bean>
<import resource="sub-file.xml"/>
</x:myBeans>
<x:otherElements>
<!-- Some non-spring xml elements (which are not used by application logic) goes here -->
</x:otherElements>
</x:flow>
The myBeans tag contains a set of native spring beans and import statements. The otherElements tag contains a set of elements that are only used by a separate application which generates this file. My application is only interested in those spring beans and imports.
So I'm using the following custom bean parser to parse this file and add the spring bean definitions to the spring context.
public class MyBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
#Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
Element myBeansElement = DomUtils.getChildElementByTagName(element, "myBeans");
if (myBeansElement != null) {
// Parsing 'bean' elements
List<Element> beanElements = DomUtils.getChildElementsByTagName(myBeansElement, "bean");
for (Element beanElement : beanElements) {
BeanDefinitionHolder bdh = parserContext.getDelegate().parseBeanDefinitionElement(beanElement);
parserContext.getRegistry().registerBeanDefinition(bdh.getBeanName(), bdh.getBeanDefinition());
}
// Parsing 'import' statements
List<Element> importElements = DomUtils.getChildElementsByTagName(myBeansElement, "import");
for (Element importElement : importElements) {
// TODO
}
}
}
}
The parsing and adding spring beans (my-bean-1, my-bean-2) to the context works fine.
But how can I parse and add the beans defined in the file sub-file.xml (which has the same structure) to the spring context?
I was able to get the above working as follows.
// Parsing 'import' statements
List<Element> importElements = DomUtils.getChildElementsByTagName(myBeansElement, "import");
Resource currentResource = parserContext.getReaderContext().getResource();
for (Element importElement : importElements) {
String importPath = importElement.getAttribute("resource");
try {
Resource importResource = currentResource.createRelative(importPath);
parserContext.getReaderContext().getReader().loadBeanDefinitions(importResource);
} catch (IOException | BeanDefinitionStoreException e) {
e.printStackTrace();
}
}
Also I had to parse the import statements before the bean elements, so that the bean refs are not broken.
where i have to read and write userdata.properties from UI, every thing up to now is working like reading and writing ,before i mention the hardcoded direct path in file like this
File f = new File("D:\\user\\userdata.properties")
but my problem is if i mention like this i cannot change the path,for that i have to mention
D:\user\userdata.properties in another path.properties file ,now i have to read path.properties file in
File f = new File(........)
please help me how o do that.this is how presently i am using to read userdata.properties
#RequestMapping("/proxy")
public String ProxySettings(Model model) throws Exception {
File f = new File("D:\\sahi\\userdata.properties");
//String path = MobeeProxyChangeController.class.getResourceAsStream("/property/path.properties").toString();
Properties properties = new Properties();
try {
properties.load(new FileInputStream(f));
String getHost = properties.getProperty("ext.http.proxy.host");
String getPort = properties.getProperty("ext.http.proxy.port");
model.addAttribute("proxyHost", getHost.trim());
model.addAttribute("proxyPort", getPort.trim());
} catch (Exception e) {
e.printStackTrace();
}
return "proxyFile";
}
Thanks in advance
venu
First of all, you shouldn't call the load method for each request. That will turn out very expensive for you.
Secondly, since you're already using spring, there is no need to read a properties file this way. You can just inject it.
I suggest creating fields for proxyHost and proxyPort in your class as fields.
public class MyController {
...
private String proxyHost;
private String proxyPort;
#RequestMapping("/proxy")
public String ProxySettings(Model model) throws Exception {
model.addAttribute("proxyHost", this.proxyHost);
model.addAttribute("proxyPort", this.proxyPort);
return "proxyFile";
}
// provide setters for the above fields or use #Autowired
}
Your spring configuration would look like:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:userdate.properties" />
</bean>
<bean class="com.foo.MyController">
<property name="proxyHost" value="${ext.http.proxy.host}"/>
<property name="proxyPort" value="${ext.http.proxy.port}"/>
</bean>
You can read the userdata.properties either from the classpath or file system depending on where you keep it.
Put your properties file on the classpath and reference it via a MessageSource
Example:
Properties file "foo.properties":
foo=bar
baz=phleem
Java code:
public static void main(final String[] args) {
final ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("foo");
System.out.println(messageSource.getMessage("foo", new Object[0], Locale.getDefault()));
}
Output:
bar
You can let Spring inject that MessageSource, but I think you have to do that through XML (or through #Configuration). But you can also do it manually, however, you should only do it once per class, not once per request.
OK, here's an example of how you could wire it together using Spring:
XML configuration:
<bean id="proxySettings" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="userdata" />
</bean>
Controller class:
#Controller
public class ProxyController {
private static final Locale LOCALE = Locale.getDefault();
private static final Object[] EMPTY_ARGS = new Object[0];
#Autowired #Qualifier("proxySettings")
private MessageSource messageSource;
#RequestMapping("/proxy")
public String proxySettings(final Model model) throws Exception {
model.addAttribute("proxyHost", messageSource.getMessage("ext.http.proxy.host", EMPTY_ARGS, LOCALE));
model.addAttribute("proxyPort", messageSource.getMessage("ext.http.proxy.port", EMPTY_ARGS, LOCALE));
return "proxyFile";
}
}
I am new in Java EJB 3.0. It is possible to call a (session) bean—deployed on JBoss—from a desktop application client?
Thanks in advance.
Yes you can. Some specifics are here (references EJB2 but it the same for EJB3 when it comes to remote clients): http://www.theserverside.com/discussions/thread.tss?thread_id=9197
Paraphrased:
Hashtable env = new Hashtable();
env.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
env.put("java.naming.provider.url", "jnp://localhost:1099");
env.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces");
Context ctx = new InitialContext(env);
// name is whatever JNDI name you gave it
Object o = ctx.lookup("home name");
EJBHome ejbHome = (EJBHome) PortableRemoteObject.narrow(o,EJBHome.class);
// This is userID should be the one passed.
EJB ejb = ejbHome.create(..);
Yes.
public static void main(String args[]) throws Exception {
InitialContext ctx = new InitialContext();
YourService yourService = (YourService) ctx.lookup("com.example.session.YourService");
String time = yourService.getTime();
System.out.println("Time is: " + time);
}
For client configuration you must provide jndi.properties file with contents
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost
If you are looking for working examples on JBoss try download source code of Enterprise JavaBeans 3.0, Fifth Edition
Let's assume you have the following remote interface:
#Remote
public interface HelloBeanRemote {
public String sayHello();
}
And a session bean implementing it:
#Stateless
public class HelloBean implements HelloBeanRemote {
...
}
And that this EJB is correctly packaged and deployed on JBoss.
On the client side, create a jndi.properties with the following content and put it on the classpath:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost:1099
Then use the following code to call your EJB:
Context context;
try {
context = new InitialContext();
HelloBeanRemote beanRemote = (HelloBeanRemote)context.lookup("HelloBean/remote");
beanRemote.test();
} catch (NamingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
Alternatively, if you don't want to provide a jndi.properties file, you can explicitly setup the JNDI environment in the code and create the context like this:
Properties properties = new Properties();
properties.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.factory.url.pkgs","=org.jboss.naming:org.jnp.interfaces");
properties.put("java.naming.provider.url","localhost:1099");
Context context = new InitialContext(properties);
But I'd recommend using the jndi.properties for the sake of portability.
You can also expose the bean as a web service. I believe this is available as of EJB 3. It is quite nice considering you can do it with annotations. You may wish to consider using this option to decrease coupling. Here is a link to a tutorial.