Foren

Overridding DataSourceFactoryBean - Ext Plugin

Ohad Raz, geändert vor 11 Jahren.

Overridding DataSourceFactoryBean - Ext Plugin

New Member Beiträge: 23 Beitrittsdatum: 27.06.12 Neueste Beiträge
Hello,

I would like to start by saying that I am new to Liferay, and posting this thread in continuance to earlier thread I posted: Data Source.
I understand that Liferay maintains its own database schema (I refer to the default Hypersonic thing).
I want Liferay to use Oracle, and I understand I can configure it in the portal-ext.properties file.
But I would like to use a configuration file which is a standard in the organization I am working on. I have a library that "knows" where to look for these configuration files, knows how to parse them, and how to produce a data source.
I learned that there is this Ext Plugin thing, that lets one override default Liferay's configurations and implementations.
So, I found that the datasource is created by the com.liferay.portal.dao.jdbc.DataSourceFactoryImpl class. Using debug, I see that it is used by the com.liferay.portal.dao.jdbc.spring.DataSourceFactoryBean class, which is a Spring factory bean class.
So I figured that I can override this bean configuration, and define my own factory bean (instead of the DataSourceFactoryBean), giving it the same ID as the DataSourceDacrotyBean. This new bean (my bean, that replaces the DataSourceDacrotyBean) will use the aforementioned library, and will produce a data source based on the "organization standard" configuration files.
I did a little search and found that the DataSourceFactoryBean is defined in these two XML files:
  • portal-impl/src/META-INF/counter-spring.xml
  • portal-impl/src/META-INF/infrastructure-spring.xml

and as an inner bean in these XMLs:
  • portal-impl/src/META-INF/shard-data-source-spring.xml
  • portal-impl/src/META-INF/dynamic-data-source-spring.xml.

So I have a few questions:
  • Does my idea is correct and realistic? Can I override these bean configurations in a way that my new bean (that replaces the DataSourceDacrotyBean) will become the manager of the data sources for the whole of Liferay?
  • Which of the aforementioned XMLs should I override? Do I need to override all?
  • How to use EXT plugin? Say that I want to override the definition of com.liferay.portal.dao.jdbc.spring.DataSourceFactoryBean, in portal-impl/src/META-INF/dynamic-data-source-spring.xml. Should I just put the new definition in my EXT plugin, or should copy the whole file, and change the specific bean I am interested in?
  • Should I use a single EXT plugin, or each override should have its own EXT plugin?
  • What are the rules and best practices? Should my new bean be by the same name and package as the one I override?
  • I saw in http://www.liferay.com/community/wiki/-/wiki/Main/Database+Configuration that one can use the EXT environment to do something similar to what I am trying to do. But I learned that in Liferay 6 the "old" EXT environment was replaced with the new EXT plugin. So this page seems to be out of date. Where can I find an up to date documentation similar to that?
  • Everywhere I looked it is described as implementing an EXT plugin is very simple. However, as I have these so many questions, it is likely not... (or that I am a complete dumb, which is also a valid option...). Where can I find good, detailed documentations, examples, best practices, how-to, etc. of the EXT plugin?


Thanks in advance,
Ohad
thumbnail
David H Nebinger, geändert vor 11 Jahren.

RE: Overridding DataSourceFactoryBean - Ext Plugin

Liferay Legend Beiträge: 14919 Beitrittsdatum: 02.09.06 Neueste Beiträge
The EXT plugin on the 6.x line gives the most access to overriding and extending the portal core, but in the end it does not provide total access.

As this relates to your case, I do not believe you can deploy an EXT plugin that replaces the core's spring configuration files.

I think in the end you'd be stuck w/ editing the spring config files in place to change the use of Liferay's DataSourceFactoryBean with your own implementation. Since you're replacing them manually with your own, you should probably also put the code for your own implementation in there too (package as a jar and drop into the ROOT/WEB-INF/lib folder) so, in the case where the EXT is undeployed or fails to load, at least the core will be able to start up correctly.

This is the way you would do it, but let's now discuss the implications... First it means that you will have a lot of manual work to do when performing a Liferay version upgrade (even a minor dot release). You'd have to re-apply your changes to the new version (since they are not standard), and you'll also have to perform another scan against the new release to see if there are additional places you now have to apply the changes to. For re-applying your changes, you can't just copy your files in overtop of the files in the new release because Liferay may have made changes in there that you might break if you dropped an older version in there.

Second, it depends on your organization knowing the changes are needed and how to apply them. You may get hit by a bus, so they should not be dependent upon you to do the next deployment. And as there is a big gap between releases, you're going to want the process fully documented so it will be easier to re-apply the changes the next go round.

Third, if you're running EE you may find it difficult or impossible to apply some of the fix packs. The fix packs are designed to apply updates to an unmodified core; since your core is modified, you might not be able to apply them correctly.

Finally, you asked about best practices. Best practices, with respect to Liferay, is to never modify the core directly (due to many of the things I wrote above). Liferay provides the ability to either set the connection parameters using the portal-ext.properties file or a JNDI definition for the datasource to use, and it's really the only contract you can depend upon from release to release that will just work.

If someone were stuffing this "organizational standard" down my throat, in the end I would do the JNDI route. I'd create an implementation of the DataSource interface that would invoke the library that actually creates the datasource, and my implementation would become a wrapper over the datasource created by that library. Register the DataSource wrapper within the application container in JNDI and let Liferay use that implementation.

You'd still invoke the custom library to create the appropriate datasource, and this would not require any modification to the Liferay core. Since the core is unmodified, it can be upgraded and patched easily.
Ohad Raz, geändert vor 11 Jahren.

RE: Overridding DataSourceFactoryBean - Ext Plugin

New Member Beiträge: 23 Beitrittsdatum: 27.06.12 Neueste Beiträge
David,

Thanks for the detailed response.
With this thread, and the one I posted earlier (to which you also replied), I see there is no way I can set a data source of my own, that will be the manager of the whole Liferay, except for the JNDI.
I thought of the JNDI option in the first place, but then neglected it, as I cannot control when the binding will happen. So, it just might be that Liferay will try to set up the datasource prior to the DS being bound, which may lead to a JNDI naming exception.
However, since you mentioned it, is there a way for me to control it, and have my bean that creates the DS and binds it to some JNDI name to load before Liferay do the lookup?

Thanks,
Ohad
thumbnail
David H Nebinger, geändert vor 11 Jahren.

RE: Overridding DataSourceFactoryBean - Ext Plugin

Liferay Legend Beiträge: 14919 Beitrittsdatum: 02.09.06 Neueste Beiträge
You register the data source in jndi in the application container, so it will be created when the app container starts up.

Passing the JNDI reference to Liferay, it will use JNDI to get the registered datasource, it will not create one of it's own.

Therefore you have total control over when it will be created.

Personally I'd probably implement a lazy DS creation method. Register the DataSource wrapper implementation with JNDI, but do not create the actual DataSource representing the Oracle connection until one of it's methods are called (but that's just the way I'd do it, you're free to implement an active/immediate connection when your DataSource is being instantiated by the container).

The way the Liferay code works, if you have a JNDI reference defined in portal-ext.properties, Liferay will use that one and ignore the jdbc.default.XXX attributes and will not create it's own DataSource.
Ohad Raz, geändert vor 11 Jahren.

RE: Overridding DataSourceFactoryBean - Ext Plugin

New Member Beiträge: 23 Beitrittsdatum: 27.06.12 Neueste Beiträge
David,

Thank you for your response, I really appreciate it.

Yes, I read that Liferay first checks for the JNDI name, and if set, use it.
I also saw in the code that it has an if statement, where if the jdbc.default.jndi.name property is set, then do a JNDI lookup (otherwise, read the rest of the jdbc.default.XXX and create a DS accordingly).

But still, I cannot set the DS in the container level... Because this is, once again, not using the "company standard" configuration file...

Think of the process as if I have to bundle everything up together - the web container, Liferay and the actual application in a single tarball artifact. This artifact also has a file, specifying the "company standard" configuration version. During installation of the tarball, this file is read, retrieves and installs the config in a certain place (say, in /opt/{company}/config/{application}/{version}). As the server is started, there are Spring beans that know how to read this file, and to create a DS. In Spring, one can be sure that the DS is created before any other bean that might need it is loaded due to Spring's dependency resolving mechanism, or, if not sufficient, the depends-on attribute added to the bean element in the beans definition.

So as I said, the web container, Liferay, and the app are bundled together. With the idea of having the web container set the DS, I would have to have multiple specific web-container configuration files (for example, for Tomcat, I would have to have multiple artifacts, where the only difference is Tomcat's context.xml file, having a different Resource definition). Aside for having just too many versions of the same thing, this will simply won't do, because I wouldn't be able to say that a certain artifact (say, the one that has production database configuration) was ever tested, and it an artifact is not tested - it does not go into production...

So I need another way to make sure my DS is created and bind to some JNDI name before Liferay do the lookup.
Is there a mean for me to do so?
I may think of the startup servlets. Perhaps I can have a servlet that will take care of the DS thing loaded before LIferay's startup servlet gets loaded?
If so, where (in terms of between which startup servlets) should it come (I saw there were several startup servlets, and I am not sure which is the one that finally triggers the DS)?
Or is there another, perhaps, better way?...

Thanks,
Ohad
thumbnail
David H Nebinger, geändert vor 11 Jahren.

RE: Overridding DataSourceFactoryBean - Ext Plugin

Liferay Legend Beiträge: 14919 Beitrittsdatum: 02.09.06 Neueste Beiträge
I think you missed my idea entirely...

A generic data source wrapper which we'll call CompanyDataSource. This CompanyDataSource is only responsible for one thing; pulling in all of the code/logic/whatever to instantiate the actual DataSource instance using your messed up 'company standard' stuff.

Now this CompanyDataSource is not unique to each environment. It's a standard copy that is in every instance of Tomcat on every server. The tomcat side of things don't change, it can be named the same thing so the portal-ext.properties and Liferay side of things don't change...

Sounds pretty much like the exact answer you're looking for...

CompanyDataSource could be used to do some sort of IP/path mapping, or hostname/path mapping or some sort of environment variable/path mapping, but the single instance definition can be coded to return the correct connection info regardless of the server/tomcat/environment it's being used from.

And I really don't understand why you need to control when the DS is actually instantiated... Whether it is done right away by the app container or somewhere down the line when Liferay needs it, as long as it is instantiated, ready, and used by Liferay when it needs a connection to the database, that's all that matters.
Ohad Raz, geändert vor 11 Jahren.

RE: Overridding DataSourceFactoryBean - Ext Plugin

New Member Beiträge: 23 Beitrittsdatum: 27.06.12 Neueste Beiträge
David,

Yes, I have missed your idea at the first place.
Indeed, this idea of yours is likely what I was after.
I will give it a try tomorrow.

Regarding controlling when the DS is initialized, well, I was in the state of mind that it is up to me to create the DS, either as a Spring Bean, servlet, or an application context listener. In such a case, I have to be sure that when Liferay do the lookup, it'll find something. So this is what I worry about.
If, according to your idea, I will leave it to the container, then yes, I have nothing to worry about, the container will take care about all of this.

So your idea, once understood, sounds perfect!

Thank you once again for all you willingness and support.

Cheers,
Ohad