Aleksey Gureev Persistence Manager

At a present moment Informa library is providing wide variety of parsers. It also allows user to store his groups of channels, channels, items and other objects in persistent storages using Hiberate and JDO interfaces. However, there are some problems with the implementations. Despite the fact that all of implementations have similar interfaces for helper classes (ChannelBuilderIF and etc) and objects (ChannelGroupIF and etc), they still should be handled in implementation specific way. This means that if you wish to migrate from in-memory storage model to Hibernate you should be ready to change your client application code significantly.

Persistence Manager module is intended to hide the internal implementation complexities from developer and provide unified and well-defined interface to all persistent services. Persistence Manager itself will take care of initialization, transactions management and other specific things. It is also intended to solve the other big problem - multi-threading. Great number of applications suffer from incorrect work with persistence models in multi-threaded environment, having many unsynchronized sessions and objects floating in heap.

Structure of the module is extremely simple. Base part of the module has only two classes and one interface:

By initialization the process of establishing ready to use object of PersistenceManagerIF type is meant. There are two general ways to get one for yourself:

  • Create it manually using constructor of particular Manager.
  • Get it from PersistenceManagerConfig.

This method isn't good as you hard-code the name of implementation class in your application. In order to change implementation you will need to rebuild whole application (or module). This is how it's done on practice.

PersistenceManagerIF manager = new de.nava.informa.utils.manager.memory.PersistenceManager();

This way of establishing manager object is much more flexible. During initialization PersistenceManagerConfig object (which is following Singleton pattern) reads the value of informa.persistencemanager system property. This property can hold the name of class implementing PersistenceManagerIF. Here's how you can define the name of class on startup.

java -cp ... \ -Dinforma.persistencemanager=de.nava.informa.utils.manager.memory.PersistenceManager \ my.app.Main

In this example we specify that in-memory implementation required.

The other way to specify necessary class is passing its name at run-time using appropriate method of PersistenceManagerConfig class.

setPersistenceManagerClassName("de.nava.informa.utils.manager.memory.PersistenceManager");

This piece of code initializes the same manager.

When initialization is done in one of these ways you can get your manager by making a direct call to configuration object:

PersistenceManagerIF manager = PersistenceManagerConfig.getPersistenceManager();

Now that you have manager object what you can do with it? Many things. Lets create some channels for the start.

Channels in Informa are rich on properties, but to create channel you need to know only few things: its title and location. Without these two properties channels have no practical meaning. Lets create one.

URL url = new URL("http://my.home.net/rss.xml"); ChannelIF newChannel = manager.createChannel("My home blog", url);

Creating the channel doesn't mean that it will be automatically populated with items from the given location. This only means that it will be stored in persistent storage. You should take care of reading channels' data yourself, using Informa parsers or by means of another convenient utility module - Poller (check de.nava.informa.utils.poller package).

Now you have your new channel. Lets add some items to it.

Items cannot live on their own in wild nature, they should always have parent channel. As we already have one, created in the previous step, we can proceed to creation of items. The simplest form is when item has only the title. So, necessary minimum is title.

ItemIF newItem = manager.createItem(newChannel, "Item title");

This command creates new item and puts it into the channel.

There is another way to create item. Sometimes it's necessary to duplicate existing item by copying all of its properties. It can be done easily by calling appropriate item creation method of manager.

ItemIF newItem = manager.createItem(newChannel, itemToCopy);

Sometimes it's necessary to organize the channels into groups. Each channel can reside in several groups and can also live on its own without any groups associated. To create empty group you should make the following call.

ChannelGroupIF newGroup = manager.createGroup("My new group");

To assign the channel to this new group you should make another call.

manager.addChannelToGroup(newChannel, newGroup);

Later you may decide to remove channel from group. Do so by third call.

manager.removeChannelFromGroup(newChannel, newGroup);

You might need to move channels from one group to another, avoiding duplicates, and delete one of the groups. This is called merging. Assume that we have groupA and groupB. We wish to leave with only groupA aboard, having all channels from groupB in it. This is how we can reach our goal.

manager.mergeGroups(groupA, groupB);

When deleting groups only group objects are removed from storage. It's because channels can live alone, not being in any groups. Here's how groups are deleted.

manager.deleteGroup(group);

This topic is going after groups intentionally. You should first learn the basics of grouping to understand how deleting of channels works. When channel is deleted it's automatically removed from all groups it currently in. Also, all items of the channel are also removed from persistent storage. This means that the mentioned objects are no longer valid persistent entities and manager may start throwing exceptions (if it has no means of handling non-stored entities) if you will try to continue performing operations over them. To delete channel make a call to manager.

manager.deleteChannel(channel);

Deleting items is very simple. Also has no pitfalls.

manager.deleteItem(item);

Well, by this moment we know how to create several entities, but how we can get these entities back when we restart our application? Each of the entities provides methods to get their children. You can get list of channels from group and list of items from channel. The only thing left to learn is how to get the list of groups. Manager can give it to you.

ChannelGroupIF[] groups = manager.getGroups();

Attentive reader may ask, about getting the list of channels which are not in groups? Yes, by this moment manager interface has no method to get the list of groupless channels. You can simply avoid these situations by using default group to keep "ungrouped" channels in it.

Here comes the tricky part. It's always welcome if you notify manager about changes in objects. Of course, it's not necessary to tell manager about operations you just did by it's own means, but you'd better tell it when you make your own modifications (changing title of channels, or manipulating read state of an item and others). It's a very simple rule. In most cases forgetting to notify manager about changes will not be a big problem and next time you will request some operation over your objects manager will try to update the database according to noticed changes, but you'd better do not rely on this much. First, because it's not reliable enough for some implementations (for example, Hibernate). And second, some of implementations will require significantly more operations to parse your objects hierarchy in searching these changes.

You may ask: "Why are you haven't made automatic changes detection if it's so vital?". Answering... Because if we will detect each particular change in sequence of 100 modifications we will have 100 update statements. And if we will make all of our modifications and then notify the manager we will have significantly less of them. Moreover, we can have even single statement if all the changes were made on behalf of single object.

This is how we do our updates:

URL url = new URL("http://my.home.net/rss.xml"); ChannelIF newChannel = manager.createChannel("My home blog", url); ... // Update and notify manager newChannel.setTitle("Some new title"); manager.updateChannel(newChannel); // The similar picture with groups and channels manager.updateGroup(someGroup); manager.updateItem(someItem);