Changing single parameters in XML file wrapped as Java Object - java

I am getting a config.xml file via a REST API which has a specific structure. I am adapting this config.xml via Java and pushing it again via PUT command to the REST endpoint to update it.
This XML structure contains the same amount of properties (let's say 'name' and 'description') but might be enhanced by some more properties (e.g. 'category'), which I am not aware of.
<config>
<name>myName</name>
<description>myDescription<description>
<category>myCategory<category>
</config>
My goal is to adapt this config file via Java Code while wrapping it into an Object. So I built a Class 'Config' containing 'String name' and 'String description'.
I can easily parse the config.xml to my Config object with JAXB and adapt name and description, but when marshalling it to XML the category would be missing, although it was returned by the REST API. Is there a way (maybe ValueChangeListener?) to adapt only the changed values in an existing xml file?
public class Config { String name; String description; }
So I don't event want to be able to change 'category' at all, I just don't want to lose the data.
Info: In my scenario the Config class is very complex and has alot of subclasses (it's a representing a Jenkins Job). So the example above is very simplified.
I got the idea to create a second config file, only having the changed parameters. Afterwards merging the to config files. But I had no idea how to be aware what exactly has changed and how to implement it.
Example:
I want to change description to "newDescription", so my expected XML would be:
<config>
<name>myName</name>
<description>newDescription<description>
<category>myCategory<category>
</config>
unfortuntately it is:
<config>
<name>myName</name>
<description>newDescription<description>
</config>
Means category parameter is lost, as I am not aware of it and therefore didnt add it to the Config class. Summarized, there might be parameters in the XML file which I am not aware, which I also do not want to change - but don't want to lose when pushing an updated config.

Related

Get existing object by reading out of XML using Java (JAXB)

I have some problems describing my problem.
I have an XML-File with names of existing classes in my project.
E.g.:
<?xml version="1.0" encoding="UTF-8"?>
<MetaClasses>
<MetaClass ID="1">
<Meta>ExistingClassName</Meta>
</MetaClass>
<MetaClass ID="2">
...
</MetaClass>
</MetaClasses>
And i need the corresponding existing class, because i need to work with this class.
I don't want to create a new object, like MetaClass meta = new MetaClass();, i want the real class with its real attributes, properties and methods, since the XML ClassName is just simple text.
I need the properties of that class to proceed my project.
I hope you were able to get my problem correctly or rather i explained it correctly :P
Thank you
I did it by myself.
Maybe someone has the same problem and I could help you with my answer.
I had to take the String inside the <Meta>-Tag and did this:
Class classMeta = Class.forName(string);
Bean beanMeta = (Bean) classMeta.newInstance();
Then I was able to work with this Meta-Class.

Camel route logic / Output expectations

Background:
I'm trying to unmarshal an xml file using jaxb and camel. I think I'm having a hard time with it because I don't know exactly what to expect filewise.
For example, I have:
from("file://C:/test.xml").unmarshal(jaxb).to("file://C:/testEnd.java");
With that, I'm expecting to see the result of the unmarshalling in the .java file (i.e. parameters and values from the xml file elements). However, when I run the program, nothing shows up in the .java file, but I don't receive any errors.
The same thing happens with marshalling. When I have a .java file as the from function and a .xml file in the to function, nothing happens.
For example, I have:
from("file://C:/test.java").marshal(jaxb).to("file://C:/testEnd.xml");
From this, I would expect to see values from my annotated java file appear in the xml file.
Question:
Is my expectation in both of these cases correct? Or is there something wrong with that logic?
Please try this:
If in your code you want to save java object in the form of an xml and then again use that xml to retrieve the state of the java object saved earlier, we do marshalling and unmarshalling
1) Marshalling: convert java object to xml based and save it to file
Create a producerTemplate sending java object to the producerendpoint, marshal it against jaxb dataformat and it would be converted to xml using the pojo bean marked with XmlRootElement and referred as contextPath in jaxb tag.
public class ClientEight {
#Produce(uri="direct:invoice")
ProducerTemplate template;
public static void main(String rgs[]) throws InterruptedException{
AbstractApplicationContext ctx= new ClassPathXmlApplicationContext("resources/camel-configTen.xml");
InvoiceXml invoice= new InvoiceXml("fdf3443",3454, 435345.44f, "hfhfddfdg"); //any java object we are passing
ClientEight client = (ClientEight) ctx.getBean("client");
Object xmlObj= client.template.requestBody(invoice);
System.out.println(xmlObj);
}
Above is a client code which u are using to send java object to a producer endpoint and since u are using template.requestBody, u are getting back the object returned.
<camel:camelContext>
<camel:dataFormats>
<!-- path to jaxb annotated class -->
<camel:jaxb id="invoiceJaxb" contextPath="com.java.bean"
prettyPrint="true" />
</camel:dataFormats>
<camel:route>
<camel:from uri="direct:invoice" />
<camel:marshal ref="invoiceJaxb" />
<camel:log message=" ${body}" />
<camel:to uri="file://src/resources?fileName=One.xml"/>
</camel:route>
</camel:camelContext>
This would be your camel config file. Hope this helps
The file component take a directory, and process all files in this directory. it doesn't handle file by default, you have to use options, or the stream component.
see http://camel.apache.org/file2.html
Only directories
Camel supports only endpoints configured with a
starting directory. So the directoryName must be a directory. If you
want to consume a single file only, you can use the fileName option,
e.g. by setting fileName=thefilename. Also, the starting directory
must not contain dynamic expressions with ${ } placeholders. Again use
the fileName option to specify the dynamic part of the filename.

Multiple config block in java properties file

I need to define multiple configuration blocks in a single .properties file in Spring. Currently I am having multiple .properties file like below:
one.properties:
publishing.channel=ftp
ftp.user=user1
ftp.password=pass1
ftp.host=abc.xyz.com
ftp.port=21
two.properties
publishing.channel=ftp
ftp.user=user2
ftp.password=pass2
ftp.host=def.xyz.com
ftp.port=21
What I now require is defining only one .properties file and add all the configuration blocks in it like so:
publishing.channel=ftp
ftp.user=user1
ftp.password=pass1
ftp.host=abc.xyz.com
ftp.port=21
then another one
publishing.channel=ftp
ftp.user=user2
ftp.password=pass2
ftp.host=cdf.xyz.com
ftp.port=21
it could be http too
publishing.channel=http
http.user=user2
http.password=pass2
http.host=cdf.xyz.com
Problem is when I put multiple property blocks like this, I cannot differentiate in code as my bean methods (e.g. getHost()) will only fetch the last declared one in the properties file. I do not want to create many variables like host1, host2, host3 and so on as it would need to be modified in case there is another block of properties added. How can I make it generic?
Thanks in advance.
You can use PropertiesConfiguration from Apache Commons Configuration and then access all the values of same key and add your logic to get the required value.
Use getStringArray(key) method or getList(key) method to access all values.

xstream: only parsing child element

I have following xml
<root>
<child-1>
</child-1>
<child-2>
<subchild-21>
</subchild-22>
</child-2>
</root>
My requirement is such that I only want to parse child-2. I am unaware of root and child-1.
Is it possible with xstream because I couldn't find a way to ignore root.
There are several ways to go, depending on your requirements.
If you know the name of the class to parse (child-2 here), you could look for the <child-2> and </child-2> entry in the XML, copy them along with the content in-between to a new temporary XML file (you can create temporary files using createTempFile() from the standard File class). This is the way I would suggest.
If you want to take out the child-2 instance without knowing its name, but you know the names of the surrounding classes, you could mock their classes, that is create classes of the same name, but without their specific content. In your example there is no content (might have been ignored at export time), but it's important to have the same member data in the mock classes for the import to succeed. (unless you use ignoreUnknownElements() as stated by Philipi Willemann)
Of course, if you're the one creating the XML, you should be able to export only the child-2 instance in the first place.
If you know the root name you can create a simple class has an attribute of the class you have mapped to child-2:
#XStreamAlias("root")
class Root {
#XStreamAlias("child-2")
private Child2 child;
//get and set
}
Then when you are processing the XML you can set XStream to ignore unknown elements with xstream.ignoreUnknownElements();

Generate Spring bean definition from a Java object

Let's suggest that I have a bean defined in Spring:
<bean id="neatBean" class="com..." abstract="true">...</bean>
Then we have many clients, each of which have slightly different configuration for their 'neatBean'. The old way we would do it was to have a new file for each client (e.g., clientX_NeatFeature.xml) that contained a bunch of beans for this client (these are hand-edited and part of the code base):
<bean id="clientXNeatBean" parent="neatBean">
<property id="whatever" value="something"/>
</bean>
Now, I want to have a UI where we can edit and redefine a client's neatBean on the fly.
My question is: given a neatBean, and a UI that can 'override' properties of this bean, what would be a straightforward way to serialize this to an XML file as we do [manually] today?
For example, if the user set property whatever to be "17" for client Y, I'd want to generate:
<bean id="clientYNeatBean" parent="neatBean">
<property id="whatever" value="17"/>
</bean>
Note that moving this configuration to a different format (e.g., database, other-schema'd-xml) is an option, but not really an answer to the question at hand.
You can download the Spring-beans 2.5 xsd from here and run xjc on it to generate the Java classes with JAXB bindings. Then you can create the Spring-beans object hierarchy on runtime (and manipulate it as you wish) and then serialize it to an XML string using the JAXB Marshaller as shown in Pablojim's answer.
I'd use Jax-b to do this. You'de create a bean object with a list of property objects inside.
#XmlRootElement(name = "bean")
#XmlAccessorType(XmlAccessType.FIELD)
public class Bean {
#XmlAttribute
private String id;
#XmlAttribute
private String parent;
#XmlElement(name="property")
private List<BeanProperty> properties
Then You'd need to also add annotations to BeanProperty. Then when you have a populated object simply marshal it to xml using jaxb:
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal( myBean, System.out );
For full code examples see: http://java.sun.com/webservices/docs/2.0/tutorial/doc/JAXBUsing.html
Alternatively you could use Groovy - you can drop it in place and creating this xml would be very simple... : http://www.ibm.com/developerworks/java/library/j-pg05199/index.html
If you want a simple to implement, no work solution, you can look at the IDE support provided in IntelliJ and Eclipse (The Spring Tool Suite).
They parse all the bean files (you can configure which set) and inspect the java code so it knows which classes there are, which properties are in those classes. Everywhere you can use Ctrl-Space to help with the options, etc...
I imagine you could setup 'projects' w/o Java code and only the spring config files in order to reduce the learning curve of front line personnel who must make these changes.
What you need is obviously a factory for your neatBeans.
In Spring, instead of declaring a bean, you can declare a FactoryBean whose role is to actually create and configure your final bean.
The NeatBeanFactoryBean could read a property file (or xml configuration) to determine how to configure the produced neatBeans, depending on some runtime parameter (the current clientID for example) or compile-time parameter (environment variable).
To add to the other two questions, I believe Spring already has a working model for bean definitions (see org.springframework.beans.factory.config.BeanDefinition); you could base your work on that.
I'd suggest using
<context:property-placeholder location="classpath*:clientX.properties"/>
and then in your bean def:
<bean id="${clientYNeatBeanId}" parent="neatBean">
<property id="whatever" value="${whateverValue}"/>
</bean>
Then for each client you can have a clientX.properties containing
whateverValue=17
whateverAnotherValue=SomeText
.properties files are easier to edit both manually, and programaticalyl via java.util.Properties store(..) / save(..) methods

Categories