Java enum fields serialization/deserialization - java

Intro
I am using Apache Storm (Local mode, not Remote mode) in my Java project and when creating the topology I need to pass an object to one of the bolts
TopologyBuilder builder = new TopologyBuilder();
.....
builder.setBolt("last-bolt", new MyBolt(Classifier.SECONDS)).someGrouping(...);
.....
LocalCluster cluster = new LocalCluster();
cluster.submitTopology("test", conf, builder.createTopology());
The object itself has some non-serializable fields. Instead of subclassing the classes to which those fields belong and making them Serializable I have taken another approach. Since the actual object isn't gonna be changing a lot and it can be enumerated I've decided to make it an enum and pass it like that to bolt's tasks. The good thing about enum is that it is serializable under all costs. This approach works in local mode because (if I understood Storm correctly) there is only one JVM running on my computer and things can't get complicated actually.
Question
If the enum consists of a static final non-serializable field will that field be constructed properly when the enum is deserialized by another process on a different machine or a cluster running multiple JVMs?
The actual enum (static final field is at the end)
public enum Classifier {
SECONDS {
public String classify(String timestamp) {
DateTime dateTime = formatter.parseDateTime(timestamp);
int second = dateTime.getSecondOfMinute();
if (second <= 30) {
return "00 - 30";
} else {
return "30 - 60";
}
}
public int getNumberOfCategories() {
return 2;
}
},
WEEK {
public String classify(String timestamp) {
DateTime dateTime = formatter.parseDateTime(timestamp);
int dayOfWeek = dateTime.getDayOfWeek();
String typeOfDay = (dayOfWeek >= 1 && dayOfWeek <= 5) ? "workday" : "weekend";
int hour = dateTime.getHourOfDay();
String hourInterval = hour + " - " + (hour == 23 ? 0 : hour + 1);
return typeOfDay + " " + hourInterval;
}
public int getNumberOfCategories() {
return 48;
}
};
private static final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
public abstract String classify(String timestamp);
public abstract int getNumberOfCategories();
}
More details
DateTimeFormatter and DateTime are from org.joda.time package.

All static final fields are initialized when class is loaded. Whatever serialization mechanism is used it will first initialize the static fields and execute static initialization blocks. Note that static fields are not deserialised because we are not deserialising classes but objects (please also refer to this answer https://stackoverflow.com/a/6429497/1937263).
So the answer is yes, the field should be constructed properly.

Related

Java several enums as getter/setter

I wanted to use enum class to assign book formats to book object. Issue that I have encountered is that some books have 1 format and other have several.
public enum Format {
HARD_COVER, PAPERBACK, E_BOOK, AUDIOBOOK
}
Let's say that first book have only 1 format while second book have all formats. How can I solve it with getters/setters?
How can I solve it with getters/setters?
I wouldn't do this since in my experience, getters and setters are for setting single properties. If I have a class that requires a collection of property, then I'd have the class contain a List for this:
List<Format> formats = new ArrayList<>();
and then:
public void addFormat(Format format) {
formats.add(format); // add to the ArrayList of Format
}
and similarly a removeFormat(Format format) method for removing from the List.
Edit: as correctly stated by Dorian Gray in comments, it would be better that the collection of Formats be a Set<Format> initialized as an EnumSet` since this would prevent duplicate Formats being added to the collection.
Set<Format> formatSet = EnumSet.noneOf(Format.class);
You could work with Bitwise Operators to have one property with the possibility of having multiple values.
Have Enums with int values like this:
public enum Format {
HARD_COVER(1),
PAPERBACK(2),
E_BOOK(4),
AUDIOBOOK(8);
public final int value;
Format(int value) {
this.value = value;
}
}
In your Book class add an int property called format instead of a Format property. Also create a method called isFormat that returns a boolean value according to its Format parameter:
class Book {
private int format;
public Book(int format) {
this.format = format;
}
public boolean isFormat(Format format) {
return format.value == (this.format & format.value); // &: bitwise and
}
}
Finally create a book adding formats separated by | (bitwise or):
Book book = new Book(Format.E_BOOK.value | Format.HARD_COVER.value | Format.AUDIOBOOK.value);
And check its formats like this:
System.out.println("Is paperback?: " + book.isFormat(Format.PAPERBACK));
System.out.println("Is hard cover?: " + book.isFormat(Format.HARD_COVER));
System.out.println("Is audio book?: " + book.isFormat(Format.AUDIOBOOK));
System.out.println("Is E-book?: " + book.isFormat(Format.E_BOOK));
The result of this execution is this:
Is paperback?: false
Is hard cover?: true
Is audio book?: true
Is E-book?: true

Creating different POJO child objects depending on criteria but share common fields

I am implementing a log management system and want the types of logs to be extendible. We get a base object parsed from JSON (from Filebeat) such as:
class LogLine {
String message
Date timestamp
String type
String source
}
Given this LogLine object, I want to be able to create different objects, which will also extend this LogLine.
class SomeLog extends LogLine {
int myfield
String username
}
class SomeOtherLog extends LogLine {
Date startTime
Date endTime
String username
String transactionID
}
So, in my current non-ideal implementation:
void parse(String s){
LogLine logLine = .....parseFromString(s)
if ( logline.type.equals('def') ){
SomeLog someLog = new SomeLog.Builder.build(logLine)
} else if ( logline.message.containts('abc') ){
SomeOtherLog someotherLog = new SomeOtherLog.Builder.build(logline)
}
}
However, as you can imagine the builders in subclasses copies the superclass LogLine object, is there anyway I can do that without copying the values as they are already subclasses? Is there a design pattern to achieve this? I would not like to rely on reflection like BeanUtils.copyProperperties
When you create a new object based on another it's a good idea to make a copy of all field. It's a best practice called defensive copying.
Since you parse a string, a defensive copy doesn't needed. Also I suppose you'll want to parse some specific fields from input string like myfield for SomeLog and startDate for SomeOtherLog. You could re-factor object creation like
LogLine result = null;
if (s.contains('type=def') {
result = SomeLog.parse(s);
} else if (trickyRegexp.mathces(s)) {
result = SomeOtherLog.parse(s);
} else {
result = LogLine.parse(s);
}
If you have many subclasses of LogLine then probably you'll want to move creation logic to a LogFactory which manages all the stuff regarding parsing string to specific object.
Introduce a factory interface for creating LogLine objects.
public interface LogLineFactory {
public LogLine createLog(LogLine logLine);
}
and use a Map for the lookup.
private Map<String, LogLineFactory > logLineFactories = new HashMap<>();
{
logLineFactories .put("def", new SomeLogFactory());
logLineFactories .put("abc", new SomeOtherLogFactory());
}
You can then ommit the if else branches using the map looup.
LogLine logLine = parseFromString(s);
LogFactory logFactory = logLineFactories.get(logLine.type);
if(logFactory != null) {
LogLine wrappedLogLine = logFactory.createLog(logLine);
}
Maybe you will need more information to create the LogLines and you have to change the interface.
public interface LogLineFactory {
public LogLine createLog(LogLine logLine, String s);
}
PS: with Java 8 you might want to use method references.
logLineFactories.put("def", SomeLog::new);
logLineFactories.put("abc", SomeOtherLog::new);

Mapping from java.time.MonthDay to java.sql.*

We have a case, where we require only the day and the month and thus would use the java.time.MonthDay (Javadocs) to represent that information.
We are aware that we could create our own JPA object for persistence or just use the java.sql.Date object, but that generally requires an unrequired year information.
Another way is to call the method .atYear(int) (Javadoc) (with a fictitious year) on it and receive a java.time.LocalDate (Javadoc), which can be easily converted to java.sql.Date. But this is prone to missunderstandings in the future (and also persist the year information).
Is there some "elegant"/supposed solution for this case? Or is there a replacement for SQL that supports the whole new date and time API for Persistence.
Another case would be java.time.YearMonth (Javadoc).
Thanks for reading!
Since SQL databases don't have a type compatible with MonthDay, use a VARCHAR columns, and simply use toString() and MonthDay.parse().
Or use a custom DateTimeFormatter, if you don't like the --12-03 format.
The default format will correctly sort, as a string.
here are the code snippets:
// column define:
#Column(name = "date", columnDefinition = "mediumint", nullable = false)
#Convert(converter = MonthDayIntegerAttributeConverter.class)
protected MonthDay date;
// converter define:
public class MonthDayIntegerAttributeConverter implements AttributeConverter<MonthDay, Integer> {
#Override
public Integer convertToDatabaseColumn(MonthDay attribute) {
return (attribute.getMonthValue() * 100) + attribute.getDayOfMonth();
}
#Override
public MonthDay convertToEntityAttribute(Integer dbData) {
int month = dbData / 100;
int day = dbData % 100;
return MonthDay.of(month, day);
}
}

Calculated value in Groovy enum

I need to create an enum or a class that has some predefined values and must have an option to calculate other values. Say we have this enum:
enum Duration {
ONE_HOUR("1${DurationUnits.DURATION_SUFFIX_HOUR}"),
ONE_DAY("24${DurationUnits.DURATION_SUFFIX_HOUR}"),
ONE_WEEK("7${DurationUnits.DURATION_SUFFIX_DAY}"),
ONE_MONTH("30${DurationUnits.DURATION_SUFFIX_DAY}")
String duration
Environment(String duration) {
this.duration = duration
}
static String custom(Date startTime, Date endTime) {
// TODO Calculate difference in days between the two dates
}
private static class DurationUnits {
public static final String DURATION_SUFFIX_HOUR = "h"
public static final String DURATION_SUFFIX_DAY = "d"
}
}
So my question is if I can define an enum value, say CUSTOM, that represents the value of all the non predefined ones. That is, a generic value for the custom(Date a, Date b) method.
I can't figure out how to do it, actually I don't think it's even possible.
As an alternative I'm thinking of creating a class instead of an enum, like this:
class Duration {
private static final String DURATION_SUFFIX_HOUR = "h"
private static final String DURATION_SUFFIX_DAY = "d"
/** Predefined values */
static final String ONE_HOUR = "1${DURATION_SUFFIX_HOUR}"
static final String ONE_DAY = "24${DURATION_SUFFIX_HOUR}"
static final String ONE_WEEK = "7${DURATION_SUFFIX_DAY}"
static final String ONE_MONTH = "30${DURATION_SUFFIX_DAY}"
static String custom(Date startDate, Date endDate) {
// TODO Calculate difference in days between the two dates
}
}
What do you think is the best way to go? This duration will be an input parameter to a method that calls a REST web service, that's why I was trying to define it as an enum instead of just passing a string.
You can use String append.
ONE_HOUR("1" + DurationUnits.DURATION_SUFFIX_HOUR),
ONE_DAY("24" + DurationUnits.DURATION_SUFFIX_HOUR),
ONE_WEEK("7" + DurationUnits.DURATION_SUFFIX_DAY),
ONE_MONTH("30" + DurationUnits.DURATION_SUFFIX_DAY);
However it might be better to use TimeUnits
ONE_HOUR(1, TimeUnit.HOUR),
ONE_DAY(1, TimeUnit.DAY),
ONE_WEEK(7, TimeUnit.DAY),
ONE_MONTH(30, TimeUnit.DAY);
Note: not all months have 30 days.
You can also use Duration for a duration of time.
Duration ONE_HOUR = Duration.ofHours(1);
Duration ONE_DAY = Duration.ofDays(1);
This would allow you to create more Duration as needed.
If it were Java (I'm not a Groovy expert) I'd suggest something like ONE_HOUR("1" + DurationUnits.DURATION_SUFFIX_HOUR), or yet better use another enum that represents the unit itself, e.g. like this:
enum DurationUnit {
HOUR ("k"),
DAY( "d" );
private final String key;
//constructor and getter
}
enum Duration {
ONE_HOUR(1, DurationUnit.HOUR ),
...
private final int quantity;
private DurationUnit unit;
//constructor and getters
public String toString() {
return quantity + unit.getKey(); //should return 1h, 24h, 1d etc.
}
}
This should be doable in Groovy as well and would allow for calculations/comparisons without needing to parse a string.
Update:
For custom durations (and if Java 8's own Duration class doesn't fit your needs) you'd make Duration a normal (immutable) class with some constants:
class Duration {
public static final Duration ONE_HOUR = new Duration(1, DurationUnit.HOUR );
...
private final int quantity;
private DurationUnit unit;
//constructor and getters
}

Calling a #Canonical Groovy POGO constructor inside Java code

Given this Groovy domain class (for persistence in MongoDB):
#Canonical
class Counter {
#Id String id
String name
long count = 0
Date createdTimestamp = new Date()
Date updatedTimestamp = new Date()
}
Since only 'name' need be supplied when creating a new Counter, is there a way to call the #Canonical-generated map-based constructors, as the Groovy approach below will not compile in Java:
// Invalid Java code
counterRepository.save(new Counter(name: newCounterName));
Must I either use the implicit setter:
// Valid, but a bit verbose, Java code
Counter counter = new Counter();
counter.setName(newCounterName);
counterRepository.save(counter);
Or create a static factory method in the Counter POGO:
static Counter init(String newCounterName) {
return new Counter(name: newCounterName)
}
Enabling the following:
// Valid, concise, but perhaps/hopefully redundant?
counterRepository.save(Counter.init(counterName));
The last approach is the one currently used.
If I understand you correctly you don't really want to use #Cannonical, you are more after #TupleConstructor. With this AST you can specify fields you want to use and have more fine grained controller over the constructor. An example could be:
#TupleConstructor(includes=['name'])
class Counter {
#Id String id
String name
long count = 0
Date createdTimestamp = new Date()
Date updatedTimestamp = new Date()
}
For more see http://docs.groovy-lang.org/latest/html/gapi/groovy/transform/TupleConstructor.html

Categories

Resources