Using LocalDateTime with JAXB - java

I am trying to use JAXB with fields of the LocalDateTime type. I wrote an adapter to handle conversion:
public class LocalDateTimeXmlAdapter extends XmlAdapter<String, LocalDateTime> {
#Override
public String marshal(LocalDateTime arg0) throws Exception {
return arg0.toString();
}
#Override
public LocalDateTime unmarshal(String arg) throws Exception {
return LocalDateTime.parse(arg);
}
}
I registered the adapter in package-info.java like so:
#XmlJavaTypeAdapters({
#XmlJavaTypeAdapter(type=LocalDateTime.class, value=LocalDateTimeXmlAdapter.class)
})
package xml;
import java.time.LocalDateTime;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
This seems to be sufficient according to this page.
However, I keep getting the following error:
com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
java.time.LocalDateTime does not have a no-arg default constructor.
I understand the reason for the exception being thrown, but I can hardly add a default constructor to java.time.LocalDateTime. This seems to be a shortcoming of the class / a strange design decision. Are there any workarounds?

What you have should work. One of the following may be wrong:
Since you have specified the #XmlJavaTypeAdapter at the package level it will only apply to properties on classes in your package called xml. Is there a class in your model from a different package that has a mapped property of type LocalDateTime?
It is also possible that your package-info.java file is not being compiled.

Had same behaviour: IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions.
My pbm was: I have several packages (three) where the package-info.java file is needed, like shown in the following picture.
I "solved" this pbm by adding a package-info.java in each of the three directories. Example for package fr.gouv.agriculture.dal.ct.planCharge.metier.dao.charge.xml:
#XmlJavaTypeAdapter(type = LocalDate.class, value = LocalDateXmlAdapter.class)
package fr.gouv.agriculture.dal.ct.planCharge.metier.dao.charge.xml;
If someone has a better idea than copy/paste into several package-info.java files, thanks in advance.

Related

How to get a method produce custom output without modifying the method in the class?

My Java project is organised as follows:
(package no.1) it.blabla.myproject package containing the classes strictly related to my project
(package no.2) it.blabla.myproject.utils package containing some utility classes, some of which I use in package no.1.
I want to keep package no.2 "general" = not related to my project, so I can re-use it for other projects.
Package no.2 contains the following class:
...
import org.xml.sax.SaxParseException;
...
// For handling errors in xml parsing
class XmlHandler extends DefaultHandler {
...
//stuff
...
#Override
public void error(SAXParseException e) throws SAXException {
System.out.println(e.getMessage()+"; "+currentNodeName); // Here there should be
// a project-specific error message
// between the e.getMessage() and
// the currentNodeName
};
...
}
My problem is: the message returned by this error handler must contain some specific text related to the project (for example: System.out.println(e.getMessage() + "; the data of Company XYZ are not ok; " + currentNodeName);)
How could I achieve this without loss of generality of my utils package? I know I could just pass a String parameter to the method and put my custom error message in it, but it's kinda ugly...
Since I am quite new to Java, I'd like to understand how this could be implemented properly. Do you see anything wrong or not fulfilling best-practices in my project structure?
Thanks in advance.
EDIT: I could also create another class named for example CustomHandler extends XmlHandler, the problem is: should I create it in package 1 or 2? Some other classes in package 2 use XmlHandler in turn, so I should modify also them in order to use CustomHandler... but then the utils package will again loose its generality
Welcome to Inheritance. You see how the XmlHandler extends DefaultHandler and then overrides the error method? You can do the same thing in you project-specific implementation by creating, for example, a CustomXmlHandler which extends the XmlHandler
class CustomXmlHandler extends XmlHandler {
...
#Override
public void error(SAXParseException e) throws SAXException {
// Your custom error handling
}
...
}

Deserialization of serialized final class using Jackson

I have following value holder class for users:
package entities;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;
import javax.annotation.Nullable;
#Value.Immutable
#JsonSerialize(as = ImmutableUser.class)
#JsonDeserialize(as = ImmutableUser.class)
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public interface User {
String getUsername();
String getEmail();
#Nullable String getPassword();
#Nullable String getEncodedPassword();
}
Immutable final implementation of this value holder is being generated during compilation:
#SuppressWarnings("all")
#ParametersAreNonnullByDefault
#Generated({"Immutables.generator", "User"})
#Immutable
public final class ImmutableUser implements User {
Serialized instance of Immutable
{"#class":"entities.ImmutableUser$Json","username":"testuser","email":"123#gmail.com","password":null,"encodedPassword":null}
The problem is that deserialization of this JSON fails with following error:
java.lang.IllegalArgumentException: Class entities.ImmutableUser$Json is not assignable to entities.User
at com.fasterxml.jackson.databind.JavaType._assertSubclass(JavaType.java:466)
at com.fasterxml.jackson.databind.JavaType.narrowBy(JavaType.java:149)
at com.fasterxml.jackson.databind.type.TypeFactory.constructSpecializedType(TypeFactory.java:315)
at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver._typeFromId(ClassNameIdResolver.java:64)
... 38 more
Why does #class property in JSON for serialized instance have value "entities.ImmutableUser$Json" instead of "entities.ImmutableUser"? Is it because of fact that the class is final?
Is there any other way to serialize such classes and to avoid problems during deserialization?
Found out that the problem was caused by generated class. Turns out
that such classes should be marshaled using specific classes:
immutables.github.io/site1.x/json.html
The http://immutables.github.io/site1.x/json.html is referring to the older version of documentation and is quite irrelevant if you use Immutables v2.0 and up. In your case you're facing an already fixed issue (similar to https://github.com/immutables/immutables/issues/175). Try upgrading to Immutables v2.1 to get it resolved.
In nutshell, Jackson have feature annotation #JsonValue to substitute object during serialization. Unfortunately as we found out that it does not play well with other functionality as #JsonTypeInfo and #JsonSubTypes. See https://github.com/FasterXML/jackson-databind/issues/937.
Version 2.1 of Immutables no longer used #JsonValue, so it should work now. If not, please report it as a bug to https://github.com/immutables/immutables/issues

How to give warning message in own API?

Is that possible to give custom warning message in my own API like below? Is Resource leak:'ois' is never closed message related with Java API or JVM?
It's possible using a compiler API. You have to extend an AbstractProcessor and then make sure compiler knows about it.
Lets say we don't like programmers to swear in the source code. So, when someone defines a field with name "shit", we want to show a warning. Here is a simple implementation:
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
#SupportedSourceVersion(SourceVersion.RELEASE_7)
#SupportedAnnotationTypes("*")
public class Test extends AbstractProcessor {
public int shit;
public int foo;
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> rootElements = roundEnv.getRootElements();
for (Element element : rootElements) {
if (element.getKind() == ElementKind.CLASS) {
List<? extends Element> classElements = element.getEnclosedElements();
for (Element classElement : classElements) {
if (classElement.getKind() == ElementKind.FIELD) {
if (classElement.getSimpleName().contentEquals("shit")) {
processingEnv.getMessager().printMessage(
Kind.WARNING,
"How dare you to swear in the source code?!",
classElement
);
}
}
}
}
}
return false;
}
public static void main(String[] args) {
//
}
}
Now, we want to apply such a processor just for this very class, because there is an ugly bad-called field too.
Using a command line:
javac Test.java
javac -processor Test Test.java
We need to firstly build a processor and then apply it while compiling (in this case to the same file).
And this is the output we get:
Test.java:17: warning: How dare you to swear in the source code?!
public int shit;
^
1 warning
To have the same warning in Eclipse or any other IDE, it's necessary to change compiler settings so it uses this custom processor.
Update: In the comments, kapep sent a link on how to set a custom processor in Eclipse: http://kerebus.com/2011/02/using-java-6-processors-in-eclipse/
Just for the record: Exactly the same warning may be achieved by implementing interface Closeable:
import java.io.Closeable;
import java.io.IOException;
public class Test implements Closeable {
#Override
public void close() throws IOException {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
new Test();
}
}
And you see the same warning:
You can create warnings, notes, errors and other diagnostic messages like this using an annotation processor. It's a compiler plugin api integrated in the JDK. It lets you analyse the outline structure of source code. Despite the name you don't really need to handle any annotation when processing code. Messages are created using the Messager class. If you provide an element, it will be marked and the message will be shown next to it in the source code editor.
You won't be able to show message on elements inside methods or expressions though, only on declarations like types, properties, methods or parameters. It's possible to additionally parse the method body and generate messages based on the content using other tools, but as far as I know you can't show the message on the actual local element then. You could still show the message on the enclosing method or don't specify any element at all and show it in the IDE's log.
The IDE also needs to support this. I know that Eclipse and NetBeans do support messages generated by annotation processors, but there are probably other modern IDE that do so as well. If you need more features like messages on elements inside method bodies or the quick fix feature as shown in the example, I guess you need to create a plugin for the IDE.
I would believe that it is related to the eclipse ide, you could possibly write a plugin which displays warnings like that.
For example, when you use a method which has the annotation '#Deprecated' the ide automatically tells the programmer that the method is deprecated.

How to make annotation deprecated only on one target

I have an annotation that can be added on METHOD and TYPE and is used in thousands of places in our project.
#Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
#Target({METHOD, TYPE})
#Inherited
public #interface RequiredStore{
Store value();
}
Is it possible to make the annotation deprecated only on methods while keeping it non-deprecated on types? I want other developers to be notified by IDE that it should not be used on methods any more, until we'll refactor all existing usages and finally remove the METHOD part.
If it's not possible, is there any Way to handle such case beside creating new annotation only for types and deprecating the old one?
You could use an annotation Processor.
For example, the annotation and its processor would be placed in its own .jar file and added as a dependency of the sources that use the annotation.
The custom .jar would have the following structure:
src/main/
java/com/company/annotations/
RequiredStore.java
RequiredStoreProcessor.java
resources/META-INF/services
javax.annotation.processing.Processor
RequiredStore.java stays as you have it above.
RequiredStoreProcessor.java could look something like this:
package com.company.annotations;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
#SupportedAnnotationTypes("com.company.annotations.RequiredStore")
public class RequiredStoreProcessor extends AbstractProcessor {
#Override
public boolean process(
Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
for (Element element
: roundEnv.getElementsAnnotatedWith(RequiredStore.class)) {
if (element.getKind().equals(ElementKind.METHOD)) {
processingEnv.getMessager().printMessage(
Diagnostic.Kind.WARNING,
"Using #RequiredStore on methods has been deprecated\n"
+ "Class: " + element.getEnclosingElement() + "\n"
+ "Method: " + element.getSimpleName() + "\n");
}
}
// Other processing...
return false;
}
#Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
}
The javax.annotation.processing.Processor file allows javac to pickup the Processor via SPI and simply contains
com.company.annotations.RequiredStoreProcessor
Finally, compile this into a .jar and add it to the classpath where the annotations are being used. Any methods that have the #RequiredStore will produce a compiler warning. For example, for this class,
package com.company.business;
import com.company.annotations.RequiredStore;
#RequiredStore
public interface Business {
#RequiredStore
public void someMethod();
}
The compiler warning would be this:
warning: Using #RequiredStore on methods has been deprecated
Class: com.company.business.Business
Method: someMethod
As for an indication in the IDE, you might have to write a custom inspection and unfortunately this depends on the IDE used.
Notes:
Decent custom annotations reference: Code Generation using Annotation Processors in the Java language
If you are okay about using native aspectj, another option is to use AspectJ's code enforcement policy this way:
public aspect RequiredStoreAnnotationCheck {
declare warning: execution(#RequiredStore * *.*(..)) : "Required store annotation not appropriate for methods..";
}
If the IDE is integrated with AspectJ, this would be flagged as a compile time check.
AspectJ in action book has a good amount of detail on this too.
Here is one of my blog articles for more context: http://www.java-allandsundry.com/2012/03/code-policy-enforcement-using-aspectj.html

In java how do you refer to a class that is in the default package of a third party library?

I have downloaded a third party library and they have classes I need to refer to in the default package? How do I import these classes?
It's not possible directly with the compiler. Sun removed this capability. If something is in the default namespace, everything must be in the default namespace.
However, you can do it using the ClassLoader. Assuming the class is called Thirdparty, and it has a static method call doSomething(), you can execute it like this:
Class clazz = ClassLoader.getSystemClassLoader().loadClass("Thirdparty");
java.lang.reflect.Method method = clazz.getMethod("doSomething");
method.invoke(null);
This is tedious to say the least...
Long ago, sometime before Java 1.5, you used to be able to import Thirdparty; (a class from the unnamed/default namespace), but no longer. See this Java bug report. A bug report asking for a workaround to not being able to use classes from the default namespace suggests to use the JDK 1.3.1 compiler.
To avoid the tedious method.invoke() calls, I adapted the above solution:
Write an interface for the desired functionality in your desired my.package
package my.package;
public interface MyAdaptorInterface{
public void function1();
...
}
Write an adaptor in the default package:
public class MyAdaptor implements my.package.MyAdaptorInterface{
public void function1(){thirdparty.function1();}
...
}
Use ClassLoader/Typecast to access object from my.package
package my.package;
Class clazz = ClassLoader.getSystemClassLoader().loadClass("MyAdaptor");
MyAdaptorInterface myObj = (MyAdaptorInterface)clazz.newInstance();
myObj.function1();

Categories

Resources