General

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.

Module structure

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

  • PersistenceManagerIF - main interface of any Persistence Manager. This interface defines contract between client application and persistence management implementation. Interface provides methods, which are necessary to create groups of channels, channels and items, populate groups with channels and channels with items, delete each of them and perform some other valuable manipulations. The main benefit is that you don't need to know how things work internally, you just need to know what you wish to do.
  • PersistenceManagerConfig - configuration storage. This is the factory for PersistenceManagerIF implementations. You can use this class to get manager object at run-time without knowing what implementation is used in particular. In other words, you specify what implementation to use by setting system properties and do not hard-code the names of classes and packages in code.
  • PersistenceManagerException - type of exception, thrown in error cases by PersistenceManagerIF.

Working with Persistence Manager

Initialization

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.

Creating manager manually

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();

Getting manager from PersistenceManagerConfig

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.

Creating channels

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.

Creating items

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);

Creating groups of channels

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);

Merging groups

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);

Deleting groups

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);

Deleting channels

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

Deleting items is very simple. Also has no pitfalls.

manager.deleteItem(item);

Getting groups, channels and items

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.

Updating groups, channels and items

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);

Limitations

  • Current version supports only groups (ChannelGroupIF), channels (ChannelIF) and items (ItemIF). It's not planned to extend the library by now as it looks sufficient for the most applications to have only these three kinds of entities.
  • When working with manager make sure that you are not using externally created objects of the types understood by manager. For example, you may have some ChannelIF objects unserialized from XML or created manually. Using them with manager will not work. Manager implementation is free to support these cases, but it also free to not to do so. Please be careful with where you get your objects.