diff --git a/commons-exo-extension/src/main/webapp/WEB-INF/conf/commons-exo/cache-configuration.xml b/commons-exo-extension/src/main/webapp/WEB-INF/conf/commons-exo/cache-configuration.xml new file mode 100644 index 00000000..44590f11 --- /dev/null +++ b/commons-exo-extension/src/main/webapp/WEB-INF/conf/commons-exo/cache-configuration.xml @@ -0,0 +1,1354 @@ + + + + + + org.infinispan.transaction.lookup.TransactionManagerLookup + org.exoplatform.services.transaction.infinispan.JBossStandaloneJTAManagerLookup + + + + org.exoplatform.services.transaction.TransactionService + org.exoplatform.services.transaction.infinispan.JBossTransactionsService + + + timeout + ${gatein.jcr.transaction.timeout:420} + + + + + + org.exoplatform.services.cache.ExoCacheFactory + org.exoplatform.services.cache.impl.infinispan.ExoCacheFactoryImpl + + + cache.config.template + ${exo.cache.config.template:jar:/conf/cache/infinispan/local/cache-config.xml} + + + cache.config.template + ${exo.cache.config.template:jar:/conf/cache/infinispan/cluster/cache-config.xml} + + + cache.async.config.template + ${exo.cache.async.config.template:jar:/conf/cache/infinispan/cluster/cache-async-config.xml} + + + + + + org.exoplatform.services.cache.ExoCacheFactory + + addCreator + addCreator + org.exoplatform.services.cache.impl.infinispan.ExoCacheCreatorPlugin + add Exo Cache Creator + + + Generic-insp-cacheCreator + The generic cache creator + + + + + NONE + + + LRU + + + UNORDERED + + + MANUAL + + + LIRS + + + + + ${exo.cache.eviction.defaultStrategy:LIRS} + + + ${exo.cache.expiration.defaultMaxIdle:-1} + + + ${exo.cache.expiration.defaultWakeUpInterval:5000} + + + + + + + + + org.exoplatform.services.cache.CacheService + + addExoCacheConfig + addExoCacheConfig + org.exoplatform.services.cache.ExoCacheConfigPlugin + Configures the cache for analytics queue which must remain a local cache + + + analytics.queue + + + + analytics.queue + + + ${analytics.queue.MaxNodes:2000} + + + ${analytics.queue.TimeToLive:-1} + + + + + + commons.WebNotificationCache + The Cache configuration for the Web Notification Cache + + + commons.WebNotificationCache + + + ${exo.cache.commons.WebNotificationCache.strategy:LIRS} + + + ${exo.cache.commons.WebNotificationCache.TimeToLive:86400} + + + ${exo.cache.commons.WebNotificationCache.MaxNodes:100000} + + + ${exo.cache.commons.WebNotificationCache.cacheMode:replication} + + + + + + commons.WebNotificationsCache + The Cache configuration for the Web Notification List Cache + + + commons.WebNotificationsCache + + + ${exo.cache.commons.WebNotificationsCache.strategy:LIRS} + + + ${exo.cache.commons.WebNotificationsCache.TimeToLive:86400} + + + ${exo.cache.commons.WebNotificationsCache.MaxNodes:1000} + + + ${exo.cache.commons.WebNotificationsCache.cacheMode:replication} + + + + + + commons.WebNotificationCountCache + The Cache configuration for the Web Notification Count Cache + + + commons.WebNotificationCountCache + + + ${exo.cache.commons.WebNotificationCountCache.strategy:LIRS} + + + ${exo.cache.commons.WebNotificationCountCache.TimeToLive:86400} + + + ${exo.cache.commons.WebNotificationCountCache.MaxNodes:1000} + + + ${exo.cache.commons.WebNotificationCountCache.cacheMode:replication} + + + + + + commons.UserSettingService + The Cache configuration for the UserSettingService + + + commons.UserSettingService + + + ${exo.cache.commons.UserSettingService.strategy:LIRS} + + + ${exo.cache.commons.UserSettingService.MaxNodes:500} + + + ${exo.cache.commons.UserSettingService.TimeToLive:86400} + + + ${exo.cache.commons.UserSettingService.cacheMode:asyncInvalidation} + + + + + + commons.UserStateService + The Cache configuration for the UserStateService + + + commons.UserStateService + + + ${exo.cache.commons.UserStateService.strategy:LIRS} + + + ${exo.cache.commons.UserStateService.MaxNodes:1000} + + + ${exo.cache.commons.UserStateService.TimeToLive:86400} + + + ${exo.cache.commons.UserStateService.cacheMode:replication} + + + + + gamification.domain + + + gamification.domain + + + ${exo.cache.gamification.domain.MaxNodes:1000} + + + ${exo.cache.gamification.domain.TimeToLive:-1} + + + + + gamification.rule + + + gamification.rule + + + ${exo.cache.gamification.rule.MaxNodes:2000} + + + ${exo.cache.gamification.rule.TimeToLive:-1} + + + + + gamification.realization + + + gamification.realization + + + ${exo.cache.gamification.realization.MaxNodes:5000} + + + ${exo.cache.gamification.realization.TimeToLive:-1} + + + + + wiki.PageRenderingCache + The wiki markup cache configuration + + + wiki.PageRenderingCache + + + ${exo.cache.wiki.PageRenderingCache.strategy:LIRS} + + + ${exo.cache.wiki.PageRenderingCache.MaxNodes:1000} + + + ${exo.cache.wiki.PageRenderingCache.TimeToLive:86400} + + + ${exo.cache.wiki.PageRenderingCache.cacheMode:replication} + + + + + wiki.PageAttachmentCache + The Cache configuration for wiki page attachment count + + + wiki.PageAttachmentCache + + + ${exo.cache.wiki.PageAttachmentCache.strategy:LIRS} + + + ${exo.cache.wiki.PageAttachmentCache.MaxNodes:1000} + + + ${exo.cache.wiki.PageAttachmentCache.TimeToLive:86400} + + + ${exo.cache.wiki.PageAttachmentCache.cacheMode:replication} + + + + + perkstore.product + + + perkstore.product + + + ${exo.cache.perkstore.product.MaxNodes:1000} + + + ${exo.cache.perkstore.product.TimeToLive:-1} + + + + + perkstore.order + + + perkstore.order + + + ${exo.cache.perkstore.order.MaxNodes:2000} + + + ${exo.cache.perkstore.order.TimeToLive:-1} + + + + + portal.User + Exo Cache cluster configuration for IDM User entity + + + portal.User + + + ${exo.cache.portal.user.strategy:LIRS} + + + ${exo.cache.portal.user.MaxNodes:5000} + + + ${exo.cache.portal.user.TimeToLive:3600} + + + ${exo.cache.portal.user.cacheMode:replication} + + + + + portal.Profile + Exo Cache cluster configuration for IDM User Profile entity + + + portal.Profile + + + ${exo.cache.portal.profile.strategy:LIRS} + + + ${exo.cache.portal.profile.MaxNodes:5000} + + + ${exo.cache.portal.profile.TimeToLive:3600} + + + ${exo.cache.portal.profile.cacheMode:replication} + + + + + portal.Membership + Exo Cache cluster configuration for IDM Membership entity + + + portal.Membership + + + ${exo.cache.portal.membership:LIRS} + + + ${exo.cache.portal.membership.MaxNodes:5000} + + + ${exo.cache.portal.membership.TimeToLive:86400} + + + ${exo.cache.portal.membership.cacheMode:replication} + + + + + + portal.Role + Exo Cache cluster configuration for IDM Membership Type entity + + + portal.Role + + + ${exo.cache.portal.role.strategy:LIRS} + + + ${exo.cache.portal.role.MaxNodes:5000} + + + ${exo.cache.portal.role.TimeToLive:-1} + + + ${exo.cache.portal.role.cacheMode:replication} + + + + + + portal.Group + Exo Cache cluster configuration for IDM Group entity + + + portal.Group + + + ${exo.cache.portal.group.strategy:LIRS} + + + ${exo.cache.portal.group.MaxNodes:5000} + + + ${exo.cache.portal.group.TimeToLive:-1} + + + ${exo.cache.portal.group.cacheMode:replication} + + + + + + commons.SettingService + The Cache configuration for the setting service + + + commons.SettingService + + + ${exo.cache.commons.SettingService.MaxNodes:100000} + + + ${exo.cache.commons.SettingService.TimeToLive:86400} + + + ${exo.cache.commons.SettingService:replication} + + + ${exo.cache.commons.SettingService.strategy:LIRS} + + + + + + portal.PortletPreferences + The cache configuration for Portlet Preferences + + + portal.PortletPreferences + + + ${exo.cache.portal.preferences.strategy:LIRS} + + + ${exo.cache.portal.preferences.MaxNodes:10000} + + + ${exo.cache.portal.preferences.TimeToLive:86400} + + + ${exo.cache.portal.preferences.cacheMode:invalidation} + + + + + portal.NavigationNode + The Cache configuration for the MOP session Manager + + + portal.NavigationNode + + + ${exo.cache.portal.mop.strategy:LIRS} + + + ${exo.cache.portal.mop.MaxNodes:5000} + + + ${exo.cache.portal.mop.TimeToLive:86400} + + + ${exo.cache.portal.mop.cacheMode:invalidation} + + + + + portal.NavigationService + The cache configuration for the navigation Trees service + + + portal.NavigationService + + + ${exo.cache.portal.navigation.strategy:LIRS} + + + ${exo.cache.portal.navigation.MaxNodes:1000} + + + ${exo.cache.portal.navigation.TimeToLive:86400} + + + ${exo.cache.portal.navigation.cacheMode:invalidation} + + + + + portal.SiteService + The cache configuration for Site layout service + + + portal.SiteService + + + ${exo.cache.portal.site.strategy:LIRS} + + + ${exo.cache.portal.site.MaxNodes:1000} + + + ${exo.cache.portal.site.TimeToLive:86400} + + + ${exo.cache.portal.site.cacheMode:invalidation} + + + + + portal.SiteKeysByFilterService + The cache configuration for Site layout service + + + portal.SiteKeysByFilterService + + + ${exo.cache.portal.site.strategy:LIRS} + + + ${exo.cache.portal.site.MaxNodes:1000} + + + ${exo.cache.portal.site.TimeToLive:86400} + + + ${exo.cache.portal.site.cacheMode:invalidation} + + + + + portal.DescriptionService + The Cache configuration for the description service + + + portal.DescriptionService + + + ${exo.cache.portal.description.strategy:LIRS} + + + ${exo.cache.portal.description.MaxNodes:10000} + + + ${exo.cache.portal.description.TimeToLive:86400} + + + ${exo.cache.portal.description.cacheMode:invalidation} + + + + + portal.PageService + The Cache configuration for the page service + + + portal.PageService + + + ${exo.cache.portal.page.strategy:LIRS} + + + ${exo.cache.portal.page.MaxNodes:1000} + + + ${exo.cache.portal.page.TimeToLive:86400} + + + ${exo.cache.portal.page.cacheMode:invalidation} + + + + + + portal.TemplateService + The Cache configuration for the template service + + + portal.TemplateService + + + ${exo.cache.portal.template.strategy:LIRS} + + + ${exo.cache.portal.template.MaxNodes:5000} + + + ${exo.cache.portal.template.TimeToLive:-1} + + + ${exo.cache.portal.template.cacheMode:invalidation} + + + + + portal.ResourceBundleData + + + + portal.ResourceBundleData + + + ${exo.cache.portal.ResourceBundleData.MaxNodes:1000} + + + ${exo.cache.portal.ResourceBundleData.TimeToLive:-1} + + + + + + social.IdentityCache + The Cache configuration for the IdentityCache + + + social.IdentityCache + + + ${exo.cache.social.IdentityCache.strategy:LIRS} + + + ${exo.cache.social.IdentityCache.MaxNodes:1100} + + + ${exo.cache.social.IdentityCache.TimeToLive:86400} + + + ${exo.cache.social.IdentityCache.cacheMode:replication} + + + + + social.IdentityIndexCache + The Cache configuration for the IdentityIndexCache + + + social.IdentityIndexCache + + + ${exo.cache.social.IdentityIndexCache.strategy:LIRS} + + + ${exo.cache.social.IdentityIndexCache.MaxNodes:1100} + + + ${exo.cache.social.IdentityIndexCache.TimeToLive:86400} + + + ${exo.cache.social.IdentityIndexCache.cacheMode:replication} + + + + + social.ProfileCache + The Cache configuration for the ProfileCache + + + social.ProfileCache + + + ${exo.cache.social.ProfileCache.strategy:LIRS} + + + ${exo.cache.social.ProfileCache.MaxNodes:1100} + + + ${exo.cache.social.ProfileCache.TimeToLive:86400} + + + ${exo.cache.social.ProfileCache.cacheMode:replication} + + + + + social.IdentitiesCountCache + The Cache configuration for the IdentitiesCountCache + + + social.IdentitiesCountCache + + + ${exo.cache.social.IdentitiesCountCache.strategy:LIRS} + + + ${exo.cache.social.IdentitiesCountCache.MaxNodes:1100} + + + ${exo.cache.social.IdentitiesCountCache.TimeToLive:86400} + + + ${exo.cache.social.IdentitiesCountCache.cacheMode:replication} + + + + + social.IdentitiesCache + The Cache configuration for the IdentitiesCache + + + social.IdentitiesCache + + + ${exo.cache.social.IdentitiesCache.strategy:LIRS} + + + ${exo.cache.social.IdentitiesCache.MaxNodes:1100} + + + ${exo.cache.social.IdentitiesCache.TimeToLive:86400} + + + ${exo.cache.social.IdentitiesCache.cacheMode:replication} + + + + + + + social.RelationshipCache + The Cache configuration for the RelationshipCache + + + social.RelationshipCache + + + ${exo.cache.social.RelationshipCache.strategy:LIRS} + + + ${exo.cache.social.RelationshipCache.MaxNodes:100000} + + + ${exo.cache.social.RelationshipCache.TimeToLive:86400} + + + ${exo.cache.social.RelationshipCache.cacheMode:replication} + + + + + social.RelationshipFromIdentityCache + The Cache configuration for the RelationshipFromIdentityCache + + + social.RelationshipFromIdentityCache + + + ${exo.cache.social.RelationshipFromIdentityCache.strategy:LIRS} + + + ${exo.cache.social.RelationshipFromIdentityCache.MaxNodes:100000} + + + ${exo.cache.social.RelationshipFromIdentityCache.TimeToLive:86400} + + + ${exo.cache.social.RelationshipFromIdentityCache.cacheMode:replication} + + + + + social.RelationshipsCountCache + The Cache configuration for the RelationshipsCountCache + + + social.RelationshipsCountCache + + + ${exo.cache.social.RelationshipsCountCache.strategy:LIRS} + + + ${exo.cache.social.RelationshipsCountCache.MaxNodes:10000} + + + ${exo.cache.social.RelationshipsCountCache.TimeToLive:86400} + + + ${exo.cache.social.RelationshipsCountCache.cacheMode:replication} + + + + + social.RelationshipsCache + The Cache configuration for the RelationshipsCache + + + social.RelationshipsCache + + + ${exo.cache.social.RelationshipsCache.strategy:LIRS} + + + ${exo.cache.social.RelationshipsCache.MaxNodes:10000} + + + ${exo.cache.social.RelationshipsCache.TimeToLive:86400} + + + ${exo.cache.social.RelationshipsCache.cacheMode:replication} + + + + + social.SuggestionsCache + The Cache configuration for the SuggestionsCache + + + social.SuggestionsCache + + + ${exo.cache.social.SuggestionsCache.strategy:LIRS} + + + ${exo.cache.social.SuggestionsCache.MaxNodes:1000} + + + ${exo.cache.social.SuggestionsCache.TimeToLive:86400} + + + ${exo.cache.social.SuggestionsCache.cacheMode:replication} + + + + + + + social.ActivityCache + The Cache configuration for the ActivityCache + + + social.ActivityCache + + + ${exo.cache.social.ActivityCache.strategy:LIRS} + + + ${exo.cache.social.ActivityCache.MaxNodes:20000} + + + ${exo.cache.social.ActivityCache.TimeToLive:86400} + + + ${exo.cache.social.ActivityCache.cacheMode:replication} + + + + + social.ActivitiesCountCache + + + + ${exo.cache.social.ActivitiesCountCache.strategy:LIRS} + + + social.ActivitiesCountCache + + + ${exo.cache.social.ActivitiesCountCache.MaxNodes:20000} + + + ${exo.cache.social.ActivitiesCountCache.TimeToLive:86400} + + + ${exo.cache.social.ActivitiesCountCache.cacheMode:replication} + + + + + social.ActivitiesCache + + + + ${exo.cache.social.ActivitiesCache.strategy:LIRS} + + + social.ActivitiesCache + + + ${exo.cache.social.ActivitiesCache.MaxNodes:20000} + + + ${exo.cache.social.ActivitiesCache.TimeToLive:86400} + + + ${exo.cache.social.ActivitiesCache.cacheMode:replication} + + + + + + + social.SpaceCache + The Cache configuration for the SpaceCache + + + social.SpaceCache + + + ${exo.cache.social.SpaceCache.strategy:LIRS} + + + ${exo.cache.social.SpaceCache.MaxNodes:100} + + + ${exo.cache.social.SpaceCache.TimeToLive:86400} + + + ${exo.cache.social.SpaceCache.cacheMode:replication} + + + + + social.SpaceSimpleCache + + + + social.SpaceSimpleCache + + + ${exo.cache.social.SpaceSimpleCache.MaxNodes:500} + + + ${exo.cache.social.SpaceSimpleCache.TimeToLive:86400} + + + + + social.SpaceRefCache + The Cache configuration for the SpaceRefCache + + + social.SpaceRefCache + + + ${exo.cache.social.SpaceRefCache.strategy:LIRS} + + + ${exo.cache.social.SpaceRefCache.MaxNodes:100} + + + ${exo.cache.social.SpaceRefCache.TimeToLive:86400} + + + ${exo.cache.social.SpaceRefCache.cacheMode:replication} + + + + + social.SpacesCountCache + The Cache configuration for the SpacesCountCache + + + social.SpacesCountCache + + + ${exo.cache.social.SpacesCountCache.strategy:LIRS} + + + ${exo.cache.social.SpacesCountCache.MaxNodes:4000} + + + ${exo.cache.social.SpacesCountCache.TimeToLive:86400} + + + ${exo.cache.social.SpacesCountCache.cacheMode:replication} + + + + + social.SpacesCache + The Cache configuration for the SpacesCache + + + social.SpacesCache + + + ${exo.cache.social.SpacesCache.strategy:LIRS} + + + ${exo.cache.social.SpacesCache.MaxNodes:4000} + + + ${exo.cache.social.SpacesCache.TimeToLive:86400} + + + ${exo.cache.social.SpacesCache.cacheMode:replication} + + + + + social.translation + The Cache configuration for the TranslationStorage + + + social.translation + + + ${exo.cache.social.TranslationCache.strategy:LIRS} + + + ${exo.cache.social.TranslationCache.MaxNodes:5000} + + + ${exo.cache.social.TranslationCache.TimeToLive:86400} + + + ${exo.cache.social.TranslationCache.cacheMode:replication} + + + + + social.linkSettings + The Cache configuration for the LinkStorage + + + social.linkSettings + + + ${exo.cache.social.LinkSettingsCache.strategy:LIRS} + + + ${exo.cache.social.LinkSettingsCache.MaxNodes:5000} + + + ${exo.cache.social.LinkSettingsCache.TimeToLive:86400} + + + ${exo.cache.social.TranslationCache.cacheMode:replication} + + + + + social.cmsSetting + The Cache configuration for the CMSStorage + + + social.cmsSetting + + + ${exo.cache.social.cmsSettingCache.strategy:LIRS} + + + ${exo.cache.social.cmsSettingCache.MaxNodes:5000} + + + ${exo.cache.social.cmsSettingCache.TimeToLive:86400} + + + ${exo.cache.social.TranslationCache.cacheMode:replication} + + + + + + social.ProfileAvatar + The Cache configuration for the ProfileAvatar + + + social.ProfileAvatar + + + ${exo.cache.social.ProfileAvatar.strategy:LIRS} + + + ${exo.cache.social.ProfileAvatar.MaxNodes:300} + + + ${exo.cache.social.ProfileAvatar.TimeToLive:600} + + + ${exo.cache.social.TranslationCache.cacheMode:replication} + + + + + wallet.transactions + Wallet application transactions cache + + + wallet.transactions + + + ${exo.cache.wallet.transactions.MaxNodes:2000} + + + ${exo.cache.wallet.transactions.TimeToLive:-1} + + + ${exo.cache.wallet.transactions.strategy:LIRS} + + + ${exo.cache.wallet.transactions.cacheMode:asyncInvalidation} + + + + + wallet.account + + + + wallet.account + + + ${exo.cache.wallet.account.MaxNodes:2000} + + + ${exo.cache.wallet.account.TimeToLive:-1} + + + ${exo.cache.wallet.account.strategy:LIRS} + + + ${exo.cache.wallet.account.cacheMode:asyncInvalidation} + + + + + + + + diff --git a/commons-exo-extension/src/main/webapp/WEB-INF/conf/configuration.xml b/commons-exo-extension/src/main/webapp/WEB-INF/conf/configuration.xml new file mode 100644 index 00000000..8adc015f --- /dev/null +++ b/commons-exo-extension/src/main/webapp/WEB-INF/conf/configuration.xml @@ -0,0 +1,23 @@ + + + + war:/conf/commons-exo/cache-configuration.xml + diff --git a/commons-exo-extension/src/main/webapp/WEB-INF/conf/uiconf/portal/webui/container/ContainerConfigOption.groovy b/commons-exo-extension/src/main/webapp/WEB-INF/conf/uiconf/portal/webui/container/ContainerConfigOption.groovy deleted file mode 100644 index 1822acb2..00000000 --- a/commons-exo-extension/src/main/webapp/WEB-INF/conf/uiconf/portal/webui/container/ContainerConfigOption.groovy +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (C) 2023 eXo Platform SAS. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -import org.exoplatform.webui.core.model.SelectItemCategory; -import org.exoplatform.webui.core.model.SelectItemOption; - - List templates = new ArrayList(); - - SelectItemCategory table = new SelectItemCategory("table"); - table.addSelectItemOption(new SelectItemOption("simpleTable", - "", - "SimpleTableContainerLayout")); - table.addSelectItemOption(new SelectItemOption("siteTopBar", - "\n" + - " Everyone\n" + - " \n" + - " Everyone\n" + - " \n" + - " \n" + - " social\n" + - " TopBarLogo\n" + - " \n" + - " Company Logo\n" + - " Everyone\n" + - " false\n" + - " false\n" + - " \n" + - " \n" + - " \n" + - " layout-management\n" + - " SiteNavigation\n" + - " \n" + - " site navigation\n" + - " Everyone\n" + - " false\n" + - " false\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " social\n" + - " TopBarMenu\n" + - " \n" + - " Top Bar Menu \n" + - " Everyone\n" + - " false\n" + - " false\n" + - " \n" + - " \n" + - " \n" + - " \n" + - "\n", - "SiteTopBarContainerLayout")); - templates.add(table); - - SelectItemCategory row = new SelectItemCategory("row"); - row.addSelectItemOption(new SelectItemOption("simpleRow", - "", - "SimpleRowContainerLayout")); - templates.add(row); - - SelectItemCategory column = new SelectItemCategory("column"); - column.addSelectItemOption(new SelectItemOption("simpleColumn", - "" + - " " + - " SimpleColumnContainer" + - " " + - "", - "SimpleColumnContainerLayout")); - templates.add(column); - - return templates; diff --git a/exo.core.component.database/pom.xml b/exo.core.component.database/pom.xml new file mode 100644 index 00000000..e23e6c8a --- /dev/null +++ b/exo.core.component.database/pom.xml @@ -0,0 +1,187 @@ + + + + + + 4.0.0 + + + org.exoplatform.commons-exo + commons-exo + 7.0.x-SNAPSHOT + + + exo.core.component.database + + Meeds:: PLF Core :: Component :: Database Service + Implementation of Database Service of Exoplatform SAS eXo Core' project. + + + 0.18 + + + + + io.meeds.kernel + exo.kernel.component.common + + + io.meeds.kernel + exo.kernel.commons + + + io.meeds.kernel + exo.kernel.container + + + io.meeds.kernel + exo.kernel.component.cache + + + io.meeds.kernel + exo.kernel.commons.test + test + + + javax.resource + javax.resource-api + test + + + org.hsqldb + hsqldb + test + + + com.experlog + xapool + true + + + com.atomikos + transactions-jta + provided + + + org.mockito + mockito-all + + + + + org.jboss.logging + jboss-logging + + + javax.transaction + javax.transaction-api + + + org.hibernate.orm + hibernate-core + + + org.slf4j + slf4j-api + + + org.jboss.spec.javax.transaction + jboss-transaction-api_1.1_spec + + + org.jboss.logging + jboss-logging + + + org.javassist + javassist + + + + + commons-dbcp + commons-dbcp + runtime + + + xerces + xercesImpl + + + commons-pool + commons-pool + + + + + commons-pool + commons-pool + runtime + + + com.atomikos + transactions-jta + provided + + + org.mockito + mockito-all + + + + + + + + + maven-antrun-plugin + + + prepare-test-policy + process-test-resources + + + Creating Access Policy for tests + + + + + + + + + + + + + + + + + run + + + + + + + diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/DAO.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DAO.java new file mode 100644 index 00000000..f45a459f --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DAO.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import org.exoplatform.commons.utils.PageList; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.List; + +/** + * Created by The eXo Platform SAS Author : Tuan Nguyen + * tuan08@users.sourceforge.net Apr 4, 2006 + */ +public abstract class DAO +{ + + /** + * Logger. + */ + private static final Log LOG = ExoLogger.getLogger("exo.core.component.database.DAO"); + + protected ExoDatasource eXoDS_; + + protected DBObjectMapper mapper_; + + static int totalQueryTime = 0; + + static int totalBathTime = 0; + + static int totalCloseConnect = 0; + + public DAO(ExoDatasource datasource) + { + eXoDS_ = datasource; + mapper_ = new ReflectionMapper(); + } + + public DAO(ExoDatasource datasource, DBObjectMapper mapper) + { + eXoDS_ = datasource; + mapper_ = mapper; + } + + public ExoDatasource getExoDatasource() + { + return eXoDS_; + } + + abstract public T load(long id) throws Exception; + + abstract public PageList loadAll() throws Exception; + + abstract public void update(T bean) throws Exception; + + abstract public void update(List beans) throws Exception; + + abstract public void save(T bean) throws Exception; + + abstract public void save(List beans) throws Exception; + + abstract public void remove(T bean) throws Exception; + + abstract public T remove(long id) throws Exception; + + abstract public T createInstance() throws Exception; + + protected T loadUnique(String query) throws Exception + { + Connection connection = eXoDS_.getConnection(); + try + { + return loadUnique(connection, query); + } + finally + { + eXoDS_.closeConnection(connection); + } + } + + protected T loadUnique(Connection connection, String query) throws Exception + { + Statement statement = null; + try + { + statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(query); + if (!resultSet.next()) + { + return null; + } + T bean = createInstance(); + mapper_.mapResultSet(resultSet, bean); + resultSet.close(); + return bean; + } + finally + { + if (statement != null) + statement.close(); + } + } + + protected void loadInstances(String loadQuery, List list) throws Exception + { + Connection connection = eXoDS_.getConnection(); + try + { + loadInstances(connection, loadQuery, list); + } + finally + { + eXoDS_.closeConnection(connection); + } + } + + protected void loadInstances(Connection connection, String loadQuery, List list) throws Exception + { + Statement statement = connection.createStatement(); + ResultSet resultSet = null; + try + { + resultSet = statement.executeQuery(loadQuery); + while (resultSet.next()) + { + T bean = createInstance(); + mapper_.mapResultSet(resultSet, bean); + list.add(bean); + } + } + finally + { + if (resultSet != null) + { + try + { + resultSet.close(); + } + catch (Exception e) + { + LOG.debug("Could not close the result set"); + } + } + try + { + statement.close(); + } + catch (Exception e) + { + LOG.debug("Could not close the statement"); + } + } + } + + protected void execute(String query, T bean) throws Exception + { + Connection connection = eXoDS_.getConnection(); + try + { + execute(connection, query, bean); + } + finally + { + eXoDS_.closeConnection(connection); + } + } + + protected void execute(Connection connection, String query, T bean) throws Exception + { + PreparedStatement statement = connection.prepareStatement(query); + if (bean != null) + mapper_.mapUpdate(bean, statement); + statement.executeUpdate(); + eXoDS_.commit(connection); + statement.close(); + } + + public E loadDBField(String query) throws Exception + { + Connection connection = eXoDS_.getConnection(); + try + { + return this. loadDBField(connection, query); + } + finally + { + eXoDS_.closeConnection(connection); + } + } + + @SuppressWarnings("unchecked") + protected E loadDBField(Connection connection, String query) throws Exception + { + Statement statement = connection.createStatement(); + long startGet = System.currentTimeMillis(); + ResultSet resultSet = statement.executeQuery(query); + totalQueryTime += System.currentTimeMillis() - startGet; + if (!resultSet.next()) + return null; + E value = (E)resultSet.getObject(1); + resultSet.close(); + statement.close(); + return value; + } + + protected void execute(String template, List beans) throws Exception + { + Connection connection = eXoDS_.getConnection(); + try + { + execute(connection, template, beans); + } + finally + { + eXoDS_.closeConnection(connection); + } + } + + protected void execute(Connection connection, String template, List beans) throws Exception + { + PreparedStatement statement = connection.prepareStatement(template); + QueryBuilder builder = eXoDS_.getQueryBuilder(); + for (T bean : beans) + { + String query = builder.mapDataToSql(template, mapper_.toParameters(bean)); + statement.addBatch(query); + LOG.info(" addBatch " + query); + } + statement.executeBatch(); + statement.close(); + eXoDS_.commit(connection); + } + + public DBObjectMapper getDBObjectMapper() + { + return mapper_; + } + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObject.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObject.java new file mode 100644 index 00000000..cbda6af9 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObject.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +/** + * Created by The eXo Platform SAS + * Author : Tuan Nguyen tuan.nguyen@exoplatform.com Mar 15, 2007 + */ +abstract public class DBObject +{ + + protected long dbObjectId_ = -1; + + public long getDBObjectId() + { + return dbObjectId_; + } + + public void setDBObjectId(long id) + { + dbObjectId_ = id; + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObjectEvent.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObjectEvent.java new file mode 100644 index 00000000..0a4a6a14 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObjectEvent.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import org.exoplatform.services.listener.Event; + +/** + * Created by The eXo Platform SAS + * Author : Nhu Dinh Thuan nhudinhthuan@exoplatform.com Apr 6, 2007 + */ +public class DBObjectEvent, T extends DBObject> extends Event +{ + + public DBObjectEvent(String name, E e, T t) + { + super(name, e, t); + } + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObjectMapper.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObjectMapper.java new file mode 100644 index 00000000..f371fa9f --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObjectMapper.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; + +/** + * Created by The eXo Platform SAS + * Author : Nhu Dinh Thuan nhudinhthuan@exoplatform.com Mar 29, 2007 + */ +public interface DBObjectMapper +{ + + public void mapUpdate(T bean, PreparedStatement statement) throws Exception; + + public String[][] toParameters(T bean) throws Exception; + + public void mapResultSet(ResultSet res, T bean) throws Exception; + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObjectPageList.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObjectPageList.java new file mode 100644 index 00000000..90ed43c5 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObjectPageList.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.hibernate.Session; +import org.hibernate.query.Query; + +import org.exoplatform.commons.utils.PageList; + +/** + * @author Tuan Nguyen (tuan08@users.sourceforge.net) + * @since Oct 21, 2004 + * @version $Id: DBObjectPageList.java 5332 2006-04-29 18:32:44Z geaz $ + */ +public class DBObjectPageList extends PageList +{ + + private String findQuery_; + + private String countQuery_; + + private HibernateService service_; + + private Map binding = new HashMap(); + + public DBObjectPageList(HibernateService service, Class objectType) throws Exception + { + super(20); + service_ = service; + findQuery_ = "from o in class " + objectType.getName(); + countQuery_ = "select count(o) from " + objectType.getName() + " o"; + Session session = service_.openSession(); + List l = session.createQuery(countQuery_).list(); + Number count = (Number)l.get(0); + setAvailablePage(count.intValue()); + } + + public DBObjectPageList(HibernateService service, ObjectQuery oq) throws Exception + { + super(20); + service_ = service; + findQuery_ = oq.getHibernateQueryWithBinding(); + countQuery_ = oq.getHibernateCountQueryWithBinding(); + binding = oq.getBindingFields(); + + Session session = service_.openSession(); + + Query countQuery = session.createQuery(countQuery_); + bindFields(countQuery); + + List l = countQuery.list(); + + Number count = (Number)l.get(0); + setAvailablePage(count.intValue()); + } + + public DBObjectPageList(HibernateService service, int pageSize, String query, String countQuery) throws Exception + { + super(pageSize); + service_ = service; + findQuery_ = query; + countQuery_ = countQuery; + Session session = service_.openSession(); + List l = session.createQuery(countQuery_).list(); + Number count = (Number)l.get(0); + setAvailablePage(count.intValue()); + } + + @Override + protected void populateCurrentPage(int page) throws Exception + { + Session session = service_.openSession(); + Query query = session.createQuery(findQuery_); + bindFields(query); + + int from = getFrom(); + query.setFirstResult(from); + query.setMaxResults(getTo() - from); + currentListPage_ = query.list(); + } + + @Override + public List getAll() throws Exception + { + Session session = service_.openSession(); + + Query query = session.createQuery(findQuery_); + bindFields(query); + + return query.list(); + } + + /** + * Bind a value to a named query parameter. + * + * @param query + */ + private void bindFields(Query query) + { + for (Entry entry : binding.entrySet()) + { + query.setParameter(entry.getKey(), entry.getValue()); + } + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObjectQuery.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObjectQuery.java new file mode 100644 index 00000000..9c33c5ee --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBObjectQuery.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import org.exoplatform.services.database.annotation.Table; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @author Tuan Nguyen (tuan08@users.sourceforge.net) + * @since Nov 25, 2004 + * @version $Id: ObjectQuery.java 6006 2006-06-06 10:01:27Z thangvn $ + */ +public class DBObjectQuery +{ + + private static DateTimeFormatter ft_ = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + private Class type_; + + private String orderBy_; + + private String groupBy_; + + private List parameters_; + + private List selectParameter_; + + public DBObjectQuery(Class type) + { + type_ = type; + parameters_ = new ArrayList(3); + selectParameter_ = new ArrayList(10); + } + + public DBObjectQuery addEQ(String field, Object value) + { + if (value == null) + return this; + parameters_.add(new Parameter(field, " = ", value)); + return this; + } + + public DBObjectQuery addGT(String field, Object value) + { + if (value == null) + return this; + parameters_.add(new Parameter(field, " > ", value)); + return this; + } + + public DBObjectQuery addLT(String field, Object value) + { + if (value == null) + return this; + parameters_.add(new Parameter(field, " < ", value)); + return this; + } + + public DBObjectQuery addLIKE(String field, String value) + { + if (value == null || value.length() < 1) + return this; + parameters_.add(new Parameter(field, " LIKE ", optimizeInputString(value))); + return this; + } + + public DBObjectQuery addSUM(String field) + { + selectParameter_.add(new Parameter("SUM", field)); + return this; + } + + public DBObjectQuery addSelect(String field, String value) + { + selectParameter_.add(new Parameter(field, " AS ", value)); + return this; + } + + public DBObjectQuery addSelect(String... fields) + { + for (String field : fields) + { + selectParameter_.add(new Parameter(field, null, null)); + } + return this; + } + + public DBObjectQuery addSelect(String field) + { + selectParameter_.add(new Parameter(field, null, null)); + return this; + } + + public DBObjectQuery addSelectCount(String type) + { + selectParameter_.add(new Parameter("countselect", type)); + return this; + } + + public DBObjectQuery addSelectMaxMin(String op, String field) + { + selectParameter_.add(new Parameter(op, field)); + return this; + } + + public DBObjectQuery setAscOrderBy(String field) + { + orderBy_ = " ORDER BY " + field + " ASC"; + return this; + } + + public DBObjectQuery setDescOrderBy(String field) + { + orderBy_ = " ORDER BY " + field + " DESC"; + return this; + } + + public DBObjectQuery setGroupBy(String field) + { + groupBy_ = " GROUP BY " + field; + return this; + } + + public String toQuery() + { + return constuctQuery(false); + } + + public String toQueryUseOR() + { + return constuctQuery(true); + } + + private String constuctQuery(boolean useOR) + { + StringBuilder builder = new StringBuilder("SELECT "); + if (selectParameter_.size() > 0) + { + for (int i = 0; i < selectParameter_.size(); i++) + { + if (i > 0) + builder.append(", "); + parameters_.get(i).build(builder); + } + } + else + { + builder.append(" * "); + } + + Table table = type_.getAnnotation(Table.class); + builder.append(" FROM ").append(table.name()); + if (parameters_.size() > 0) + { + builder.append(" WHERE "); + for (int i = 0; i < parameters_.size(); i++) + { + if (i > 0) + builder.append(useOR ? " OR " : " AND "); + parameters_.get(i).build(builder); + } + } + if (orderBy_ != null) + builder.append(orderBy_); + return builder.toString(); + } + + public String toCountQuery() + { + return consturctCountQuery(false); + } + + public String toCountQueryUseOR() + { + return consturctCountQuery(true); + } + + private String consturctCountQuery(boolean useOR) + { + StringBuilder builder = new StringBuilder(); + Table table = type_.getAnnotation(Table.class); + builder.append("SELECT COUNT(*) FROM ").append(table.name()); + if (parameters_.size() > 0) + { + builder.append(" WHERE "); + for (int i = 0; i < parameters_.size(); i++) + { + if (i > 0) + builder.append(useOR ? " OR " : " AND "); + parameters_.get(i).build(builder); + } + } + return builder.toString(); + } + + /* + * public String getHibernateGroupByQuery() { StringBuilder b = new + * StringBuilder("SELECT ") ; if(selectParameter_.size() > 0){ for(int i = 0; + * i < selectParameter_.size(); i++){ Parameter p = selectParameter_.get(i) ; + * if(p.op_.equals("fieldselect")){ b.append(p.field_) ; }else + * if(p.op_.equals("countselect")){ b.append("COUNT"); if (p.field_ != "" || + * p.field_.length() > 0){ b.append("(").append(p.field_).append(" )"); }else{ + * b.append("(*)"); } }else { + * b.append(p.op_).append("(").append(p.field_).append(") "); } if(i < + * selectParameter_.size() - 1 ) b.append(" , ") ; } } Table table = + * type_.getAnnotation(Table.class) ; b.append(" FROM ").append(table.name()) + * ; if(parameters_.size() > 0) { b.append(" WHERE ") ; for(int i = 0; i < + * parameters_.size(); i ++) { if(i > 0) b.append(" AND ") ; Parameter p = + * parameters_.get(i) ; if(p.value_ instanceof String) { + * b.append(p.field_).append(p.op_).append("'").append(p.value_).append("'") ; + * } else if(p.value_ instanceof Date) { String value = ft_.format((Date) + * p.value_) ; + * b.append(' ').append(p.field_).append(p.op_).append("'").append( + * value).append("'") ; } else if(p.op_.equals("max") || p.op_.equals("min")){ + * b.append(p.op_).append("(").append(p.field_).append(") "); } else{ + * b.append(' ').append(p.field_).append(p.op_).append(p.value_); } } } + * if(groupBy_ != null ) b.append(groupBy_ ); if(orderBy_ != null ) + * b.append(orderBy_ ); return b.toString() ; } + */ + + public String optimizeInputString(String value) + { + value = value.replace('*', '%'); + value = value.replaceAll("'", "'"); + value = value.replaceAll("<", "<"); + value = value.replaceAll(">", ">"); + return value; + } + + public List getParameters() + { + return parameters_; + } + + static public class Parameter + { + + String op_; + + String field_; + + String label_; + + Object value_; + + public Parameter(String field, String op, Object value) + { + op_ = op; + field_ = field; + value_ = value; + } + + public Parameter(String op, String field) + { + op_ = op; + field_ = field; + } + + void build(StringBuilder builder) + { + builder.append(' ').append(field_).append(op_); + if (op_ == null || op_.trim().length() < 1 || value_ == null) + return; + builder.append(' '); + if (CharSequence.class.isInstance(value_)) + { + builder.append('\'').append(value_).append('\''); + } + else if (value_ instanceof Date) + { + LocalDateTime ldt = LocalDateTime.ofInstant(((Date)value_).toInstant(), ZoneId.systemDefault()); + String value = ldt.format(ft_); + builder.append("'").append(value).append("'"); + } + else + { + builder.append(value_); + } + } + } + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBQueryParameter.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBQueryParameter.java new file mode 100644 index 00000000..2ac81b6f --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBQueryParameter.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import java.util.Comparator; + +/** + * Created by The eXo Platform SAS + * Author : Nhu Dinh Thuan nhudinhthuan@exoplatform.com Apr 2, 2007 + */ +public class DBQueryParameter +{ + + protected String name; + + protected String value; + + protected Operator operator; + + protected int order = WHERE; + + public final static int SELECT = 1, WHERE = 3, GROUP = 4, HAVING = 5, ORDER = 6; + + protected DBQueryParameter() + { + } + + public DBQueryParameter(String value) + { + this.value = value; + } + + public DBQueryParameter(String name, String value) + { + this.name = name; + this.value = value; + } + + public DBQueryParameter(String name, String value, Operator operator) + { + this.name = name; + this.value = value; + this.operator = operator; + } + + public void build(StringBuilder builder) + { + if (order == WHERE && builder.indexOf(" WHERE ") < 0) + builder.append(" WHERE "); + builder.append(' ').append(name).append(operator.toString()).append(value); + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public Operator getOperator() + { + return operator; + } + + public void setOperator(Operator operator) + { + this.operator = operator; + } + + public String getValue() + { + return value; + } + + public void setValue(String value) + { + this.value = value; + } + + public final static Comparator PARAMETER_SORT = new Comparator() + { + public int compare(DBQueryParameter param1, DBQueryParameter param2) + { + return param1.order - param2.order; + } + }; + + public static class Operator + { + + public final static Operator AND = new Operator(" AND "); + + public final static Operator OR = new Operator(" OR "); + + public final static Operator LESS_THAN = new Operator(" < "); + + public final static Operator LESS_THAN_AND_EQUALS = new Operator(" <= "); + + public final static Operator GREATER_THAN = new Operator(" > "); + + public final static Operator GREATER_THAN_AND_EQUALS = new Operator(" >= "); + + public final static Operator LIKE = new Operator(" LIKE "); + + public final static Operator IN = new Operator(" IN "); + + public final static Operator IS = new Operator(" IS "); + + public final static Operator EQUALS = new Operator(" = "); + + private String value; + + public Operator(String value) + { + this.value = value; + } + + public String toString() + { + return value; + } + + public String getValue() + { + return value; + } + + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBTableManager.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBTableManager.java new file mode 100644 index 00000000..53e2f3be --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DBTableManager.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +/** + * Created by The eXo Platform SAS + * Author : Tuan Nguyen tuan08@users.sourceforge.net Apr 4, 2006 + * The DBTableManager is an interface + * to help the developer to check if a table is existed or not and create/drop a + * table. We can implement more one more DBTableManager according to the + * database type and version. + */ +abstract public class DBTableManager +{ + /** + * This method should: 1. Extract the table information such table name and + * table fields from the class T, the information are annotated in the class T + * 2. Generate the SQL statement and create the table + * + * @param + * @param type + * @param dropIfExist + * @throws Exception + */ + abstract public void createTable(Class type, boolean dropIfExist) throws Exception; + + /** + * This method should: 1. Extract the table information from the class T, the + * information are annotated in the class T 2. Generate the SQL statement and + * drop the table + * + * @param + * @param type + * @throws Exception + */ + abstract public void dropTable(Class type) throws Exception; + + /** + * This method should: 1. Extract the table information from the class T, the + * information are annotated in the class T 2. Check to see if the table is + * existed in the database system + * + * @param + * @param type + * @return + * @throws Exception + */ + abstract public boolean hasTable(Class type) throws Exception; + + /** + * This method should check the database type and version and create a + * corresponded DBTableManager + * + * @param datasource + * @return + */ + final static public DBTableManager createDBTableManager(ExoDatasource datasource) + { + if (datasource.getDatabaseType() == ExoDatasource.HSQL_DB_TYPE) + { + return new StandardSQLTableManager(datasource); + } + else if (ExoDatasource.ORACLE_DB_TYPE == datasource.getDatabaseType()) + { + return new OracleTableManager(datasource); + } + else if (ExoDatasource.SQL_SERVER_TYPE == datasource.getDatabaseType()) + { + return new MSSQLServerTableManager(datasource); + } + return new StandardSQLTableManager(datasource); + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/DatabaseService.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DatabaseService.java new file mode 100644 index 00000000..cae0c27c --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/DatabaseService.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import org.exoplatform.services.transaction.TransactionService; + +import java.sql.Connection; + +/** + * Created by The eXo Platform SAS + * Author : Tuan Nguyen tuan08@users.sourceforge.net Apr 4, 2006 + * This service should provide a single + * interface to access the different datasource. + */ +public interface DatabaseService +{ + + /** + * This method should return the default datasource of the application + * + * @return + * @throws Exception + */ + public ExoDatasource getDatasource() throws Exception; + + /** + * This method should look up the datasouce by the datasource name and return. + * If the datasource is not found then the method should return null + * + * @param dsname + * @return + * @throws Exception + */ + public ExoDatasource getDatasource(String dsname) throws Exception; + + public Connection getConnection() throws Exception; + + public Connection getConnection(String dsName) throws Exception; + + public void closeConnection(Connection conn) throws Exception; + + /** + * This method should return the transaction service + * + * @return + * @throws Exception + */ + public TransactionService getTransactionService() throws Exception; + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/ExoDatasource.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/ExoDatasource.java new file mode 100644 index 00000000..36e60ed8 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/ExoDatasource.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import org.exoplatform.services.database.table.IDGenerator; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; + +import javax.sql.DataSource; + +/** + * Created by The eXo Platform SAS + * Author : Tuan Nguyen tuan08@users.sourceforge.net Apr 4, 2006 + * This class is a wrapper class for the java.sql.Datasource class. + * In additional to the java.sql.Datasourcemethod such getConnection(). + * The ExoDatasource provides 2 other methods: + * DBTableManager getDBTableManager and IDGenerator getIDGenerator() + */ +public class ExoDatasource +{ + + /** + * Logger. + */ + private static final Log LOG = ExoLogger.getLogger("exo.core.component.organization.database.ExoDatasource"); + + final public static int STANDARD_DB_TYPE = 0; + + final public static int HSQL_DB_TYPE = 1; + + final public static int MYSQL_DB_TYPE = 2; + + final public static int DB2_DB_TYPE = 3; + + final public static int DERBY_DB_TYPE = 4; + + final public static int ORACLE_DB_TYPE = 5; + + final public static int SQL_SERVER_TYPE = 6; + + static int totalGetConnect = 0; + + final public static int MSSQL_DB_TYPE = 6; + + final public static int SYSBASE_DB_TYPE = 7; + + final public static int POSTGRES_DB_TYPE = 8; + + private DataSource xaDatasource_; + + private DBTableManager tableManager_; + + private IDGenerator idGenerator_; + + private QueryBuilder queryManager_; + + private String databaseName_; + + private String databaseVersion_; + + private int dbType_ = STANDARD_DB_TYPE; + + Connection conn; + + /** + * The constructor should: + * 1. Keep track of the datasource object + * 2. Create the DBTableManager object base on the datasource information such database type, version + * 3. Create an IDGenerator for the datasource + * + * @param ds + * @throws Exception + */ + public ExoDatasource(final DataSource ds) throws Exception + { + xaDatasource_ = ds; + DatabaseMetaData metaData = ds.getConnection().getMetaData(); + + databaseName_ = metaData.getDatabaseProductName(); + databaseVersion_ = metaData.getDatabaseProductVersion(); + + String dbname = databaseName_.toLowerCase(); + LOG.debug("DB Name: " + dbname); + if (dbname.indexOf("oracle") >= 0) + { + dbType_ = ORACLE_DB_TYPE; + } + else if (dbname.indexOf("hsql") >= 0) + { + dbType_ = HSQL_DB_TYPE; + } + else if (dbname.indexOf("mysql") >= 0) + { + dbType_ = MYSQL_DB_TYPE; + } + else if (dbname.indexOf("derby") >= 0) + { + dbType_ = DERBY_DB_TYPE; + } + else if (dbname.indexOf("db2") >= 0) + { + dbType_ = DB2_DB_TYPE; + } + else if (dbname.indexOf("server") >= 0) + { + dbType_ = SQL_SERVER_TYPE; + } + else + { + dbType_ = STANDARD_DB_TYPE; + } + + tableManager_ = DBTableManager.createDBTableManager(this); + idGenerator_ = new IDGenerator(this); + queryManager_ = new QueryBuilder(dbType_); + } + + /** + * This method should return the real Datasource object + * + * @return + */ + public DataSource getDatasource() + { + return xaDatasource_; + } + + /** + * This method should call the datasource getConnection method and return the + * Connection object. The developer can add some debug code or broadcast an + * event here. + * + * @return + * @throws Exception + */ + public Connection getConnection() throws Exception + { + return xaDatasource_.getConnection(); + } + + /** + * This method should delegate to the method close of the Connection object. + * The developer can add debug or broadcast an event here. + * + * @param conn + * @throws Exception + */ + public void closeConnection(Connection conn) throws Exception + { + conn.close(); + } + + /** + * This method should delegate to the commit() method of the Connection + * object. The developer can add the debug code here + * + * @param conn + * @throws Exception + */ + public void commit(Connection conn) throws Exception + { + conn.setAutoCommit(false); + conn.commit(); + } + + /** + * This method should return the DBTableManager object. The DBTableManager + * object should be initialized in the constructor according to the database + * type and version + * + * @return + */ + public DBTableManager getDBTableManager() + { + return tableManager_; + } + + /** + * This method should return the IDGenerator object, the developer can use the + * id generator to generate an unique long id for an db object + * + * @return + */ + public IDGenerator getIDGenerator() + { + return idGenerator_; + } + + public int getDatabaseType() + { + return dbType_; + } + + public String getDatabaseName() + { + return databaseName_; + } + + public String getDatabaseVersion() + { + return databaseVersion_; + } + + public QueryBuilder getQueryBuilder() + { + return queryManager_; + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/HibernateService.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/HibernateService.java new file mode 100644 index 00000000..bd188a59 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/HibernateService.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; + +import java.io.Serializable; +import java.util.Collection; + +/** + * Created by The eXo Platform SAS + * Author : Tuan Nguyen tuan08@users.sourceforge.net + * Date: Jun 14, 2003 Time: 1:12:22 PM + */ +public interface HibernateService +{ + public Configuration getHibernateConfiguration(); + + public Session openSession(); + + public Session openNewSession(); + + public void closeSession(Session session); + + /** Close the session that assign to the current thread */ + public void closeSession(); + + SessionFactory getSessionFactory(); + + public Object findOne(Session session, String query, String id) throws Exception; + + public Collection findAll(Session session, String query) throws Exception; + + public Object findExactOne(Session session, String query, String id) throws Exception; + + public Object findOne(Class clazz, java.io.Serializable id) throws Exception; + + public Object findOne(ObjectQuery q) throws Exception; + + public Object create(Object obj) throws Exception; + + public Object update(Object obj) throws Exception; + + public Object save(Object obj) throws Exception; + + public Object remove(Object obj) throws Exception; + + public Object remove(Class clazz, Serializable id) throws Exception; + + public Object remove(Session session, Class clazz, Serializable id) throws Exception; +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/MSSQLServerTableManager.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/MSSQLServerTableManager.java new file mode 100644 index 00000000..9862b984 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/MSSQLServerTableManager.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import org.exoplatform.services.database.annotation.TableField; + +/** + * Created by The eXo Platform SAS + * Author : Le Bien Thuy lebienthuy@exoplatform.com Apr 4, 2006 + */ +public class MSSQLServerTableManager extends StandardSQLTableManager +{ + + public MSSQLServerTableManager(ExoDatasource datasource) + { + super(datasource); + } + + protected void appendDateField(TableField field, StringBuilder builder) + { + builder.append(field.name()).append(" DATETIME"); + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/ObjectQuery.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/ObjectQuery.java new file mode 100644 index 00000000..c2f6bbd4 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/ObjectQuery.java @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Tuan Nguyen (tuan08@users.sourceforge.net) + * @since Nov 25, 2004 + * @version $Id: ObjectQuery.java 6006 2006-06-06 10:01:27Z thangvn $ + */ +public class ObjectQuery +{ + + private Class type_; + + private String orderBy_; + + private String groupBy_; + + private List parameters_; + + private List selectParameter_; + + public ObjectQuery(Class type) + { + type_ = type; + parameters_ = new ArrayList(3); + selectParameter_ = new ArrayList(10); + } + + public ObjectQuery addEQ(String field, Object value) + { + if (value != null) + { + parameters_.add(new Parameter(" = ", field, value)); + } + return this; + } + + public ObjectQuery addGT(String field, Object value) + { + if (value != null) + { + parameters_.add(new Parameter(" > ", field, value)); + } + return this; + } + + public ObjectQuery addLT(String field, Object value) + { + if (value != null) + { + parameters_.add(new Parameter(" < ", field, value)); + } + return this; + } + + public ObjectQuery addLIKE(String field, String value) + { + if (value != null && value.length() > 0) + { + parameters_.add(new Parameter(" LIKE ", field, optimizeInputString(value))); + } + return this; + } + + public String optimizeInputString(String value) + { + value = value.replace('*', '%'); + return value; + } + + public ObjectQuery addSUM(String field) + { + selectParameter_.add(new Parameter("SUM", field)); + return this; + } + + public ObjectQuery addSelect(String field) + { + selectParameter_.add(new Parameter("FIELDSELECT", field)); + return this; + } + + public ObjectQuery addSelectCount(String type) + { + selectParameter_.add(new Parameter("COUNTSELECT", type)); + return this; + } + + public ObjectQuery addSelectMaxMin(String op, String field) + { + selectParameter_.add(new Parameter(op, field)); + return this; + } + + public ObjectQuery setGroupBy(String field) + { + groupBy_ = " GROUP BY o." + field; + return this; + } + + public ObjectQuery setAscOrderBy(String field) + { + orderBy_ = " ORDER BY o." + field + " asc"; + return this; + } + + public ObjectQuery setDescOrderBy(String field) + { + orderBy_ = " ORDER BY o." + field + " desc"; + return this; + } + + public String getHibernateQuery() + { + StringBuffer b = new StringBuffer(); + b.append("from o in class ").append(type_.getName()); + if (parameters_.size() > 0) + { + b.append(" WHERE "); + for (int i = 0; i < parameters_.size(); i++) + { + if (i > 0) + b.append(" AND "); + Parameter p = parameters_.get(i); + if (p.value_ instanceof String) + { + if (p.field_.startsWith("UPPER") || p.field_.startsWith("LOWER")) + { + b.append(p.field_).append(p.op_).append("'").append(p.value_).append("'"); + } + else + { + b.append(" o.").append(p.field_).append(p.op_).append("'").append(p.value_).append("'"); + } + } + else if (p.value_ instanceof Date) + { + SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + String value = ft.format((Date)p.value_); + b.append(" o.").append(p.field_).append(p.op_).append("'").append(value).append("'"); + } + else + { + b.append(" o.").append(p.field_).append(p.op_).append(p.value_); + } + } + } + if (orderBy_ != null) + b.append(orderBy_); + return b.toString(); + } + + /** + * + * @return + */ + public String getHibernateQueryWithBinding() + { + StringBuffer b = new StringBuffer(); + b.append("from o in class ").append(type_.getName()); + if (parameters_.size() > 0) + { + b.append(" WHERE "); + for (int i = 0; i < parameters_.size(); i++) + { + if (i > 0) + b.append(" AND "); + Parameter p = parameters_.get(i); + if (p.value_ instanceof String) + { + if (p.field_.startsWith("UPPER") || p.field_.startsWith("LOWER")) + { + b.append(p.field_).append(p.op_).append(":").append(p.field_.substring(6, p.field_.length() - 1)) + .append(i); + } + else + { + b.append(" o.").append(p.field_).append(p.op_).append(":").append(p.field_).append(i); + } + } + else if (p.value_ instanceof Date) + { + b.append(" o.").append(p.field_).append(p.op_).append(":").append(p.field_).append(i); + } + else + { + b.append(" o.").append(p.field_).append(p.op_).append(p.value_); + } + } + } + + if (orderBy_ != null) + { + b.append(orderBy_); + } + + return b.toString(); + } + + /** + * + * @return + */ + public Map getBindingFields() + { + Map binding = new HashMap(); + + if (parameters_.size() > 0) + { + for (int i = 0; i < parameters_.size(); i++) + { + Parameter p = parameters_.get(i); + if (p.value_ instanceof String) + { + if (p.field_.startsWith("UPPER") || p.field_.startsWith("LOWER")) + { + binding.put(p.field_.substring(6, p.field_.length() - 1) + i, p.value_); + } + else + { + binding.put(p.field_ + i, p.value_); + } + } + else if (p.value_ instanceof Date) + { + binding.put(p.field_ + i, p.value_); + } + } + } + + return binding; + } + + public String getHibernateGroupByQuery() + { + StringBuffer b = new StringBuffer(); + b.append("select "); + if (selectParameter_.size() > 0) + { + for (int i = 0; i < selectParameter_.size(); i++) + { + Parameter p = selectParameter_.get(i); + if (p.op_.equals("fieldselect")) + { + b.append("o.").append(p.field_); + } + else if (p.op_.equals("countselect")) + { + b.append("COUNT"); + if (!(p.field_.equals("")) || p.field_.length() > 0) + { + b.append("(").append(p.field_).append(" o)"); + } + else + { + b.append("(o)"); + } + } + else + { + b.append(p.op_).append("(").append("o.").append(p.field_).append(") "); + } + if (i < selectParameter_.size() - 1) + b.append(" , "); + } + } + b.append(" from o in class ").append(type_.getName()); + if (parameters_.size() > 0) + { + b.append(" where "); + for (int i = 0; i < parameters_.size(); i++) + { + if (i > 0) + b.append(" and "); + Parameter p = parameters_.get(i); + if (p.value_ instanceof String) + { + b.append(" o.").append(p.field_).append(p.op_).append("'").append(p.value_).append("'"); + } + else if (p.value_ instanceof Date) + { + SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + String value = ft.format((Date)p.value_); + b.append(" o.").append(p.field_).append(p.op_).append("'").append(value).append("'"); + } + else if (p.op_.equals("MAX") || p.op_.equals("MIN")) + { + b.append(p.op_).append("(").append("o.").append(p.field_).append(") "); + } + else + { + b.append(" o.").append(p.field_).append(p.op_).append(p.value_); + } + } + } + if (groupBy_ != null) + b.append(groupBy_); + if (orderBy_ != null) + b.append(orderBy_); + return b.toString(); + } + + public String getHibernateCountQuery() + { + StringBuffer b = new StringBuffer(); + b.append("SELECT COUNT(o) FROM o IN CLASS ").append(type_.getName()); + if (parameters_.size() > 0) + { + b.append(" WHERE "); + for (int i = 0; i < parameters_.size(); i++) + { + if (i > 0) + b.append(" AND "); + Parameter p = parameters_.get(i); + if (p.value_ instanceof String) + { + if (p.field_.startsWith("UPPER") || p.field_.startsWith("LOWER")) + { + b.append(p.field_).append(p.op_).append("'").append(p.value_).append("'"); + } + else + { + b.append(" o.").append(p.field_).append(p.op_).append("'").append(p.value_).append("'"); + } + } + else if (p.value_ instanceof Date) + { + SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + String value = ft.format((Date)p.value_); + b.append(" o.").append(p.field_).append(p.op_).append("'").append(value).append("'"); + } + else + { + b.append(" o.").append(p.field_).append(p.op_).append(p.value_); + } + } + } + return b.toString(); + } + + /** + * + * @return + */ + public String getHibernateCountQueryWithBinding() + { + StringBuffer b = new StringBuffer(); + b.append("SELECT COUNT(o) FROM o IN CLASS ").append(type_.getName()); + if (parameters_.size() > 0) + { + b.append(" WHERE "); + for (int i = 0; i < parameters_.size(); i++) + { + if (i > 0) + b.append(" AND "); + Parameter p = parameters_.get(i); + if (p.value_ instanceof String) + { + if (p.field_.startsWith("UPPER") || p.field_.startsWith("LOWER")) + { + b.append(p.field_).append(p.op_).append(":").append(p.field_.substring(6, p.field_.length() - 1)) + .append(i); + } + else + { + b.append(" o.").append(p.field_).append(p.op_).append(":").append(p.field_).append(i); + } + } + else if (p.value_ instanceof Date) + { + b.append(" o.").append(p.field_).append(p.op_).append(":").append(p.field_).append(i); + } + else + { + b.append(" o.").append(p.field_).append(p.op_).append(p.value_); + } + } + } + return b.toString(); + } + + static class Parameter + { + String op_; + + String field_; + + String label_; + + Object value_; + + Parameter(String op, String field, Object value) + { + op_ = op; + field_ = field; + value_ = value; + } + + Parameter(String op, String field) + { + op_ = op; + field_ = field; + } + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/OracleTableManager.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/OracleTableManager.java new file mode 100644 index 00000000..8c62a940 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/OracleTableManager.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import org.exoplatform.services.database.annotation.TableField; + +/** + * Created by The eXo Platform SAS + * Author : Le Bien Thuy lebienthuy@exoplatform.com Apr 4, 2006 + */ +public class OracleTableManager extends StandardSQLTableManager +{ + + public OracleTableManager(ExoDatasource datasource) + { + super(datasource); + } + + @Override + protected void appendId(StringBuilder builder) + { + builder.append("ID INT NOT NULL PRIMARY KEY, "); + } + + @Override + protected void appendLongField(TableField field, StringBuilder builder) + { + builder.append(field.name()).append(" NUMBER"); + } + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/QueryBuilder.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/QueryBuilder.java new file mode 100644 index 00000000..f527d1e1 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/QueryBuilder.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import org.exoplatform.services.database.annotation.Query; +import org.exoplatform.services.database.annotation.Table; +import org.exoplatform.services.database.annotation.TableField; + +import java.lang.reflect.Method; +import java.util.List; + +/** + * Created by The eXo Platform SAS + * Author : Nhu Dinh Thuan nhudinhthuan@exoplatform.com Mar 30, 2007 + */ +public class QueryBuilder +{ + + private int databaseType = ExoDatasource.STANDARD_DB_TYPE; + + public QueryBuilder() + { + } + + public QueryBuilder(int dbType) + { + databaseType = dbType; + } + + public String createSelectQuery(Class type, long id) throws Exception + { + Table table = type.getAnnotation(Table.class); + TableField[] fields = table.field(); + + StringBuilder query = new StringBuilder("SELECT "); + for (int i = 0; i < fields.length; i++) + { + TableField field = fields[i]; + query.append(field.name()); + if (i != fields.length - 1) + query.append(", "); + } + query.append(" FROM ").append(table.name()); + if (id > -1) + query.append(" WHERE ID = ").append(id); + return query.toString(); + } + + public String createUpdateQuery(Class type) throws Exception + { + Table table = type.getAnnotation(Table.class); + TableField[] fields = table.field(); + + StringBuilder query = new StringBuilder("UPDATE ").append(table.name()).append(" SET "); + for (int i = 0; i < fields.length; i++) + { + TableField field = fields[i]; + query.append(field.name()).append(" = '$").append(field.name()).append('\''); + if (i != fields.length - 1) + query.append(", "); + else + query.append(" WHERE ID = $id"); + } + return query.toString(); + } + + public String createInsertQuery(Class type) throws Exception + { + Table table = type.getAnnotation(Table.class); + TableField[] fields = table.field(); + + StringBuilder query = new StringBuilder("INSERT INTO ").append(table.name()).append("(ID, "); + for (int i = 0; i < fields.length; i++) + { + TableField field = fields[i]; + query.append(field.name()); + if (i != fields.length - 1) + query.append(", "); + else + query.append(") VALUES($id, "); + } + + for (int i = 0; i < fields.length; i++) + { + query.append("'$").append(fields[i].name()).append('\''); + if (i != fields.length - 1) + query.append(", "); + else + query.append(")"); + } + return query.toString(); + } + + public String createUpdateQuery(Class type, long id) throws Exception + { + Table table = type.getAnnotation(Table.class); + TableField[] fields = table.field(); + + StringBuilder query = new StringBuilder("UPDATE ").append(table.name()).append(" SET "); + for (int i = 0; i < fields.length; i++) + { + TableField field = fields[i]; + query.append(field.name()).append(" = ?"); + if (i != fields.length - 1) + query.append(", "); + else + query.append(" WHERE ID = ").append(id); + } + + return query.toString(); + } + + public String createInsertQuery(Class clazz, long id) throws Exception + { + Table table = clazz.getAnnotation(Table.class); + TableField[] fields = table.field(); + + StringBuilder query = new StringBuilder("INSERT INTO ").append(table.name()).append("(ID, "); + for (int i = 0; i < fields.length; i++) + { + TableField field = fields[i]; + query.append(field.name()); + if (i != fields.length - 1) + query.append(", "); + } + query.append(") VALUES(").append(id).append(", "); + + for (int i = 0; i < fields.length; i++) + { + query.append("?"); + if (i != fields.length - 1) + query.append(", "); + else + query.append(")"); + } + return query.toString(); + } + + public String createRemoveQuery(Class type, long id) throws Exception + { + Table table = type.getAnnotation(Table.class); + StringBuilder builder = new StringBuilder("DELETE FROM "); + builder.append(table.name()).append(" WHERE ID = ").append(id).toString(); + return builder.toString(); + } + + public > String getQuery(Class clazz, String name) throws Exception + { + Query query = clazz.getAnnotation(Query.class); + String value = null; + if (query != null && query.name().equals(name)) + value = getQuery(query); + if (value != null) + return value; + List list = ReflectionUtil.getMethod(clazz, name); + for (Method method : list) + { + query = method.getAnnotation(Query.class); + if (query != null && query.name().equals(name)) + value = getQuery(query); + if (value != null) + return value; + } + return null; + } + + private String getQuery(Query query) + { + switch (databaseType) + { + case ExoDatasource.STANDARD_DB_TYPE : + return query.standardSQL(); + case ExoDatasource.HSQL_DB_TYPE : + if (query.hsqlSQL().length() > 0) + return query.hsqlSQL(); + case ExoDatasource.MYSQL_DB_TYPE : + if (query.mysqlSQL().length() > 0) + return query.mysqlSQL(); + case ExoDatasource.MSSQL_DB_TYPE : + if (query.mssqlSQL().length() > 0) + return query.mssqlSQL(); + case ExoDatasource.ORACLE_DB_TYPE : + if (query.oracleSQL().length() > 0) + return query.oracleSQL(); + case ExoDatasource.DB2_DB_TYPE : + if (query.db2SQL().length() > 0) + return query.db2SQL(); + case ExoDatasource.DERBY_DB_TYPE : + if (query.derbySQL().length() > 0) + return query.derbySQL(); + case ExoDatasource.POSTGRES_DB_TYPE : + if (query.postgresSQL().length() > 0) + return query.postgresSQL(); + case ExoDatasource.SYSBASE_DB_TYPE : + if (query.sysbaseSQL().length() > 0) + return query.sysbaseSQL(); + default : + return query.standardSQL(); + } + } + + public String mapDataToSql(String template, String[][] parameters) throws Exception + { + StringBuilder builder = new StringBuilder(); + int i = 0; + int start = 0; + while (i < template.length()) + { + if (template.charAt(i) != '$') + { + i++; + continue; + } + + if (i > 0 && template.charAt(i - 1) == '\\') + { + builder.append(template.subSequence(start, i - 1)); + start = i; + i++; + continue; + } + + if (i == template.length() - 1) + break; + + int j = i + 1; + while (j < template.length()) + { + if (Character.isWhitespace(template.charAt(j))) + break; + if (template.charAt(j) == '\'' && template.charAt(j - 1) != '\\') + break; + if (template.charAt(j) == ',' && template.charAt(j - 1) != '\\') + break; + j++; + } + String name = template.substring(i + 1, j); + start = replace(template, builder, parameters, name, start, i); + i++; + } + if (start > 0 && start < template.length()) + { + builder.append(template.subSequence(start, template.length())); + } + if (builder.length() < 1) + return template.toString(); + return builder.toString(); + } + + private int replace(String template, StringBuilder builder, String[][] parameters, String name, int start, + int current) throws Exception + { + for (int k = 0; k < parameters.length; k++) + { + if (!parameters[k][0].equals(name)) + continue; + builder.append(template.subSequence(start, current)).append(parameters[k][1]); + return current + 1 + name.length(); + } + return start; + } + + public String encode(CharSequence seq) + { + if (seq.length() < 1) + return seq.toString(); + StringBuilder builder = new StringBuilder(); + int i = 0; + int start = 0; + while (i < seq.length()) + { + if (seq.charAt(i) == '\'') + { + builder.append(seq.subSequence(start, i)).append("''"); + start = i + 1; + } + i++; + } + if (start > 0 && start < seq.length()) + { + builder.append(seq.subSequence(start, seq.length())); + } + if (builder.length() < 1) + return seq.toString(); + return builder.toString(); + } + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/ReflectionMapper.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/ReflectionMapper.java new file mode 100644 index 00000000..f78517ef --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/ReflectionMapper.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import org.exoplatform.services.database.annotation.Table; +import org.exoplatform.services.database.annotation.TableField; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.Reader; +import java.lang.reflect.Field; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.Types; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Created by The eXo Platform SAS + * Author: Nhu Dinh Thuan nhudinhthuan@exoplatform.com Apr 2, 2007 + */ +public class ReflectionMapper implements DBObjectMapper +{ + + public String[][] toParameters(T bean) throws Exception + { + Map map = new HashMap(); + getParameters(bean, bean.getClass(), map); + + String[][] parameters = new String[map.size()][2]; + Iterator iter = map.keySet().iterator(); + int i = 0; + while (iter.hasNext()) + { + parameters[i][0] = iter.next(); + parameters[i][1] = map.get(parameters[i][0]); + i++; + } + return parameters; + } + + private void getParameters(Object bean, Class clazz, Map map) throws Exception + { + Field[] fields = clazz.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) + { + Object value = ReflectionUtil.getValue(bean, fields[i]); + if (value == null) + value = new String(); + if (value instanceof Calendar) + { + value = new java.sql.Date(((Calendar)value).getTimeInMillis()); + } + else if (value instanceof Date) + { + value = new java.sql.Date(((Date)value).getTime()); + } + map.put(fields[i].getName(), value.toString()); + } + if (clazz == DBObject.class) + return; + Class superClazz = clazz.getSuperclass(); + getParameters(superClazz.cast(bean), superClazz, map); + } + + public void mapResultSet(ResultSet resultSet, T bean) throws Exception + { + Class clazz = bean.getClass(); + Table table = clazz.getAnnotation(Table.class); + TableField[] tableFields = table.field(); + + ResultSetMetaData rsmd = resultSet.getMetaData(); + int numberOfColumns = rsmd.getColumnCount(); + for (int i = 1; i <= numberOfColumns; i++) + { + String name = rsmd.getColumnName(i); + TableField tableField = searchTableField(tableFields, name); + if (tableField == null) + continue; + String fieldName = tableField.field().length() == 0 ? tableField.name() : tableField.field(); + Field field = getField(clazz, fieldName); + if (field == null) + continue; + ReflectionUtil.setValue(bean, field, getValue(rsmd.getColumnType(i), resultSet, name)); + } + } + + public void mapUpdate(T bean, PreparedStatement statement) throws Exception + { + Class clazz = bean.getClass(); + Table table = clazz.getAnnotation(Table.class); + TableField[] tableFields = table.field(); + + int i = 1; + for (TableField tableField : tableFields) + { + String fieldName = tableField.field().length() == 0 ? tableField.name() : tableField.field(); + Field field = getField(clazz, fieldName); + if (field == null) + continue; + statement.setObject(i, ReflectionUtil.getValue(bean, field)); + i++; + } + } + + private Field getField(Class clazz, String name) throws Exception + { + Field field = clazz.getDeclaredField(name); + if (field != null) + return field; + if (clazz == DBObject.class) + return null; + return getField(clazz.getSuperclass(), name); + } + + private Object getValue(int type, ResultSet resultSet, String name) throws Exception + { + switch (type) + { + case Types.CLOB : + return loadClob(resultSet, name); + case Types.BLOB : + return loadBlob(resultSet, name); + case Types.BINARY : + return loadBinary(resultSet, name); + default : + return resultSet.getObject(name.toUpperCase()); + } + } + + private synchronized byte[] loadBinary(ResultSet resultSet, String name) throws Exception + { + InputStream input = resultSet.getBinaryStream(name); + if (input == null) + return null; + ByteArrayOutputStream output = loadInputStream(input); + return output.toByteArray(); + } + + private synchronized byte[] loadBlob(ResultSet resultSet, String name) throws Exception + { + Blob clob = resultSet.getBlob(name); + if (clob == null) + return null; + ByteArrayOutputStream output = loadInputStream(clob.getBinaryStream()); + return output.toByteArray(); + } + + private synchronized String loadClob(ResultSet resultSet, String name) throws Exception + { + Clob clob = resultSet.getClob(name); + if (clob == null) + return null; + Reader input = clob.getCharacterStream(); + if (input == null) + return null; + LineNumberReader lineReader = new LineNumberReader(input); + StringBuilder builder = new StringBuilder(); + String line; + while ((line = lineReader.readLine()) != null) + { + builder.append(line); + } + return builder.toString(); + } + + private ByteArrayOutputStream loadInputStream(InputStream input) throws Exception + { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + byte[] data = new byte[1024]; + int available = -1; + while ((available = input.read(data)) > -1) + { + output.write(data, 0, available); + } + return output; + } + + private TableField searchTableField(TableField[] tableFields, String name) + { + for (TableField tableField : tableFields) + { + if (tableField.name().equals(name) && tableField.field().length() > 0) + return tableField; + } + for (TableField field : tableFields) + { + if (field.name().equals(name) && field.field().length() < 1) + return field; + } + return null; + } + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/ReflectionUtil.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/ReflectionUtil.java new file mode 100644 index 00000000..5b4c8a44 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/ReflectionUtil.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by The eXo Platform SAS + * Author : Nhu Dinh Thuan nhudinhthuan@exoplatform.com Mar 29, 2007 + */ +public class ReflectionUtil +{ + + private static final Log LOG = ExoLogger.getLogger("exo.core.component.database.ReflectionUtil"); + + public final static void setValue(Object bean, Field field, Object value) throws Exception + { + Class clazz = bean.getClass(); + Method method = getMethod("set", field, clazz); + if (method != null) + method.invoke(bean, new Object[]{value}); + method = getMethod("put", field, clazz); + if (method != null) + method.invoke(bean, new Object[]{value}); + field.setAccessible(true); + field.set(bean, value); + } + + public final static Object getValue(Object bean, Field field) throws Exception + { + Class clazz = bean.getClass(); + Method method = getMethod("get", field, clazz); + if (method != null) + return method.invoke(bean, new Object[]{}); + method = getMethod("is", field, clazz); + if (method != null) + return method.invoke(bean, new Object[]{}); + field.setAccessible(true); + return field.get(bean); + } + + public final static Method getMethod(String prefix, Field field, Class clazz) throws Exception + { + StringBuilder name = new StringBuilder(field.getName()); + name.setCharAt(0, Character.toUpperCase(name.charAt(0))); + name.insert(0, prefix); + try + { + Method method = clazz.getDeclaredMethod(name.toString(), new Class[]{}); + return method; + } + catch (Exception e) + { + if (LOG.isTraceEnabled()) + { + LOG.trace("An exception occurred: " + e.getMessage()); + } + } + return null; + } + + public final static List getMethod(Class clazz, String name) throws Exception + { + Method[] methods = clazz.getDeclaredMethods(); + List list = new ArrayList(); + for (Method method : methods) + { + if (method.getName().equals(name)) + list.add(method); + } + return list; + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/StandardSQLTableManager.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/StandardSQLTableManager.java new file mode 100644 index 00000000..1f4360ce --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/StandardSQLTableManager.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import org.exoplatform.services.database.annotation.Table; +import org.exoplatform.services.database.annotation.TableField; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * Created by The eXo Platform SAS + * Author : Tuan Nguyen tuan08@users.sourceforge.net Apr 4, 2006 + */ +public class StandardSQLTableManager extends DBTableManager +{ + + /** + * Logger. + */ + private static final Log LOG = ExoLogger + .getLogger("exo.core.component.organization.database.StandardSQLTableManager"); + + private ExoDatasource exoDatasource; + + public StandardSQLTableManager(ExoDatasource datasource) + { + exoDatasource = datasource; + } + + public void createTable(Class type, boolean dropIfExist) throws Exception + { + Table table = type.getAnnotation(Table.class); + if (table == null) + { + throw new Exception("Cannot find the annotation for class " + type.getClass().getName()); + } + StringBuilder builder = new StringBuilder(1000); + builder.append("CREATE TABLE ").append(table.name()).append(" ("); + appendId(builder); + TableField[] fields = table.field(); + for (int i = 0; i < fields.length; i++) + { + TableField field = fields[i]; + String fieldType = field.type(); + if ("string".equals(fieldType)) + { + appendStringField(field, builder); + } + else if ("int".equals(fieldType)) + { + appendIntegerField(field, builder); + } + else if ("long".equals(fieldType)) + { + appendLongField(field, builder); + } + else if ("float".equals(fieldType)) + { + appendFloatField(field, builder); + } + else if ("double".equals(fieldType)) + { + appendDoubleField(field, builder); + } + else if ("boolean".equals(fieldType)) + { + appendBooleanField(field, builder); + } + else if ("date".equals(fieldType)) + { + appendDateField(field, builder); + } + else if ("binary".equals(fieldType)) + { + appendBinaryField(field, builder); + } + if (i != fields.length - 1) + builder.append(", "); + } + builder.append(")"); + + // print out the sql string + Connection conn = exoDatasource.getConnection(); + conn.setAutoCommit(false); + Statement statement = conn.createStatement(); + LOG.debug("QUERY: \n " + builder + "\n"); + if (dropIfExist && hasTable(type)) + statement.execute("DROP TABLE IF EXISTS " + table.name()); + statement.execute(builder.toString()); + statement.close(); + conn.commit(); + exoDatasource.closeConnection(conn); + } + + public void dropTable(Class type) throws Exception + { + Table table = type.getAnnotation(Table.class); + if (table == null) + { + throw new Exception("Can not find the annotation for class " + type.getClass().getName()); + } + Connection conn = exoDatasource.getConnection(); + Statement s = conn.createStatement(); + s.execute("DROP TABLE " + table.name()); + s.close(); + conn.commit(); + exoDatasource.closeConnection(conn); + } + + public boolean hasTable(Class type) throws Exception + { + Table table = type.getAnnotation(Table.class); + if (table == null) + { + throw new Exception("Can not find the annotation for class " + type.getClass().getName()); + } + Connection connection = exoDatasource.getConnection(); + Statement statement = connection.createStatement(); + try + { + if (statement.execute("SELECT 1 FROM " + table.name()) == true) + return true; + } + catch (SQLException ex) + { + return false; + } + finally + { + statement.close(); + exoDatasource.closeConnection(connection); + } + return false; + } + + protected void appendId(StringBuilder builder) + { + builder.append("ID BIGINT NOT NULL PRIMARY KEY, "); + } + + protected void appendStringField(TableField field, StringBuilder builder) throws Exception + { + if (field.length() < 1) + { + throw new Exception("You forget to specify the length for field " + field.name() + " , type " + field.type()); + } + builder.append(field.name()).append(" ").append("VARCHAR(" + field.length() + ")"); + if (!field.nullable()) + builder.append(" NOT NULL "); + } + + protected void appendIntegerField(TableField field, StringBuilder builder) + { + builder.append(field.name()).append(" INTEGER"); + } + + protected void appendLongField(TableField field, StringBuilder builder) + { + builder.append(field.name()).append(" BIGINT"); + } + + protected void appendFloatField(TableField field, StringBuilder builder) + { + builder.append(field.name()).append(" REAL"); + } + + protected void appendDoubleField(TableField field, StringBuilder builder) + { + builder.append(field.name()).append(" DOUBLE"); + } + + protected void appendBooleanField(TableField field, StringBuilder builder) + { + builder.append(field.name()).append(" BIT"); + } + + protected void appendDateField(TableField field, StringBuilder builder) + { + builder.append(field.name()).append(" DATE"); + } + + protected void appendDateTimeField(TableField field, StringBuilder builder) + { + builder.append(field.name()).append(" DATETIME"); + } + + protected void appendBinaryField(TableField field, StringBuilder builder) + { + builder.append(field.name()).append(" VARBINARY"); + } + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/XResources.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/XResources.java new file mode 100644 index 00000000..5c5da958 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/XResources.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import java.util.HashMap; + +/** + * @author Tuan Nguyen (tuan08@users.sourceforge.net) + * @since Oct 22, 2004 + * @version $Id: XResources.java 5332 2006-04-29 18:32:44Z geaz $ + */ +public class XResources extends HashMap +{ + + public Object getResource(Class cl) + { + return get(cl); + } + + public XResources addResource(Class cl, Object resource) + { + put(cl, resource); + return this; + } + + public Object removeResource(Class cl) + { + return remove(cl); + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/annotation/Query.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/annotation/Query.java new file mode 100644 index 00000000..c5ff23c4 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/annotation/Query.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by The eXo Platform SAS Author : Nhu Dinh Thuan + * nhudinhthuan@exoplatform.com Mar 30, 2007 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface Query { + String name(); + + String standardSQL(); + + String mysqlSQL() default ""; + + String mssqlSQL() default ""; + + String oracleSQL() default ""; + + String postgresSQL() default ""; + + String hsqlSQL() default ""; + + String derbySQL() default ""; + + String sysbaseSQL() default ""; + + String db2SQL() default ""; +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/annotation/Table.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/annotation/Table.java new file mode 100644 index 00000000..66716404 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/annotation/Table.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Author : Nhu Dinh Thuan nhudinhthuan@exoplatform.com Jul 5, 2006 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface Table { + String name(); + + TableField[] field(); +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/annotation/TableField.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/annotation/TableField.java new file mode 100644 index 00000000..ab0ae931 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/annotation/TableField.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Author : Nhu Dinh Thuan nhudinhthuan@exoplatform.com Jul 5, 2006 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface TableField { + String name(); + + String field() default ""; + + String type(); + + int length() default -1; + + boolean unique() default false; + + boolean nullable() default true; + + String defaultValue() default ""; +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/creator/DBConnectionInfo.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/creator/DBConnectionInfo.java new file mode 100644 index 00000000..b7fb814f --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/creator/DBConnectionInfo.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.creator; + +import java.util.Map; + +/** + * Class contains needed database connection information. + * + * @author Anatoliy Bazko + * @version $Id$ + */ +public class DBConnectionInfo +{ + private final Map connectionProperties; + + private final String dbName; + + /** + * DBConnectionInfo constructor. + * @param connectionProperties + * connection properties + */ + public DBConnectionInfo(String dbName, Map connectionProperties) + { + this.dbName = dbName; + this.connectionProperties = connectionProperties; + } + + public Map getProperties() + { + return connectionProperties; + } + + public String getDBName() + { + return dbName; + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/creator/DBCreator.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/creator/DBCreator.java new file mode 100644 index 00000000..d6c3aaf4 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/creator/DBCreator.java @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.creator; + +import org.exoplatform.commons.utils.ClassLoading; +import org.exoplatform.commons.utils.IOUtil; +import org.exoplatform.container.configuration.ConfigurationException; +import org.exoplatform.container.configuration.ConfigurationManager; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.container.xml.PropertiesParam; +import org.exoplatform.container.xml.Property; +import org.exoplatform.services.database.utils.DialectConstants; +import org.exoplatform.services.database.utils.DialectDetecter; +import org.exoplatform.services.database.utils.JDBCUtils; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +/** + * @author Anatoliy Bazko + * @version $Id$ + */ +public class DBCreator +{ + + private final static String CONNECTION_PROPERTIES = "db-connection"; + + private final static String DRIVER_NAME = "driverClassName"; + + private final static String SERVER_URL = "url"; + + private final static String USERNAME = "username"; + + private final static String PASSWORD = "password"; + + private final static String DB_CREATION_PROPERTIES = "db-creation"; + + private final static String DB_SCRIPT_PATH = "scriptPath"; + + private final static String DB_USERNAME = "username"; + + private final static String DB_PASSWORD = "password"; + + /** + * Database template. + */ + public static final String DATABASE_TEMPLATE = "${database}"; + + /** + * User name template. + */ + public static final String USERNAME_TEMPLATE = "${username}"; + + /** + * Password template. + */ + public static final String PASSWORD_TEMPLATE = "${password}"; + + /** + * Server url. + */ + protected final String serverUrl; + + /** + * Connection properties. + */ + protected final Map connectionProperties; + + /** + * DDL script database creation. + */ + protected final String dbScript; + + /** + * User name for new database. + */ + protected final String dbUserName; + + /** + * User's password. + */ + protected final String dbPassword; + + /** + * DBCreator constructor. + * + * @param serverUrl Server URL + * @param connectionProperties Connection properties + * @param scriptPath Script path + * @param dbUserName DB username + * @param dbPassword DB password + * @param cm configuration manager instance + */ + public DBCreator(String serverUrl, Map connectionProperties, String scriptPath, String dbUserName, + String dbPassword, ConfigurationManager cm) throws ConfigurationException + { + this.serverUrl = serverUrl; + this.connectionProperties = connectionProperties; + this.dbUserName = dbUserName; + this.dbPassword = dbPassword; + this.dbScript = readScript(scriptPath, cm); + } + + /** + * DBCreator constructor. + * + * @param params + * Initializations parameters + * @param cm + * configuration manager instance + */ + public DBCreator(InitParams params, ConfigurationManager cm) throws ConfigurationException + { + if (params == null) + { + throw new ConfigurationException("Initializations parameters expected"); + } + + PropertiesParam prop = params.getPropertiesParam(CONNECTION_PROPERTIES); + + if (prop != null) + { + if (prop.getProperty(DRIVER_NAME) == null) + { + throw new ConfigurationException("driverClassName expected in db-connection properties section"); + } + + serverUrl = prop.getProperty(SERVER_URL); + if (serverUrl == null) + { + throw new ConfigurationException("url expected in db-connection properties section"); + } + + if (prop.getProperty(USERNAME) == null) + { + throw new ConfigurationException("username expected in db-connection properties section"); + } + + if (prop.getProperty(PASSWORD) == null) + { + throw new ConfigurationException("password expected in db-connection properties section"); + } + + // Store all connection properties into single map + Iterator pit = prop.getPropertyIterator(); + connectionProperties = new HashMap(); + while (pit.hasNext()) + { + Property p = pit.next(); + if (!p.getName().equalsIgnoreCase(SERVER_URL)) + { + connectionProperties.put(p.getName(), p.getValue()); + } + } + } + else + { + throw new ConfigurationException("db-connection properties expected in initializations parameters"); + } + + prop = params.getPropertiesParam(DB_CREATION_PROPERTIES); + if (prop != null) + { + String scriptPath = prop.getProperty(DB_SCRIPT_PATH); + if (scriptPath != null) + { + this.dbScript = readScript(scriptPath, cm); + } + else + { + throw new ConfigurationException("scriptPath expected in db-creation properties section"); + } + + this.dbUserName = prop.getProperty(DB_USERNAME); + if (dbUserName == null) + { + throw new ConfigurationException("username expected in db-creation properties section"); + } + + this.dbPassword = prop.getProperty(DB_PASSWORD); + if (dbPassword == null) + { + throw new ConfigurationException("password expected in db-creation properties section"); + } + } + else + { + throw new ConfigurationException("db-creation properties expected in initializations parameters"); + } + } + + /** + * Execute DDL script for new database creation. Database name are passed as parameter, + * user name and password are passed via configuration. In script database name, user name + * and password defined via templates as ${database}, ${username} and ${password} respectively. + * At execution time method replaces templates by real values. + * + * @param dbName + * new database name + * @throws DBCreatorException + * if any error occurs + */ + public DBConnectionInfo createDatabase(final String dbName) throws DBCreatorException + { + Connection conn = openConnection(); + try + { + String dialect = DialectDetecter.detect(conn.getMetaData()); + + if (dialect.equalsIgnoreCase(DialectConstants.DB_DIALECT_MSSQL) + || dialect.equalsIgnoreCase(DialectConstants.DB_DIALECT_SYBASE)) + { + executeInAutoCommitMode(conn, dbName); + } + else + { + executeInBatchMode(conn, dbName); + } + + return constructDBConnectionInfo(dbName, dialect); + } + catch (SQLException e) + { + throw new DBCreatorException("Can't execute SQL script : " + JDBCUtils.getFullMessage(e), e); + } + finally + { + try + { + conn.close(); + } + catch (SQLException e) + { + throw new DBCreatorException("Can't close connection", e); + } + } + } + + /** + * Get database connection info. + * + * @param dbName + * new database name + * @throws DBCreatorException + * if any error occurs or database is not available + */ + public DBConnectionInfo getDBConnectionInfo(String dbName) throws DBCreatorException + { + Connection conn = openConnection(); + try + { + return constructDBConnectionInfo(dbName, DialectDetecter.detect(conn.getMetaData())); + } + catch (SQLException e) + { + throw new DBCreatorException("Can not get database connection information", e); + } + finally + { + try + { + conn.close(); + } + catch (SQLException e) + { + throw new DBCreatorException("Can't close connection", e); + } + } + } + + /** + * Executes DDL script in generic batch mode. + * + * @param conn + * connection to server + * @param dbName + * database name + * @throws SQLException + * if any errors occurs + */ + private void executeInBatchMode(Connection conn, String dbName) throws SQLException + { + Statement statement = conn.createStatement(); + for (String scr : dbScript.split(";")) + { + scr = scr.replace(DATABASE_TEMPLATE, dbName); + scr = scr.replace(USERNAME_TEMPLATE, dbUserName); + scr = scr.replace(PASSWORD_TEMPLATE, dbPassword); + + String s = JDBCUtils.cleanWhitespaces(scr.trim()); + if (s.length() > 0) + { + statement.addBatch(s); + } + } + statement.executeBatch(); + } + + /** + * Construct database url connection depending on specific database. + * + * @param dbName + * database name + * @param dialect + * dialect + * @param serverUrl + * url to DB server + * @param connectionProperties + * connection properties + * @return DBConnectionInfo + */ + private DBConnectionInfo constructDBConnectionInfo(String dbName, String dialect) + { + StringBuilder dbUrl = new StringBuilder(serverUrl); + + if (dialect.equalsIgnoreCase(DialectConstants.DB_DIALECT_MSSQL)) + { + dbUrl.append(serverUrl.endsWith(";") ? "" : ";"); + dbUrl.append("databaseName="); + dbUrl.append(dbName); + dbUrl.append(";"); + } + else if (dialect.equalsIgnoreCase(DialectConstants.DB_DIALECT_ORACLE) + || dialect.equalsIgnoreCase(DialectConstants.DB_DIALECT_ORACLEOCI)) + { + // do nothing + } + else + { + dbUrl.append(serverUrl.endsWith("/") ? "" : "/"); + dbUrl.append(dbName); + } + + // clone connection properties + Map connProperties = new HashMap(); + + for (Entry entry : connectionProperties.entrySet()) + { + connProperties.put(entry.getKey(), entry.getValue()); + } + + // add url to database + connProperties.put(SERVER_URL, dbUrl.toString()); + + return new DBConnectionInfo(dbName, connProperties); + } + + /** + * Executes DDL script with autocommit mode set true. Actually need for MSSQL and Sybase database servers. + * After execution "create database" command newly created database not available for "use" command and + * therefore you can't create user inside. + * + * @param conn + * connection to server + * @param dbName + * database name + * @throws SQLException + * if any errors occurs + */ + private void executeInAutoCommitMode(Connection conn, String dbName) throws SQLException + { + conn.setAutoCommit(true); + for (String scr : dbScript.split(";")) + { + scr = scr.replace(DATABASE_TEMPLATE, dbName); + scr = scr.replace(USERNAME_TEMPLATE, dbUserName); + scr = scr.replace(PASSWORD_TEMPLATE, dbPassword); + + String s = JDBCUtils.cleanWhitespaces(scr.trim()); + if (s.length() > 0) + { + conn.createStatement().executeUpdate(s); + } + } + } + + /** + * Read script resource. + * + * @param scriptPath + * path to the script + * @param cm + * the configuration manager will help to find script in jars + * @return + * script content + * @throws ConfigurationException + * if script not found + */ + private String readScript(String scriptPath, ConfigurationManager cm) throws ConfigurationException + { + try + { + return IOUtil.getStreamContentAsString(cm.getInputStream(scriptPath)); + } + catch (Exception e) + { + try + { + return IOUtil.getFileContentAsString(scriptPath); + } + catch (IOException ioe) + { + throw new ConfigurationException("Can't read script at " + scriptPath, e); + } + } + } + + /** + * Open connection to the DB. + * + * @param connectionProperties + * connection properties + * @return connection + * @throws DBCreatorException + * if can't establish connection to DB + */ + private Connection openConnection() throws DBCreatorException + { + Connection conn = null; + try + { + ClassLoading.forName(connectionProperties.get(DRIVER_NAME), this); + + conn = DriverManager.getConnection(serverUrl, connectionProperties.get(USERNAME), + connectionProperties.get(PASSWORD)); + + return conn; + } + catch (SQLException e) + { + throw new DBCreatorException("Can't establish the JDBC connection to database " + serverUrl, e); + } + catch (ClassNotFoundException e) + { + throw new DBCreatorException("Can't load the JDBC driver " + connectionProperties.get(DRIVER_NAME), e); + } + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/creator/DBCreatorException.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/creator/DBCreatorException.java new file mode 100644 index 00000000..ab4484ed --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/creator/DBCreatorException.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.creator; + +/** + * @author Anatoliy Bazko + * @version $Id$ + */ +public class DBCreatorException extends Exception +{ + + /** + * DBCreationException constructor. + */ + public DBCreatorException(Throwable e) + { + super(e); + } + + /** + * DBCreationException constructor. + */ + public DBCreatorException(String message, Throwable e) + { + super(message, e); + } + + /** + * DBCreationException constructor. + */ + public DBCreatorException(String message) + { + super(message); + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/impl/HibernateConfigurationImpl.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/impl/HibernateConfigurationImpl.java new file mode 100644 index 00000000..c1a8284b --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/impl/HibernateConfigurationImpl.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.impl; + +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.hibernate.service.ServiceRegistry; + +/** + * Created by The eXo Platform SAS . + * + * @author Gennady Azarenkov + * @version $Id: HibernateConfigurationImpl.java 5332 2006-04-29 18:32:44Z geaz + * $ Hibernate's Configuration. One per 'properties-param' entry in + * container configuration + */ +public class HibernateConfigurationImpl extends Configuration { + public SessionFactory buildSessionFactory() { + ServiceRegistry servReg = getStandardServiceRegistryBuilder().applySettings(getProperties()) + .build(); + return super.buildSessionFactory(servReg); + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/impl/HibernateServiceImpl.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/impl/HibernateServiceImpl.java new file mode 100644 index 00000000..073f7b8e --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/impl/HibernateServiceImpl.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.impl; + +import java.io.Serializable; +import java.security.PrivilegedAction; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; + +import org.exoplatform.commons.exception.ObjectNotFoundException; +import org.exoplatform.container.ExoContainer; +import org.exoplatform.container.component.ComponentRequestLifecycle; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.container.xml.PropertiesParam; +import org.exoplatform.container.xml.Property; +import org.exoplatform.services.database.HibernateService; +import org.exoplatform.services.database.ObjectQuery; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; + +/** + * Created by The eXo Platform SAS . + * + * @author Tuan Nguyen tuan08@users.sourceforge.net Date: Jun 14, 2003 + * @author dhodnett $Id: HibernateServiceImpl.java,v 1.3 2004/10/30 02:27:52 + * tuan08 Exp $ + */ +public class HibernateServiceImpl implements HibernateService, ComponentRequestLifecycle { + + public static final String AUTO_DIALECT = "AUTO"; + + private ThreadLocal threadLocal_; + + private static final Log LOG = ExoLogger.getLogger("exo.core.component.database.HibernateServiceImpl"); + + private HibernateConfigurationImpl conf_; + + private SessionFactory sessionFactory_; + + public HibernateServiceImpl(InitParams initParams) { + threadLocal_ = new ThreadLocal(); + PropertiesParam param = initParams.getPropertiesParam("hibernate.properties"); + conf_ = new HibernateConfigurationImpl(); + Iterator properties = param.getPropertyIterator(); + while (properties.hasNext()) { + Property p = (Property) properties.next(); + conf_.setProperty(p.getName(), p.getValue()); + } + + // Replace the potential "java.io.tmpdir" variable in the connection URL + String connectionURL = conf_.getProperty("hibernate.connection.url"); + if (connectionURL != null) { + connectionURL = + connectionURL.replace("${java.io.tmpdir}", System.getProperty("java.io.tmpdir")); + conf_.setProperty("hibernate.connection.url", connectionURL); + } + } + + public Configuration getHibernateConfiguration() { + return conf_; + } + + /** + * @return the SessionFactory + */ + public SessionFactory getSessionFactory() { + if (sessionFactory_ == null) { + sessionFactory_ = conf_.buildSessionFactory(); + } + + return sessionFactory_; + } + + public Session openSession() { + Session currentSession = threadLocal_.get(); + if (currentSession == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("open new hibernate session in openSession()"); + } + currentSession = getSessionFactory().openSession(); + threadLocal_.set(currentSession); + } + return currentSession; + } + + public Session openNewSession() { + Session currentSession = threadLocal_.get(); + if (currentSession != null) { + closeSession(currentSession); + } + currentSession = getSessionFactory().openSession(); + threadLocal_.set(currentSession); + return currentSession; + } + + public void closeSession(Session session) { + if (session == null) { + return; + } + try { + session.close(); + if (LOG.isDebugEnabled()) { + LOG.debug("close hibernate session in openSession(Session session)"); + } + } catch (HibernateException t) { + LOG.error("Error closing hibernate session : " + t.getMessage(), t); + } + threadLocal_.set(null); + } + + final public void closeSession() { + Session s = threadLocal_.get(); + if (s != null) { + s.close(); + } + threadLocal_.set(null); + } + + public Object findExactOne(Session session, String query, String id) throws Exception { + Object res = session.createQuery(query).setParameter("id", id).uniqueResult(); + if (res == null) { + throw new ObjectNotFoundException("Cannot find the object with id: " + id); + } + return res; + } + + public Object findOne(Session session, String query, String id) throws Exception { + List l = session.createQuery(query).setParameter("id", id).list(); + if (l.size() == 0) { + return null; + } else if (l.size() > 1) { + throw new Exception("Expect only one object but found" + l.size()); + } else { + return l.get(0); + } + } + + public Collection findAll(Session session, String query) throws Exception { + List l = session.createQuery(query).list(); + if (l.size() == 0) { + return null; + } else { + return l; + } + } + + @SuppressWarnings("rawtypes") + public Object findOne(Class clazz, Serializable id) throws Exception { + Session session = openSession(); + Object obj = session.get(clazz, id); + return obj; + } + + public Object findOne(ObjectQuery q) throws Exception { + Session session = openSession(); + List l = session.createQuery(q.getHibernateQuery()).list(); + if (l.size() == 0) { + return null; + } else if (l.size() > 1) { + throw new Exception("Expect only one object but found" + l.size()); + } else { + return l.get(0); + } + } + + public Object create(Object obj) throws Exception { + Session session = openSession(); + session.save(obj); + session.flush(); + return obj; + } + + public Object update(Object obj) throws Exception { + Session session = openSession(); + session.update(obj); + session.flush(); + return obj; + } + + public Object save(Object obj) throws Exception { + Session session = openSession(); + session.merge(obj); + session.flush(); + return obj; + } + + public Object remove(Object obj) throws Exception { + Session session = openSession(); + session.delete(obj); + session.flush(); + return obj; + } + + @SuppressWarnings("rawtypes") + public Object remove(Class clazz, Serializable id) throws Exception { + Session session = openSession(); + Object obj = session.get(clazz, id); + session.delete(obj); + session.flush(); + return obj; + } + + @SuppressWarnings("rawtypes") + public Object remove(Session session, Class clazz, Serializable id) throws Exception { + Object obj = session.get(clazz, id); + session.delete(obj); + return obj; + } + + public void startRequest(ExoContainer container) { + + } + + public void endRequest(ExoContainer container) { + closeSession(); + } + + @Override + public boolean isStarted(ExoContainer container) { + Session s = threadLocal_.get(); + return s != null && s.isOpen(); + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/impl/XAPoolTxSupportDatabaseService.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/impl/XAPoolTxSupportDatabaseService.java new file mode 100644 index 00000000..9be06cf3 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/impl/XAPoolTxSupportDatabaseService.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.impl; + +import org.enhydra.jdbc.pool.StandardXAPoolDataSource; +import org.enhydra.jdbc.standard.StandardXADataSource; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.container.xml.PropertiesParam; +import org.exoplatform.services.database.DatabaseService; +import org.exoplatform.services.database.ExoDatasource; +import org.exoplatform.services.transaction.TransactionService; + +import java.sql.Connection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.sql.DataSource; + +/** + * Created by The eXo Platform SAS + * Author : Tuan Nguyen tuan08@users.sourceforge.net Apr 4, 2006 + */ +public class XAPoolTxSupportDatabaseService implements DatabaseService +{ + private HashMap datasources_; + + private ExoDatasource defaultDS_; + + private TransactionService txService_; + + public XAPoolTxSupportDatabaseService(InitParams params, TransactionService txService) throws Exception + { + datasources_ = new HashMap(5); + txService_ = txService; + Iterator i = params.getPropertiesParamIterator(); + while (i.hasNext()) + { + PropertiesParam param = (PropertiesParam)i.next(); + String name = param.getName(); + ExoDatasource ds = new ExoDatasource(createDatasource(param.getProperties())); + datasources_.put(name, ds); + if (defaultDS_ == null) + defaultDS_ = ds; + } + } + + public ExoDatasource getDatasource() throws Exception + { + return defaultDS_; + } + + public ExoDatasource getDatasource(String dsName) throws Exception + { + return datasources_.get(dsName); + } + + public Connection getConnection() throws Exception + { + return defaultDS_.getConnection(); + } + + public Connection getConnection(String dsName) throws Exception + { + ExoDatasource ds = datasources_.get(dsName); + return ds.getConnection(); + } + + public void closeConnection(Connection conn) throws Exception + { + defaultDS_.closeConnection(conn); + } + + public TransactionService getTransactionService() throws Exception + { + return txService_; + } + + private DataSource createDatasource(Map props) throws Exception + { + StandardXADataSource ds = new StandardXADataSource(); + + ds.setDriverName(props.get("connection.driver")); + ds.setUrl(props.get("connection.url")); + ds.setUser(props.get("connection.login")); + ds.setPassword(props.get("connection.password")); + ds.setTransactionManager(txService_.getTransactionManager()); + + StandardXAPoolDataSource pool = new StandardXAPoolDataSource(3); + pool.setMinSize(Integer.parseInt(props.get("connection.min-size"))); + pool.setMaxSize(Integer.parseInt(props.get("connection.max-size"))); + pool.setUser(props.get("connection.login")); + pool.setPassword(props.get("connection.password")); + pool.setDataSource(ds); + return pool; + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/jdbc/CreateDBSchemaPlugin.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/jdbc/CreateDBSchemaPlugin.java new file mode 100644 index 00000000..2e0de838 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/jdbc/CreateDBSchemaPlugin.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.jdbc; + +import org.exoplatform.container.component.BaseComponentPlugin; +import org.exoplatform.container.configuration.ConfigurationException; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.container.xml.ValueParam; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +/** + * Created by The eXo Platform SAS . + * + * @author Gennady + * Azarenkov + * @version $Id: CreateDBSchemaPlugin.java 8017 2006-08-16 15:12:00Z peterit $ + */ +public class CreateDBSchemaPlugin extends BaseComponentPlugin +{ + + protected static final Log LOG = ExoLogger.getLogger("exo.core.component.database.CreateDBSchemaPlugin"); + + private String dataSource; + + private String script; + + public CreateDBSchemaPlugin(InitParams params) throws ConfigurationException + { + ValueParam dsParam = params.getValueParam("data-source"); + ValueParam scriptFileParam = params.getValueParam("script-file"); + ValueParam scriptParam = params.getValueParam("script"); + + if (dsParam == null) + return; + dataSource = dsParam.getValue(); + if (scriptParam != null) + { + script = scriptParam.getValue(); + return; + } + // ClassLoader cl = this.getClass().getClassLoader(); + // //Thread.currentThread().getContextClassLoader(); + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + InputStream is = cl.getResourceAsStream(scriptFileParam.getValue()); + + if (is == null) + is = ClassLoader.getSystemResourceAsStream(scriptFileParam.getValue()); + + if (is == null) + { + try + { + LOG.warn("Db script not found as system resource... Trying to search as file by path: " + + scriptFileParam.getValue()); + is = new FileInputStream(scriptFileParam.getValue()); + LOG.info("Db script found as file by path: " + scriptFileParam.getValue()); + } + catch (IOException e) + { + LOG.warn("Db script not found as file by path: " + scriptFileParam.getValue() + ". " + e.getMessage()); + } + } + + if (is == null) + { + try + { + LOG.warn("Db script not found as system resource... Trying to search as file by url: " + + scriptFileParam.getValue()); + is = new URL(scriptFileParam.getValue()).openStream(); + LOG.info("Db script found as file by url: " + scriptFileParam.getValue()); + } + catch (IOException e) + { + LOG.warn("Db script not found as file by url: " + scriptFileParam.getValue() + ". " + e.getMessage()); + } + } + + if (is == null) + { + throw new ConfigurationException("Could not open input stream for db script " + + cl.getResource(scriptFileParam.getValue())); + } + + try + { + byte[] buf = new byte[is.available()]; + is.read(buf); + script = new String(buf); + } + catch (IOException e) + { + LOG.error(e.getLocalizedMessage(), e); + } + finally + { + try + { + is.close(); + } + catch (IOException e) + { + if (LOG.isTraceEnabled()) + { + LOG.trace("An exception occurred: " + e.getMessage()); + } + } + } + } + + public String getDataSource() + { + return dataSource; + } + + public String getScript() + { + return script; + } + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/jdbc/DBSchemaCreator.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/jdbc/DBSchemaCreator.java new file mode 100644 index 00000000..81110605 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/jdbc/DBSchemaCreator.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.jdbc; + +import org.exoplatform.container.component.ComponentPlugin; +import org.exoplatform.services.database.utils.JDBCUtils; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.exoplatform.services.naming.InitialContextInitializer; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.sql.DataSource; + +/** + * Created by The eXo Platform SAS . + * + * @author Gennady Azarenkov + * @version $Id: DBSchemaCreator.java 13053 2007-03-01 06:44:00Z tuan08 $ + */ + +public class DBSchemaCreator +{ + static private String SQL_ALREADYEXISTS = ".*((already exist)|(duplicate key)| (already used)|(ORA-00955))+.*"; + + private final Pattern pattern; + + private static final Log LOG = ExoLogger.getLogger("exo.core.component.database.DBSchemaCreator"); + + private List createDBSchemaPlugins = new ArrayList(); + + public DBSchemaCreator(InitialContextInitializer contextInit) + { + pattern = Pattern.compile(SQL_ALREADYEXISTS, Pattern.CASE_INSENSITIVE); + } + + // for testing only + private DBSchemaCreator(String dsName, String script) throws SQLException, NamingException + { + pattern = Pattern.compile(SQL_ALREADYEXISTS, Pattern.CASE_INSENSITIVE); + createTables(dsName, script); + } + + public void createTables(String dsName, String script) throws NamingException, SQLException + { + InitialContext context = new InitialContext(); + DataSource ds = (DataSource)context.lookup(dsName); + Connection conn = ds.getConnection(); + String sql = ""; + try + { + String[] scripts = JDBCUtils.splitWithSQLDelimiter(script); + + for (String scr : scripts) + { + String s = JDBCUtils.cleanWhitespaces(scr.trim()); + + if (s.length() < 1) + continue; + sql = s; + if (LOG.isDebugEnabled()) + LOG.debug("Execute script: \n[" + sql + "]"); + + try + { + conn.setAutoCommit(false); + conn.createStatement().executeUpdate(sql); + conn.commit(); + } + catch (SQLException e) + { + conn.rollback(); + // already exists check + Matcher aeMatcher = pattern.matcher(e.getMessage().trim()); + if (!aeMatcher.matches()) + throw e; + if (LOG.isDebugEnabled()) + LOG.debug(e.getMessage()); + } + + } + LOG.info("DB schema of DataSource: '" + dsName + "' created succesfully. context " + context); + } + catch (SQLException e) + { + LOG.error("Could not create db schema of DataSource: '" + dsName + "'. Reason: " + e.getMessage() + "; " + + JDBCUtils.getFullMessage(e) + ". Last command: " + sql, e); + } + finally + { + conn.close(); + } + + } + + public void addPlugin(ComponentPlugin plugin) + { + if (plugin instanceof CreateDBSchemaPlugin) + { + CreateDBSchemaPlugin csplugin = (CreateDBSchemaPlugin)plugin; + try + { + createTables(csplugin.getDataSource(), csplugin.getScript()); + createDBSchemaPlugins.add(csplugin); + } + catch (NamingException e) + { + LOG.error(e.getLocalizedMessage(), e); + } + catch (SQLException e) + { + LOG.error(e.getLocalizedMessage(), e); + } + } + } + + public ComponentPlugin removePlugin(String name) + { + return null; + } + + public Collection getPlugins() + { + return createDBSchemaPlugins; + } + + // for testing + public static DBSchemaCreator initialize(String dsName, String script) throws SQLException, NamingException + { + return new DBSchemaCreator(dsName, script); + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/table/ExoLongID.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/table/ExoLongID.java new file mode 100644 index 00000000..ee6a8b1a --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/table/ExoLongID.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.table; + +import org.exoplatform.services.database.DBObject; +import org.exoplatform.services.database.annotation.Table; +import org.exoplatform.services.database.annotation.TableField; + +/** + * Created by The eXo Platform SAS Mar 16, 2007 + */ +@Table(name = "EXO_LONG_ID", field = { + @TableField(name = "EXO_NAME", type = "string", length = 500, unique = true, nullable = false), + @TableField(name = "EXO_START", type = "long")}) +public class ExoLongID extends DBObject +{ + + final static public long BLOCK_SIZE = 3; + + private String name; + + private long currentBlockId; + + public ExoLongID() + { + } + + public ExoLongID(String name, long start) + { + this.name = name; + this.currentBlockId = start; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public long getCurrentBlockId() + { + return currentBlockId; + } + + public void setCurrentBlockId(long start) + { + this.currentBlockId = start; + } + + public void setNextBlock() + { + this.currentBlockId = this.currentBlockId + BLOCK_SIZE; + } + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/table/IDGenerator.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/table/IDGenerator.java new file mode 100644 index 00000000..9aabfcdc --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/table/IDGenerator.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.table; + +import org.exoplatform.commons.utils.PageList; +import org.exoplatform.services.database.DAO; +import org.exoplatform.services.database.DBObject; +import org.exoplatform.services.database.DBObjectMapper; +import org.exoplatform.services.database.DBTableManager; +import org.exoplatform.services.database.ExoDatasource; +import org.exoplatform.services.database.annotation.Table; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.HashMap; +import java.util.List; + +/** + * Created by The eXo Platform SAS + * Author : Tuan Nguyen tuan08@users.sourceforge.net Apr 4, 2006 + */ +public class IDGenerator extends DAO +{ + + private HashMap idTrackers_; + + public IDGenerator(ExoDatasource datasource) throws Exception + { + super(datasource, new ExoLongIDMapper()); + idTrackers_ = new HashMap(); + + DBTableManager tableManager = datasource.getDBTableManager(); + if (tableManager.hasTable(ExoLongID.class)) + return; + tableManager.createTable(ExoLongID.class, true); + } + + public ExoLongID loadObjectByName(String name) throws Exception + { + Table table = ExoLongID.class.getAnnotation(Table.class); + StringBuilder builder = new StringBuilder("SELECT EXO_NAME, EXO_START FROM "); + builder.append(table.name()).append(" WHERE EXO_NAME = '").append(name).append('\''); + return loadUnique(builder.toString()); + } + + public long generateLongId(T bean) throws Exception + { + return generateLongId(bean.getClass()); + } + + // Lazy loading + synchronized public long generateLongId(Class type) throws Exception + { + IDTracker idTracker = idTrackers_.get(type); + + if (idTracker == null) + { + ExoLongID instanceID = loadObjectByName(type.getName()); + long currentId = 0; + + if (instanceID == null) + { + instanceID = new ExoLongID(type.getName(), ExoLongID.BLOCK_SIZE); + save(instanceID); + } + else + { + currentId = instanceID.getCurrentBlockId(); + instanceID.setNextBlock(); + update(instanceID); + } + idTracker = new IDTracker(instanceID, currentId); + idTrackers_.put(type, idTracker); + } + + // System.out.println("+++>>" + load(1)) ; + + long generatedId = ++idTracker.currentId; + if (generatedId > idTracker.blockTracker.getCurrentBlockId() + ExoLongID.BLOCK_SIZE) + { + idTracker.blockTracker.setNextBlock(); + update(idTracker.blockTracker); + } + return generatedId; + } + + public void restartTracker() + { + idTrackers_.clear(); + } // for testing + + static private class IDTracker + { + + private ExoLongID blockTracker; + + private long currentId; + + private IDTracker(ExoLongID dbobject, long id) + { + blockTracker = dbobject; + currentId = id; + } + } + + static public class ExoLongIDMapper implements DBObjectMapper + { + + public String[][] toParameters(ExoLongID bean) throws Exception + { + return null; + } + + public void mapUpdate(ExoLongID bean, PreparedStatement pstm) throws Exception + { + pstm.setString(1, bean.getName()); + pstm.setLong(2, bean.getCurrentBlockId()); + } + + public void mapResultSet(ResultSet res, ExoLongID bean) throws Exception + { + bean.setName(res.getString(1)); + bean.setCurrentBlockId(res.getLong(2)); + } + } + + public ExoLongID createInstance() throws Exception + { + return new ExoLongID(); + } + + public ExoLongID load(long id) throws Exception + { + return null; + } + + public PageList loadAll() throws Exception + { + return null; + } + + public ExoLongID remove(long id) throws Exception + { + return null; + } + + public void remove(ExoLongID bean) throws Exception + { + } + + public void save(ExoLongID bean) throws Exception + { + } + + public void save(List beans) throws Exception + { + } + + public void update(ExoLongID bean) throws Exception + { + } + + public void update(List beans) throws Exception + { + } +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/utils/DialectConstants.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/utils/DialectConstants.java new file mode 100644 index 00000000..4340d972 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/utils/DialectConstants.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2012 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.utils; + +/** + * @author Anatoliy Bazko + * @version $Id: DialectConstants.java 34360 2009-07-22 23:58:59Z tolusha $ + */ +public class DialectConstants +{ + /** + * DB_DIALECT_AUTO. + */ + public final static String DB_DIALECT_AUTO = "AUTO"; + + /** + * DB_DIALECT_GENERIC. + */ + public final static String DB_DIALECT_GENERIC = "GENERIC"; + + /** + * DB_DIALECT_ORACLE. + */ + public final static String DB_DIALECT_ORACLE = "ORACLE"; + + /** + * DB_DIALECT_ORACLEOCI. + */ + public final static String DB_DIALECT_ORACLEOCI = "ORACLE-OCI"; + + /** + * DB_DIALECT_PGSQL. + */ + public final static String DB_DIALECT_PGSQL = "PGSQL"; + + /** + * DB_DIALECT_PGSQL_SCS. + */ + public final static String DB_DIALECT_PGSQL_SCS = "PGSQL-SCS"; + + /** + * DB_DIALECT_MYSQL. + */ + public final static String DB_DIALECT_MYSQL = "MYSQL"; + + /** + * DB_DIALECT_MYSQL_UTF8. + */ + public final static String DB_DIALECT_MYSQL_UTF8 = "MYSQL-UTF8"; + + /** + * DB_DIALECT_MYSQL_NDB. + */ + public final static String DB_DIALECT_MYSQL_NDB = "MYSQL-NDB"; + + /** + * DB_DIALECT_MYSQL_NDB_UTF8. + */ + public final static String DB_DIALECT_MYSQL_NDB_UTF8 = "MYSQL-NDB-UTF8"; + + /** + * DB_DIALECT_MYSQL_MYISAM. + */ + public final static String DB_DIALECT_MYSQL_MYISAM = "MYSQL-MyISAM"; + + /** + * DB_DIALECT_MYSQL_MYISAM_UTF8. + */ + public final static String DB_DIALECT_MYSQL_MYISAM_UTF8 = "MYSQL-MyISAM-UTF8"; + + /** + * DB_DIALECT_HSQLDB. + */ + public final static String DB_DIALECT_HSQLDB = "HSQLDB"; + + /** + * DB_DIALECT_DB2. + */ + public final static String DB_DIALECT_DB2 = "DB2"; + + + /** + * DB_DIALECT_DB2V8. + */ + public final static String DB_DIALECT_DB2V8 = "DB2V8"; + + /** + * DB_DIALECT_MSSQL. + */ + public final static String DB_DIALECT_MSSQL = "MSSQL"; + + /** + * DB_DIALECT_SYBASE. + */ + public final static String DB_DIALECT_SYBASE = "SYBASE"; + + /** + * DB_DIALECT_DERBY. + */ + public final static String DB_DIALECT_DERBY = "DERBY"; + + /** + * DB_DIALECT_INGRES. + */ + public final static String DB_DIALECT_INGRES = "INGRES"; + + /** + * DB_DIALECT_H2. + */ + public final static String DB_DIALECT_H2 = "H2"; + + /** + * DB_DIALECTS. + */ + public final static String[] DB_DIALECTS = {DB_DIALECT_GENERIC, DB_DIALECT_ORACLE, DB_DIALECT_ORACLEOCI, + DB_DIALECT_PGSQL, DB_DIALECT_PGSQL_SCS, DB_DIALECT_MYSQL, DB_DIALECT_MYSQL_NDB, DB_DIALECT_MYSQL_NDB_UTF8, + DB_DIALECT_HSQLDB, DB_DIALECT_DB2, DB_DIALECT_DB2V8, DB_DIALECT_MSSQL, DB_DIALECT_SYBASE, + DB_DIALECT_DERBY, DB_DIALECT_MYSQL_UTF8, DB_DIALECT_INGRES, DB_DIALECT_H2, DB_DIALECT_MYSQL_MYISAM, + DB_DIALECT_MYSQL_MYISAM_UTF8}; + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/utils/DialectDetecter.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/utils/DialectDetecter.java new file mode 100644 index 00000000..d005fc64 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/utils/DialectDetecter.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.utils; + +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; + +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +/** + * JDBC dialect detecter based on database metadata and vendor product name. + * + * @author Peter Nedonosko + * @version $Id:DialectDetecter.java 1111 2010-01-01 00:00:01Z pnedonosko $ + */ +public class DialectDetecter +{ + + /** + * Logger. + */ + private final static Log LOG = ExoLogger.getLogger("exo.core.component.database.DialectDetecter"); + + /** + * Detect databse dialect using JDBC metadata. Based on code of + * http://svn.jboss.org/repos/hibernate/core/trunk/core/src/main/java/org/hibernate/ + * dialect/resolver/StandardDialectResolver.java + * + * @param metaData {@link DatabaseMetaData} + * @return String + * @throws SQLException if error occurs + */ + public static String detect(final DatabaseMetaData metaData) throws SQLException + { + final String databaseName = metaData.getDatabaseProductName(); + + if ("HSQL Database Engine".equals(databaseName)) + { + return DialectConstants.DB_DIALECT_HSQLDB; + } + + if ("H2".equals(databaseName)) + { + return DialectConstants.DB_DIALECT_H2; + } + + if ("MySQL".equals(databaseName)) + { + return DialectConstants.DB_DIALECT_MYSQL; + } + + if ("PostgreSQL".equals(databaseName)) + { + int majorVersion = metaData.getDatabaseMajorVersion(); + int minorVersion = metaData.getDatabaseMinorVersion(); + + return (majorVersion > 9 || (majorVersion == 9 && minorVersion >= 1)) ? DialectConstants.DB_DIALECT_PGSQL_SCS + : DialectConstants.DB_DIALECT_PGSQL; + } + + if ("EnterpriseDB".equals(databaseName)) + { + return DialectConstants.DB_DIALECT_PGSQL_SCS; + } + + if ("Apache Derby".equals(databaseName)) + { + return DialectConstants.DB_DIALECT_DERBY; + } + + if ("ingres".equalsIgnoreCase(databaseName)) + { + return DialectConstants.DB_DIALECT_INGRES; + } + + if (databaseName.startsWith("Microsoft SQL Server")) + { + return DialectConstants.DB_DIALECT_MSSQL; + } + + if ("Sybase SQL Server".equals(databaseName) || "Adaptive Server Enterprise".equals(databaseName)) + { + return DialectConstants.DB_DIALECT_SYBASE; + } + + if (databaseName.startsWith("Adaptive Server Anywhere")) + { + return DialectConstants.DB_DIALECT_SYBASE; + } + + if (databaseName.startsWith("DB2/")) + { + return detectDB2Dialect(metaData); + } + + if ("Oracle".equals(databaseName)) + { + return DialectConstants.DB_DIALECT_ORACLE; + } + + return DialectConstants.DB_DIALECT_GENERIC; + } + + /** + * Detects DB2 dialect. + */ + private static String detectDB2Dialect(final DatabaseMetaData metaData) throws SQLException + { + if (LOG.isDebugEnabled()) + { + LOG.debug("DB Major version = " + metaData.getDatabaseMajorVersion() + ", DB Minor version = " + + metaData.getDatabaseMinorVersion() + ", DB Product version = " + metaData.getDatabaseProductVersion()); + } + + int majorVersion = metaData.getDatabaseMajorVersion(); + if (majorVersion < 9) + { + if (LOG.isDebugEnabled()) + { + LOG.debug("The dialect " + DialectConstants.DB_DIALECT_DB2V8 + + " will be used as the major version is lower than 9."); + } + + return DialectConstants.DB_DIALECT_DB2V8; + } + else + { + if (LOG.isDebugEnabled()) + { + LOG.debug("The dialect " + DialectConstants.DB_DIALECT_DB2 + + " will be used as the major version is greater or equal to 9"); + } + + return DialectConstants.DB_DIALECT_DB2; + } + } + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/database/utils/JDBCUtils.java b/exo.core.component.database/src/main/java/org/exoplatform/services/database/utils/JDBCUtils.java new file mode 100644 index 00000000..acc54b13 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/database/utils/JDBCUtils.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2011 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database.utils; + +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import javax.sql.DataSource; + +/** + * This class provides JDBC tools + * + * @author Nicolas Filotto + * @version $Id$ + */ +/** + * @author Anatoliy Bazko + * @version $Id: JDBCUtils.java 34360 2009-07-22 23:58:59Z tolusha $ + * + */ +public class JDBCUtils +{ + private static final Log LOG = ExoLogger.getLogger("exo.core.component.database.JDBCUtils"); + + /** + * Default SQL delimiter. + */ + public static final String SQL_DELIMITER = ";"; + + /** + * SQL delimiter comment prefix. + */ + public static final String SQL_DELIMITER_COMMENT_PREFIX = "/*$DELIMITER:"; + + public static final String SQL_DELIMITER_COMMENT_SUFFIX = "*/"; + + private JDBCUtils() + { + } + + /** + * Indicates whether or not a given table exists + * + * @param tableName + * the name of the table to check + * @param con + * the connection to use + * @return true if it exists, false otherwise + */ + public static boolean tableExists(String tableName, Connection con) + { + Statement stmt = null; + ResultSet trs = null; + try + { + String dialect = DialectDetecter.detect(con.getMetaData()); + String query; + if (dialect.startsWith(DialectConstants.DB_DIALECT_MYSQL) || dialect.startsWith(DialectConstants.DB_DIALECT_PGSQL)) + { + query = "SELECT count(*) from (SELECT 1 FROM " + tableName + " LIMIT 1) T"; + } + else if (dialect.startsWith(DialectConstants.DB_DIALECT_ORACLE)) + { + query = "SELECT count(*) from (SELECT 1 FROM " + tableName + " WHERE ROWNUM = 1) T"; + } + else if (dialect.startsWith(DialectConstants.DB_DIALECT_DB2) || dialect.startsWith(DialectConstants.DB_DIALECT_DERBY) + || dialect.startsWith(DialectConstants.DB_DIALECT_INGRES)) + { + query = "SELECT count(*) from (SELECT 1 FROM " + tableName + " FETCH FIRST 1 ROWS ONLY) T"; + } + else if (dialect.startsWith(DialectConstants.DB_DIALECT_MSSQL)) + { + query = "SELECT count(*) from (SELECT TOP (1) 1 as C FROM " + tableName + ") T"; + } + else if (dialect.startsWith(DialectConstants.DB_DIALECT_SYBASE)) + { + query = "SELECT count(*) from (SELECT TOP 1 1 FROM " + tableName + ") T"; + } + else + { + query = "SELECT count(*) FROM " + tableName; + } + stmt = con.createStatement(); + trs = stmt.executeQuery(query); + return trs.next(); + } + catch (SQLException e) + { + if (LOG.isDebugEnabled()) + { + LOG.debug("SQLException occurs while checking the table " + tableName, e); + } + return false; + } + finally + { + freeResources(trs, stmt, null); + } + } + + /** + * Retrieves the full message from SQLException. + * + * @param exception + * SQLException which will be parsed + */ + public static String getFullMessage(SQLException exception) + { + StringBuilder errorTrace = new StringBuilder(exception.getMessage()); + + SQLException next = exception; + while (next != null) + { + errorTrace.append("; "); + errorTrace.append(next.getMessage()); + + next = next.getNextException(); + } + + Throwable cause = exception.getCause(); + + return errorTrace + (cause != null ? " (Cause: " + cause.getMessage() + ")" : ""); + } + + /** + * Replace whitespace characters with space character. + */ + public static String cleanWhitespaces(String string) + { + if (string != null) + { + char[] cc = string.toCharArray(); + for (int ci = cc.length - 1; ci > 0; ci--) + { + if (Character.isWhitespace(cc[ci])) + { + cc[ci] = ' '; + } + } + return new String(cc); + } + return string; + } + + /** + * Split string resource with SQL Delimiter. Delimiter can be taken from resource + * at the begining of the first line. It surrounded with {@link #SQL_DELIMITER_COMMENT_PREFIX} + * and {@link #SQL_DELIMITER_COMMENT_SUFFIX}. Otherwise the default delimiter will + * be used {@link #SQL_DELIMITER}. + */ + public static String[] splitWithSQLDelimiter(String resource) + { + if (resource.startsWith(SQL_DELIMITER_COMMENT_PREFIX)) + { + try + { + String scripts = resource.substring(SQL_DELIMITER_COMMENT_PREFIX.length()); + + int endOfDelimIndex = scripts.indexOf(SQL_DELIMITER_COMMENT_SUFFIX); + String delim = scripts.substring(0, endOfDelimIndex).trim(); + + scripts = scripts.substring(endOfDelimIndex + 2).trim(); + return scripts.split(delim); + } + catch (IndexOutOfBoundsException e) + { + LOG.warn("Error of parse SQL-script file. Invalid DELIMITER configuration. Valid format is '" + + SQL_DELIMITER_COMMENT_PREFIX + "XXX*/' at begin of the SQL-script file, where XXX - DELIMITER string." + + " Spaces will be trimed. ", e); + LOG.info("Using DELIMITER:[" + SQL_DELIMITER + "]"); + + return resource.split(SQL_DELIMITER); + } + } + else + { + return resource.split(SQL_DELIMITER); + } + } + + /** + * Returns appropriate blob type field for specific database. + */ + public static String getAppropriateBlobType(DataSource dataSource) throws SQLException + { + String dialect = resolveDialect(dataSource); + + if (dialect.startsWith(DialectConstants.DB_DIALECT_HSQLDB)) + { + return "VARBINARY(65535)"; + } + else if (dialect.startsWith(DialectConstants.DB_DIALECT_MYSQL)) + { + return "LONGBLOB"; + } + else if (dialect.startsWith(DialectConstants.DB_DIALECT_PGSQL)) + { + return "bytea"; + } + else if (dialect.startsWith(DialectConstants.DB_DIALECT_MSSQL)) + { + return "VARBINARY(MAX)"; + } + else if (dialect.startsWith(DialectConstants.DB_DIALECT_SYBASE)) + { + return "IMAGE"; + } + else if (dialect.startsWith(DialectConstants.DB_DIALECT_INGRES)) + { + return "long byte"; + } + + return "BLOB"; + } + + /** + * Returns appropriate timestamp type field for specific database. + */ + public static String getAppropriateTimestamp(DataSource dataSource) throws SQLException + { + String dialect = resolveDialect(dataSource); + + if (dialect.startsWith(DialectConstants.DB_DIALECT_ORACLE)) + { + return "NUMBER(19, 0)"; + } + + return "BIGINT"; + } + + /** + * Returns appropriate char type field for specific database. + */ + public static String getAppropriateCharType(DataSource dataSource) throws SQLException + { + String dialect = resolveDialect(dataSource); + + if (dialect.startsWith(DialectConstants.DB_DIALECT_ORACLE)) + { + // Oracle suggests the use VARCHAR2 instead of VARCHAR while declaring data type. + return "VARCHAR2(512)"; + } + + return "VARCHAR(512)"; + } + + /** + * Returns dialect one of dialect {@link DialectConstants} based on {@link DataSource} name. + */ + public static String resolveDialect(final DataSource dataSource) throws SQLException + { + Connection jdbcConn = dataSource.getConnection(); + + try + { + return DialectDetecter.detect(jdbcConn.getMetaData()); + } + finally + { + freeResources(null, null, jdbcConn); + } + } + + /** + * Closes database related resources. + */ + public static void freeResources(ResultSet resultSet, Statement statement, Connection conn) + { + if (resultSet != null) + { + try + { + resultSet.close(); + } + catch (SQLException e) + { + LOG.error(e.getMessage(), e); + } + } + + if (statement != null) + { + try + { + statement.close(); + } + catch (SQLException e) + { + LOG.error(e.getMessage(), e); + } + } + + if (conn != null) + { + try + { + conn.close(); + } + catch (SQLException e) + { + LOG.error(e.getMessage(), e); + } + } + } + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/transaction/TransactionService.java b/exo.core.component.database/src/main/java/org/exoplatform/services/transaction/TransactionService.java new file mode 100644 index 00000000..bc95d1b5 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/transaction/TransactionService.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.transaction; + +import javax.transaction.RollbackException; +import javax.transaction.SystemException; +import javax.transaction.TransactionManager; +import javax.transaction.UserTransaction; +import javax.transaction.xa.XAResource; + +/** + * Created by The eXo Platform SAS.
The transaction service + * + * @author Gennady + * Azarenkov + * @version $Id: $ + */ + +public interface TransactionService +{ + + /** + * @return TransactionManager + */ + TransactionManager getTransactionManager(); + + /** + * @return UserTransaction + */ + UserTransaction getUserTransaction(); + + /** + * @return default timeout in seconds + */ + int getDefaultTimeout(); + + /** + * Sets timeout in seconds, + * + * @param seconds int + * @throws SystemException + */ + void setTransactionTimeout(int seconds) throws SystemException; + + /** + * Enlists XA resource in transaction manager. + * + * @param xares XAResource + * @return true if the resource was enlisted successfully; otherwise + * false. + * + * @exception RollbackException Thrown to indicate that + * the transaction has been marked for rollback only. + * + * @exception IllegalStateException Thrown if the transaction in the + * target object is in the prepared state or the transaction is + * inactive. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + */ + boolean enlistResource(XAResource xares) throws RollbackException, SystemException, IllegalStateException; + + /** + * Delists XA resource from transaction manager. + * + * @param xares XAResource + * @exception IllegalStateException Thrown if the transaction in the + * target object is inactive. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + * @return true if the resource was delisted successfully; otherwise + * false. + */ + boolean delistResource(XAResource xares) throws RollbackException, SystemException, IllegalStateException; + +} diff --git a/exo.core.component.database/src/main/java/org/exoplatform/services/transaction/impl/AbstractTransactionService.java b/exo.core.component.database/src/main/java/org/exoplatform/services/transaction/impl/AbstractTransactionService.java new file mode 100644 index 00000000..f9d02f45 --- /dev/null +++ b/exo.core.component.database/src/main/java/org/exoplatform/services/transaction/impl/AbstractTransactionService.java @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2011 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.transaction.impl; + +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.exoplatform.services.transaction.TransactionService; + +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.InvalidTransactionException; +import javax.transaction.NotSupportedException; +import javax.transaction.RollbackException; +import javax.transaction.Status; +import javax.transaction.SystemException; +import javax.transaction.Transaction; +import javax.transaction.TransactionManager; +import javax.transaction.UserTransaction; +import javax.transaction.xa.XAResource; + +/** + * This abstract class implements the main logic of all the methods expected for a + * {@link TransactionService}. If you intend to use a {@link TransactionManager} in + * standalone mode (not manager by your Application Server), you can set the + * transaction timeout thanks to the value-param called timeout + * the value of this parameter is expressed in seconds. + * + * @author Nicolas Filotto + * @version $Id$ + * + */ +public abstract class AbstractTransactionService implements TransactionService +{ + /** + * The logger + */ + private static final Log LOG = ExoLogger.getLogger("exo.kernel.component.common.AbstractTransactionService"); + + /** + * The default value of a transaction timeout in seconds + */ + private static final int DEFAULT_TIME_OUT = 60; + + /** + * The default timeout + */ + protected final int defaultTimeout; + + /** + * Indicates if the timeout has to be enforced + */ + protected final boolean forceTimeout; + + /** + * The current Transaction Manager + */ + private volatile TransactionManager tm; + + /** + * The current User Transaction + */ + private volatile UserTransaction ut; + + public AbstractTransactionService() + { + this(null); + } + + public AbstractTransactionService(InitParams params) + { + if (params != null && params.getValueParam("timeout") != null) + { + this.defaultTimeout = Integer.parseInt(params.getValueParam("timeout").getValue()); + this.forceTimeout = true; + } + else + { + this.defaultTimeout = DEFAULT_TIME_OUT; + this.forceTimeout = false; + } + } + + /** + * {@inheritDoc} + */ + public boolean delistResource(final XAResource xares) throws RollbackException, SystemException + { + TransactionManager tm = getTransactionManager(); + final Transaction tx = tm.getTransaction(); + if (tx != null) + { + int flag = XAResource.TMSUCCESS; + switch (tx.getStatus()) + { + case Status.STATUS_MARKED_ROLLBACK: + case Status.STATUS_ROLLEDBACK: + case Status.STATUS_ROLLING_BACK: flag = XAResource.TMFAIL; + } + return tx.delistResource(xares, flag); + } + else + { + throw new IllegalStateException("Could not delist the XA Resource since there is no active session"); + } + } + + /** + * {@inheritDoc} + */ + public boolean enlistResource(final XAResource xares) throws RollbackException, SystemException + { + TransactionManager tm = getTransactionManager(); + final Transaction tx = tm.getTransaction(); + if (tx != null) + { + return tx.enlistResource(xares); + } + else + { + throw new IllegalStateException("Could not enlist the XA Resource since there is no active session"); + } + } + + /** + * {@inheritDoc} + */ + public int getDefaultTimeout() + { + return defaultTimeout; + } + + /** + * {@inheritDoc} + */ + public final TransactionManager getTransactionManager() + { + if (tm == null) + { + synchronized (this) + { + if (tm == null) + { + TransactionManager tm; + try + { + tm = findTransactionManager(); + } + catch (Exception e) + { + throw new RuntimeException("Transaction manager not found", e); + } + if (forceTimeout) + { + // Only set the timeout when a timeout has been given into the + // configuration otherwise we assume that the value will be + // set at the AS level + tm = new TransactionManagerTxTimeoutAware(tm, defaultTimeout); + } + this.tm = tm; + } + } + } + return tm; + } + + /** + * Indicates whether or not the {@link TransactionManager} has been initialized + */ + protected boolean isTMInitialized() + { + return tm != null; + } + + /** + * This method will try to find the current {@link TransactionManager} + * @return the current {@link TransactionManager} + * @throws Exception if an error occurs while looking for the {@link TransactionManager} + */ + protected abstract TransactionManager findTransactionManager() throws Exception; + + /** + * {@inheritDoc} + */ + public final UserTransaction getUserTransaction() + { + if (ut == null) + { + synchronized (this) + { + if (ut == null) + { + UserTransaction ut; + try + { + ut = findUserTransaction(); + } + catch (Exception e) + { + throw new RuntimeException("UserTransaction not found", e); + } + this.ut = ut; + } + } + } + return ut; + } + + + /** + * This method will try to find the current {@link UserTransaction}, by default it will + * simply wraps a {@link TransactionManager} + * @return the current {@link UserTransaction} + * @throws Exception if an error occurs while looking for the {@link UserTransaction} + */ + protected UserTransaction findUserTransaction() throws Exception + { + return new UserTransactionWrapper(getTransactionManager()); + } + + /** + * {@inheritDoc} + */ + public void setTransactionTimeout(int seconds) throws SystemException + { + TransactionManager tm = getTransactionManager(); + tm.setTransactionTimeout(seconds); + } + + /** + * This class is used to enforce the {@link Transaction} timeout when a new transaction is + * created through the nested {@link TransactionManager} + * + * Created by The eXo Platform SAS + * Author : Nicolas Filotto + * nicolas.filotto@exoplatform.com + * 1 f�vr. 2010 + */ + private static class TransactionManagerTxTimeoutAware implements TransactionManager + { + /** + * The nested {@link TransactionManager} + */ + private final TransactionManager tm; + + /** + * The default timeout of the {@link Transaction} + */ + private final int defaultTimeout; + + /** + * This is used to know if a timeout has already been set for the next transaction + */ + private final ThreadLocal timeoutHasBeenSet = new ThreadLocal(); + + public TransactionManagerTxTimeoutAware(TransactionManager tm, int defaultTimeout) + { + this.tm = tm; + this.defaultTimeout = defaultTimeout; + } + + /** + * {@inheritDoc} + */ + public void begin() throws NotSupportedException, SystemException + { + if (timeoutHasBeenSet.get() != null) + { + // clean the ThreadLocal + timeoutHasBeenSet.set(null); + } + else + { + try + { + // Set the default transaction timeout + tm.setTransactionTimeout(defaultTimeout); + } + catch (Exception e) + { + LOG.warn("Cannot set the transaction timeout", e); + } + } + + // Start the transaction + tm.begin(); + } + + /** + * {@inheritDoc} + */ + public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, + SecurityException, IllegalStateException, SystemException + { + tm.commit(); + } + + /** + * {@inheritDoc} + */ + public int getStatus() throws SystemException + { + return tm.getStatus(); + } + + /** + * {@inheritDoc} + */ + public Transaction getTransaction() throws SystemException + { + return tm.getTransaction(); + } + + /** + * {@inheritDoc} + */ + public void resume(final Transaction tx) throws InvalidTransactionException, IllegalStateException, + SystemException + { + tm.resume(tx); + } + + /** + * {@inheritDoc} + */ + public void rollback() throws IllegalStateException, SecurityException, SystemException + { + tm.rollback(); + } + + /** + * {@inheritDoc} + */ + public void setRollbackOnly() throws IllegalStateException, SystemException + { + tm.setRollbackOnly(); + } + + /** + * {@inheritDoc} + */ + public void setTransactionTimeout(int timeout) throws SystemException + { + tm.setTransactionTimeout(timeout); + timeoutHasBeenSet.set(true); + } + + /** + * {@inheritDoc} + */ + public Transaction suspend() throws SystemException + { + return tm.suspend(); + } + } + + /** + * This class is used to propose a default implementation of a {@link UserTransaction} + * from the {@link TransactionManager} + * @author Nicolas Filotto + * @version $Id$ + * + */ + private static class UserTransactionWrapper implements UserTransaction + { + + /** + * The {@link TransactionManager} that we will use to simulate a {@link UserTransaction} + */ + private final TransactionManager tm; + + /** + * Default Constructor + * @param tm + */ + public UserTransactionWrapper(TransactionManager tm) + { + this.tm = tm; + } + + /** + * @see javax.transaction.UserTransaction#begin() + */ + public void begin() throws NotSupportedException, SystemException + { + tm.begin(); + } + + /** + * @see javax.transaction.UserTransaction#commit() + */ + public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, + SecurityException, IllegalStateException, SystemException + { + tm.commit(); + } + + /** + * @see javax.transaction.UserTransaction#rollback() + */ + public void rollback() throws IllegalStateException, SecurityException, SystemException + { + tm.rollback(); + } + + /** + * @see javax.transaction.UserTransaction#setRollbackOnly() + */ + public void setRollbackOnly() throws IllegalStateException, SystemException + { + tm.setRollbackOnly(); + } + + /** + * @see javax.transaction.UserTransaction#getStatus() + */ + public int getStatus() throws SystemException + { + return tm.getStatus(); + } + + /** + * @see javax.transaction.UserTransaction#setTransactionTimeout(int) + */ + public void setTransactionTimeout(int timeout) throws SystemException + { + tm.setTransactionTimeout(timeout); + } + } +} diff --git a/exo.core.component.database/src/main/resources/conf/portal/jdbcexo-configuration.db.xml b/exo.core.component.database/src/main/resources/conf/portal/jdbcexo-configuration.db.xml new file mode 100644 index 00000000..b578c0ea --- /dev/null +++ b/exo.core.component.database/src/main/resources/conf/portal/jdbcexo-configuration.db.xml @@ -0,0 +1,56 @@ + + + + + + org.exoplatform.services.naming.InitialContextInitializer + + bind.datasource + addPlugin + org.exoplatform.services.naming.BindReferencePlugin + + + bind-name + jdbcexo + + + class-name + javax.sql.DataSource + + + factory + org.apache.commons.dbcp.BasicDataSourceFactory + + + ref-addresses + ref-addresses + + + + + + + + + + diff --git a/exo.core.component.database/src/main/resources/conf/portal/jdbcexo-configuration.hsql.xml b/exo.core.component.database/src/main/resources/conf/portal/jdbcexo-configuration.hsql.xml new file mode 100644 index 00000000..95e989a4 --- /dev/null +++ b/exo.core.component.database/src/main/resources/conf/portal/jdbcexo-configuration.hsql.xml @@ -0,0 +1,56 @@ + + + + + + org.exoplatform.services.naming.InitialContextInitializer + + bind.datasource + addPlugin + org.exoplatform.services.naming.BindReferencePlugin + + + bind-name + jdbexo + + + class-name + javax.sql.DataSource + + + factory + org.apache.commons.dbcp.BasicDataSourceFactory + + + ref-addresses + ref-addresses + + + + + + + + + + diff --git a/exo.core.component.database/src/main/sql/create.sql b/exo.core.component.database/src/main/sql/create.sql new file mode 100644 index 00000000..03335a6f --- /dev/null +++ b/exo.core.component.database/src/main/sql/create.sql @@ -0,0 +1,16 @@ +--table name for mysql on linux is case sensitive +create table hibernate_unique_key ( + next_hi BIGINT + ); +INSERT INTO hibernate_unique_key(next_hi) VALUES ( 100 ); +/* +create table RESOURCE_BUNDLE_DATA ( + id VARCHAR(128) NOT NULL, + name VARCHAR(128) NOT NULL, + language VARCHAR(25), + country VARCHAR(25), + variant VARCHAR(25), + resourceType VARCHAR(128) NOT NULL, + data TEXT NOT NULL, + PRIMARY KEY (id)); +*/ diff --git a/exo.core.component.database/src/test/java/org/exoplatform/services/database/DBCreatorTest.java b/exo.core.component.database/src/test/java/org/exoplatform/services/database/DBCreatorTest.java new file mode 100644 index 00000000..98a8af23 --- /dev/null +++ b/exo.core.component.database/src/test/java/org/exoplatform/services/database/DBCreatorTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import junit.framework.TestCase; + +import org.exoplatform.container.PortalContainer; +import org.exoplatform.services.database.jdbc.CreateDBSchemaPlugin; +import org.exoplatform.services.database.jdbc.DBSchemaCreator; + +import java.util.List; + +/** + * Created by The eXo Platform SAS . + * + * @author Gennady Azarenkov + * @version $Id: DBCreatorTest.java 5569 2006-05-17 12:48:47Z lautarul $ + */ +public class DBCreatorTest extends TestCase +{ + + // private StandaloneContainer container; + private DBSchemaCreator dbcreator; + + public void setUp() throws Exception + { + // >>>>> to avoid two top-level container exception + // StandaloneContainer.setConfigurationPath("src/main/java/conf/standalone/test-configuration.xml"); + // container = StandaloneContainer.getInstance(); + PortalContainer container = PortalContainer.getInstance(); + dbcreator = (DBSchemaCreator)container.getComponentInstanceOfType(DBSchemaCreator.class); + } + + public void testConf() throws Exception + { + // DBSchemaCreator dbcreator = (DBSchemaCreator) container.getComponentInstanceOfType(DBSchemaCreator.class); + List plugins = (List)dbcreator.getPlugins(); + assertFalse(plugins.isEmpty()); + + assertTrue(plugins.get(0) instanceof CreateDBSchemaPlugin); + CreateDBSchemaPlugin plugin = (CreateDBSchemaPlugin)plugins.get(0); + + assertNotNull(plugin.getDataSource()); + assertNotNull(plugin.getScript()); + } + + public void tearDown() throws Exception + { + // container.stop(); + } +} diff --git a/exo.core.component.database/src/test/java/org/exoplatform/services/database/Mock.java b/exo.core.component.database/src/test/java/org/exoplatform/services/database/Mock.java new file mode 100644 index 00000000..73694d8c --- /dev/null +++ b/exo.core.component.database/src/test/java/org/exoplatform/services/database/Mock.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import org.exoplatform.services.database.annotation.Table; +import org.exoplatform.services.database.annotation.TableField; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Calendar; +import java.util.HashMap; + +/** + * Created by The eXo Platform SAS Author : Tuan Nguyen + * tuan.nguyen@exoplatform.com Mar 16, 2007 + */ +@Table(name = "MockTable", field = { + @TableField(name = "name", type = "string", length = 500, unique = true, nullable = false), + @TableField(name = "status", type = "int", defaultValue = "0"), @TableField(name = "start", type = "date"), + @TableField(name = "pass", type = "boolean", defaultValue = "false")}) +public class Mock extends DBObject +{ + + private String name; + + private int status; + + private Calendar start = Calendar.getInstance(); + + private boolean pass = false; + + public Mock() + { + } + + public Mock(String name, int status) + { + this.name = name; + this.status = status; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public boolean isPass() + { + return pass; + } + + public void setPass(boolean pass) + { + this.pass = pass; + } + + public Calendar getStart() + { + return start; + } + + public void setStart(Calendar start) + { + this.start = start; + } + + public int getStatus() + { + return status; + } + + public void setStatus(int status) + { + this.status = status; + } + + static public class MockMapper implements DBObjectMapper + { + + public String[][] toParameters(Mock bean) throws Exception + { + java.sql.Date date = new java.sql.Date(bean.getStart().getTimeInMillis()); + return new String[][]{{"id", String.valueOf(bean.getDBObjectId())}, {"name", bean.getName()}, + {"status", String.valueOf(bean.getStatus())}, {"start", date.toString()}, + {"pass", String.valueOf(bean.isPass())}}; + } + + public void mapResultSet(ResultSet res, Mock bean) throws Exception + { + bean.setName(res.getString("name")); + bean.setPass(res.getBoolean("pass")); + Calendar calendar = Calendar.getInstance(); + res.getDate("start", calendar); + bean.setStart(calendar); + bean.setStatus(res.getInt("status")); + } + + public void mapUpdate(Mock bean, PreparedStatement statement) throws Exception + { + statement.setString(1, bean.getName()); + statement.setInt(2, bean.getStatus()); + statement.setDate(3, new java.sql.Date(bean.getStart().getTimeInMillis())); + statement.setBoolean(4, bean.isPass()); + } + } + + /** + * Query( name = "GetUserByUserName" oracle = + * "select * from user where username= N'?' and fname = $fname" + + * ".............................................................." + + * ".............................................................." , + * standard="......................................................." , ) + */ + public void getUserByName(String s) + { + HashMap values = new HashMap(); + values.put("username", s); + values.put("fname", s); + // Object[] params = { + // {"username", "value"}, + // {"username", "value"}, + // {"username", "value"} + // } + // String query = createQuery("GetUserByUserName", params) ; + } + +} diff --git a/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestDAO.java b/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestDAO.java new file mode 100644 index 00000000..cc8a8dc8 --- /dev/null +++ b/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestDAO.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import junit.framework.TestCase; + +import org.exoplatform.services.listener.ListenerService; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by The eXo Platform SAS Author : Tuan Nguyen + * tuan.nguyen@exoplatform.com Mar 27, 2007 + */ +public class TestDAO extends TestCase +{ + + public void testDummy() + { + // empty, to doesn't fail during the tests + } + + private String printQueryResult(DatabaseService service) throws Exception + { + Connection conn = service.getConnection(); + Statement statement = conn.createStatement(); + String output = "\nQuery result: \n"; + ResultSet rs = statement.executeQuery("SELECT * FROM ExoLongId"); + while (rs.next()) + { + output += rs.getString(1) + "\n" + rs.getString(2) + "\n" + rs.getString(3) + "====\n"; + } + + return output; + } + + private void queries(DatabaseService service) throws Exception + { + } +} diff --git a/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestDBCreator.java b/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestDBCreator.java new file mode 100644 index 00000000..6da60046 --- /dev/null +++ b/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestDBCreator.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import junit.framework.TestCase; + +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.configuration.ConfigurationManager; +import org.exoplatform.services.database.creator.DBConnectionInfo; +import org.exoplatform.services.database.creator.DBCreator; +import org.exoplatform.services.naming.InitialContextInitializer; + +import java.sql.Connection; +import java.util.HashMap; +import java.util.Map; + +import javax.sql.DataSource; + +/** + * @author Anatoliy Bazko + * @version $Id$ + */ +public class TestDBCreator extends TestCase +{ + + protected DBCreator dbCreator; + + private InitialContextInitializer initContext; + + private PortalContainer container; + + @Override + public void setUp() throws Exception + { + container = PortalContainer.getInstance(); + dbCreator = (DBCreator)container.getComponentInstanceOfType(DBCreator.class); + initContext = (InitialContextInitializer)container.getComponentInstanceOfType(InitialContextInitializer.class); + } + + public void testDBCreate() throws Exception + { + assertNotNull(dbCreator); + + DBConnectionInfo dbInfo = dbCreator.createDatabase("testdb"); + DBConnectionInfo dbInfo1 = dbCreator.getDBConnectionInfo("testdb"); + + Map connProps = dbInfo.getProperties(); + Map connProps1 = dbInfo1.getProperties(); + + assertEquals(connProps.get("driverClassName"), connProps1.get("driverClassName")); + assertEquals(connProps.get("username"), connProps1.get("username")); + assertEquals(connProps.get("url"), connProps1.get("url")); + assertEquals(connProps.get("password"), connProps1.get("password")); + + Map refAddr = dbInfo.getProperties(); + + initContext.getInitialContextBinder().bind("testjdbcjcr", "javax.sql.DataSource", + "org.apache.commons.dbcp.BasicDataSourceFactory", null, + refAddr); + + DataSource ds = (DataSource)initContext.getInitialContext().lookup("testjdbcjcr"); + assertNotNull(ds); + + Connection conn = ds.getConnection(); + assertNotNull(conn); + } + + public void testDBCreateWithSpecificProperties() throws Exception + { + assertNotNull(dbCreator); + + String serverUrl = "jdbc:hsqldb:file:target/temp/data/dbcreator_test"; + Map connectionProperties = new HashMap(); + connectionProperties.put("driverClassName", "org.hsqldb.jdbcDriver"); + connectionProperties.put("username", "sa"); + connectionProperties.put("password", ""); + + ConfigurationManager cm = (ConfigurationManager)container.getComponentInstanceOfType(ConfigurationManager.class); + DBCreator dbCreator = + new DBCreator(serverUrl, connectionProperties, "classpath:/dbcreator/test.sql", "sa", "", cm); + + DBConnectionInfo dbInfo = dbCreator.createDatabase("testdb"); + DBConnectionInfo dbInfo1 = dbCreator.getDBConnectionInfo("testdb"); + + Map connProps = dbInfo.getProperties(); + Map connProps1 = dbInfo1.getProperties(); + + assertEquals(connProps.get("driverClassName"), connProps1.get("driverClassName")); + assertEquals(connProps.get("username"), connProps1.get("username")); + assertEquals(connProps.get("url"), connProps1.get("url")); + assertEquals(connProps.get("password"), connProps1.get("password")); + + Map refAddr = dbInfo.getProperties(); + + initContext.getInitialContextBinder().bind("testjdbcjcr2", "javax.sql.DataSource", + "org.apache.commons.dbcp.BasicDataSourceFactory", null, + refAddr); + + DataSource ds = (DataSource)initContext.getInitialContext().lookup("testjdbcjcr2"); + assertNotNull(ds); + + Connection conn = ds.getConnection(); + assertNotNull(conn); + } + + public void testDBCreateMultiThread() throws Exception + { + DBCreateThread[] queue = new DBCreateThread[100]; + + for (int i = 0; i < queue.length; i++) + { + queue[i] = new DBCreateThread(i); + queue[i].start(); + } + + for (int i = 0; i < queue.length; i++) + { + queue[i].join(); + } + + for (int i = 0; i < queue.length; i++) + { + DataSource ds = (DataSource)initContext.getInitialContext().lookup("testjdbcjcr_" + i); + assertNotNull(ds); + + Connection conn = ds.getConnection(); + assertNotNull(conn); + } + + } + + class DBCreateThread extends Thread + { + + private final int threadNumber; + + DBCreateThread(int threadNumber) + { + this.threadNumber = threadNumber; + } + + /** + * {@inheritDoc} + */ + @Override + public void run() + { + try + { + DBConnectionInfo dbInfo = dbCreator.createDatabase("testdb_" + threadNumber); + + Map refAddr = dbInfo.getProperties(); + + initContext.getInitialContextBinder().bind("testjdbcjcr_" + threadNumber, "javax.sql.DataSource", + "org.apache.commons.dbcp.BasicDataSourceFactory", null, refAddr); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + +} diff --git a/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestDatabaseService.java b/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestDatabaseService.java new file mode 100644 index 00000000..766efbd0 --- /dev/null +++ b/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestDatabaseService.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import junit.framework.TestCase; + +import org.exoplatform.container.PortalContainer; +import org.exoplatform.services.database.impl.XAPoolTxSupportDatabaseService; +import org.exoplatform.services.database.table.ExoLongID; +import org.exoplatform.services.database.table.IDGenerator; +import org.exoplatform.services.transaction.TransactionService; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.Statement; + +import javax.transaction.UserTransaction; + +/** + * Thu, May 15, 2003 @ + * @author Tuan Nguyen + * @version $Id: TestDatabaseService.java 5332 2006-04-29 18:32:44Z geaz $ + * @since 0.0 + * @email tuan08@yahoo.com + */ +public class TestDatabaseService extends TestCase +{ + + public void testDatabaseService() throws Exception + { + PortalContainer pcontainer = PortalContainer.getInstance(); + DatabaseService service = + (DatabaseService)pcontainer.getComponentInstanceOfType(XAPoolTxSupportDatabaseService.class); + assertNotNull(service); + assertConfiguration(service); + assertDBTableManager(service); + assertIDGenerator(service); + } + + private void assertConfiguration(DatabaseService service) throws Exception + { + TransactionService txservice = service.getTransactionService(); + assertTrue(service != null); + UserTransaction utx = txservice.getUserTransaction(); + Connection conn = service.getConnection(); + Statement s = null; + utx.begin(); + try + { + s = conn.createStatement(); + s.addBatch("create table test (name varchar(25), data varchar(25))"); + s.addBatch("insert into test values('name1', 'value1')"); + s.executeBatch(); + s.close(); + /* + * Call conn.commit() will cause an exception since the connection is now + * part of a global transaction. You should call utx.commit() here + */ + conn.commit(); + utx.commit(); + } + catch (Exception ex) + { + utx.rollback(); + } + // tm.rollback() ; + service.closeConnection(conn); + conn = service.getConnection(); + s = conn.createStatement(); + ResultSet rs = s.executeQuery("select name from test"); + if (rs.next()) + { + fail("Should not have any data in the test table"); + } + } + + private void assertDBTableManager(DatabaseService service) throws Exception + { + ExoDatasource datasource = service.getDatasource(); + DBTableManager dbManager = datasource.getDBTableManager(); + assertEquals(dbManager.hasTable(Mock.class), false); + dbManager.createTable(Mock.class, true); + + assertEquals(dbManager.hasTable(Mock.class), true); + dbManager.dropTable(Mock.class); + + assertEquals(dbManager.hasTable(Mock.class), false); + } + + private void assertIDGenerator(DatabaseService service) throws Exception + { + ExoDatasource datasource = service.getDatasource(); + IDGenerator idGenerator = new IDGenerator(datasource); + + for (int i = 0; i < 10; i++) + { + idGenerator.generateLongId(ExoLongID.class); + } + + idGenerator.restartTracker(); + idGenerator.generateLongId(ExoLongID.class); + } + +} diff --git a/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestHibernateService.java b/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestHibernateService.java new file mode 100644 index 00000000..6b70fdd0 --- /dev/null +++ b/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestHibernateService.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import junit.framework.TestCase; + +import org.exoplatform.container.PortalContainer; + +/** + * Thu, May 15, 2003 @ + * @author Tuan Nguyen + * @version $Id: TestDatabaseService.java 5332 2006-04-29 18:32:44Z geaz $ + * @since 0.0 + * @email tuan08@yahoo.com + */ +public class TestHibernateService extends TestCase +{ + HibernateService hservice_; + + public TestHibernateService(String name) + { + super(name); + } + + public void setUp() throws Exception + { + PortalContainer pcontainer = PortalContainer.getInstance(); + hservice_ = (HibernateService)pcontainer.getComponentInstanceOfType(HibernateService.class); + } + + public void testDabaseService() throws Exception + { + // assertTrue("Expect hibernate service instance" , hservice_ != null) ; + assertTrue("Expect database service instance", hservice_ != null); + } + + protected String getDescription() + { + return "Test Database Service"; + } +} diff --git a/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestQueryManager.java b/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestQueryManager.java new file mode 100644 index 00000000..ee315141 --- /dev/null +++ b/exo.core.component.database/src/test/java/org/exoplatform/services/database/TestQueryManager.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.database; + +import junit.framework.TestCase; + +/** + * Created by The eXo Platform SAS Author : Nhu Dinh Thuan + * nhudinhthuan@exoplatform.com Mar 30, 2007 + */ +public class TestQueryManager extends TestCase +{ + + QueryBuilder manager_; + + public TestQueryManager(String name) + { + super(name); + } + + public void setUp() throws Exception + { + manager_ = new QueryBuilder(); + } + + public void testPaser() throws Exception + { + String template = "select name from $table where id = $id and name like '&yahoo'"; + String[][] parameters = {{"table", "student"}, {"id", "12345"}}; + String pamameterSql = manager_.mapDataToSql(template, parameters); + } + + static public class Student + { + + String name, value; + + public Student(String n, String v) + { + name = n; + value = v; + } + + public String getName() + { + return name; + } + + public void setValue(String value) + { + this.value = value; + } + } + +} diff --git a/exo.core.component.database/src/test/java/org/exoplatform/services/transaction/impl/atomikos/TransactionsEssentialsTransactionService.java b/exo.core.component.database/src/test/java/org/exoplatform/services/transaction/impl/atomikos/TransactionsEssentialsTransactionService.java new file mode 100644 index 00000000..304829a7 --- /dev/null +++ b/exo.core.component.database/src/test/java/org/exoplatform/services/transaction/impl/atomikos/TransactionsEssentialsTransactionService.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.transaction.impl.atomikos; + +import com.atomikos.icatch.jta.UserTransactionManager; + +import org.exoplatform.services.transaction.TransactionService; +import org.exoplatform.services.transaction.impl.AbstractTransactionService; +import org.exoplatform.services.transaction.impl.jboss.JBossTransactionService; + +import org.picocontainer.Startable; + +import javax.transaction.TransactionManager; +import javax.transaction.UserTransaction; + +/** + * An implementation of a {@link TransactionService} for TransactionsEssentials from Atomikos + * to be used in standalone mode + * + * @deprecated {@link JBossTransactionService} used in all cases instead + * @author Nicolas Filotto + * @version $Id$ + * + */ +@Deprecated +public class TransactionsEssentialsTransactionService extends AbstractTransactionService implements Startable +{ + + /** + * @see org.exoplatform.services.transaction.impl.AbstractTransactionService#findTransactionManager() + */ + @Override + protected TransactionManager findTransactionManager() throws Exception + { + UserTransactionManager tm = new UserTransactionManager(); + tm.init(); + return tm; + } + + /** + * @see org.exoplatform.services.transaction.impl.AbstractTransactionService#findUserTransaction() + */ + @Override + protected UserTransaction findUserTransaction() throws Exception + { + return (UserTransaction)getTransactionManager(); + } + + /** + * @see org.picocontainer.Startable#start() + */ + public void start() + { + } + + /** + * @see org.picocontainer.Startable#stop() + */ + public void stop() + { + if (isTMInitialized()) + { + TransactionManager tm = getTransactionManager(); + if (tm instanceof UserTransactionManager) + { + UserTransactionManager utm = (UserTransactionManager)tm; + utm.close(); + } + } + } +} diff --git a/exo.core.component.database/src/test/java/org/exoplatform/services/transaction/impl/jboss/JBossTransactionService.java b/exo.core.component.database/src/test/java/org/exoplatform/services/transaction/impl/jboss/JBossTransactionService.java new file mode 100644 index 00000000..f9593a4b --- /dev/null +++ b/exo.core.component.database/src/test/java/org/exoplatform/services/transaction/impl/jboss/JBossTransactionService.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.transaction.impl.jboss; + +import org.exoplatform.services.transaction.impl.AbstractTransactionService; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.transaction.TransactionManager; +import javax.transaction.UserTransaction; + +/** + * @author Julien Viet + * @version $Revision$ + */ +public class JBossTransactionService extends AbstractTransactionService +{ + + /** + * {@inheritDoc} + */ + public TransactionManager findTransactionManager() + { + try + { + return (TransactionManager)new InitialContext().lookup("java:/TransactionManager"); + } + catch (NamingException e) + { + throw new IllegalStateException(e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public UserTransaction findUserTransaction() + { + try + { + return (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction"); + } + catch (NamingException e) + { + throw new IllegalStateException(e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getDefaultTimeout() + { + try + { + MBeanServer server = (MBeanServer)MBeanServerFactory.findMBeanServer(null).iterator().next(); + return (Integer)server.getAttribute(ObjectName.getInstance("jboss:service=TransactionManager"), + "TransactionTimeout"); + } + catch (Exception e) + { + throw new IllegalStateException(e); + } + } +} diff --git a/exo.core.component.database/src/test/resources/conf/portal/test-configuration.xml b/exo.core.component.database/src/test/resources/conf/portal/test-configuration.xml new file mode 100644 index 00000000..7221e1d3 --- /dev/null +++ b/exo.core.component.database/src/test/resources/conf/portal/test-configuration.xml @@ -0,0 +1,134 @@ + + + + + + org.exoplatform.services.cache.CacheService + cache:type=CacheService + org.exoplatform.services.cache.impl.CacheServiceImpl + + + cache.config.default + The default cache configuration + + + default + + + 300 + + + 300 + + + false + + + org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache + + + + + + + + org.exoplatform.services.database.DatabaseService + org.exoplatform.services.database.impl.XAPoolTxSupportDatabaseService + support the transaction db connection pool implementation + + + connection.config + Connection configuration + + + + + + + + + + + + org.exoplatform.services.transaction.TransactionService + org.exoplatform.services.transaction.impl.atomikos.TransactionsEssentialsTransactionService + + + timeout + 5 + + + + + + org.exoplatform.services.database.HibernateService + org.exoplatform.services.database.impl.HibernateServiceImpl + + + hibernate.properties + Default Hibernate Service + + + + + + + + + + + + + + + + + + + jcr.datasource + org.exoplatform.services.database.impl.HibernateServiceImpl + + + hibernate.properties + JCR Workspace datasource + + + + + + + + + + + + + + + + + + + + classpath:/conf/standalone/test-configuration.xml + diff --git a/exo.core.component.database/src/test/resources/conf/standalone/test-configuration.xml b/exo.core.component.database/src/test/resources/conf/standalone/test-configuration.xml new file mode 100644 index 00000000..8eb12fa1 --- /dev/null +++ b/exo.core.component.database/src/test/resources/conf/standalone/test-configuration.xml @@ -0,0 +1,140 @@ + + + + + + org.exoplatform.services.log.LogConfigurationInitializer + org.exoplatform.services.log.LogConfigurationInitializer + + + logger + org.exoplatform.services.log.impl.BufferedLog4JLogger + + + configurator + org.exoplatform.services.log.impl.SimpleExoLogConfigurator + + + + + + org.exoplatform.services.database.jdbc.DBSchemaCreator + org.exoplatform.services.database.jdbc.DBSchemaCreator + + + create.dbschema + addPlugin + org.exoplatform.services.database.jdbc.CreateDBSchemaPlugin + + + data-source + jdbcjcr + + + script + + + + + + + + + + + org.exoplatform.services.database.creator.DBCreator + org.exoplatform.services.database.creator.DBCreator + + + db-connection + database connection properties + + + + + + + db-creation + database creation properties + + + + + + + + + org.exoplatform.services.naming.InitialContextInitializer + org.exoplatform.services.naming.InitialContextInitializer + + + bind.datasource + addPlugin + org.exoplatform.services.naming.BindReferencePlugin + + + bind-name + jdbcjcr + + + class-name + javax.sql.DataSource + + + factory + org.apache.commons.dbcp.BasicDataSourceFactory + + + ref-addresses + ref-addresses + + + + + + + + + + + default-properties + Default initial context properties + + + + + + + diff --git a/exo.core.component.database/src/test/resources/dbcreator/test.sql b/exo.core.component.database/src/test/resources/dbcreator/test.sql new file mode 100644 index 00000000..e69de29b diff --git a/exo.core.component.database/src/test/resources/test.policy b/exo.core.component.database/src/test/resources/test.policy new file mode 100644 index 00000000..f8801c31 --- /dev/null +++ b/exo.core.component.database/src/test/resources/test.policy @@ -0,0 +1,15 @@ +grant codeBase "@MAVEN_REPO@-"{ + permission java.security.AllPermission; +}; + +grant codeBase "@MAIN_CLASSES@-"{ + permission java.security.AllPermission; +}; + +grant codeBase "@TEST_CLASSES@-"{ +}; + + + + + diff --git a/exo.kernel.component.command/pom.xml b/exo.kernel.component.command/pom.xml new file mode 100644 index 00000000..8f1a50c9 --- /dev/null +++ b/exo.kernel.component.command/pom.xml @@ -0,0 +1,107 @@ + + + + + + 4.0.0 + + + org.exoplatform.commons-exo + commons-exo + 7.0.x-SNAPSHOT + + + exo.kernel.component.command + + Meeds:: PLF:: Kernel :: Component :: Command Service + Implementation of Command Service of Exoplatform SAS 'eXo Kernel' project. + + + 0.35 + + + + + io.meeds.kernel + exo.kernel.container + + + io.meeds.kernel + exo.kernel.commons.test + test + + + io.github.weblegacy + commons-chain-web-jakarta-servlet + + + xml-apis + xml-apis + + + + + commons-digester + commons-digester + + + commons-logging + commons-logging + + + + + + + + maven-antrun-plugin + + + prepare-test-policy + process-test-resources + + + Creating Access Policy for tests + + + + + + + + + + + + + + + + + run + + + + + + + diff --git a/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/Action.java b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/Action.java new file mode 100644 index 00000000..3adafff3 --- /dev/null +++ b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/Action.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.command.action; + +import org.apache.commons.chain.Command; + +/** + * Created by The eXo Platform SAS
+ * + * Action interface extends Command interface. + * + * @author Gennady Azarenkov + * @LevelAPI Platform + */ + +public interface Action extends Command +{ + +} diff --git a/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/ActionCatalog.java b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/ActionCatalog.java new file mode 100644 index 00000000..720d7aa1 --- /dev/null +++ b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/ActionCatalog.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.command.action; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Created by The eXo Platform SAS. + * + * @author Gennady Azarenkov + * @version $Id: $ + */ + +public class ActionCatalog +{ + + private Map commands; + + public ActionCatalog() + { + this.commands = new HashMap(); + } + + public Set getAllActions() + { + return new HashSet(commands.values()); + } + + public Map getAllEntries() + { + return commands; + } + + public Set getActions(Condition conditions) + { + HashSet actions = new HashSet(); + for (Map.Entry entry : commands.entrySet()) + { + if (entry.getKey().match(conditions)) + actions.add(entry.getValue()); + } + return actions; + } + + public Action getAction(Condition conditions, int index) + { + Iterator actions = getActions(conditions).iterator(); + for (int i = 0; actions.hasNext(); i++) + { + Action c = actions.next(); + if (i == index) + return c; + } + return null; + } + + public void addAction(ActionMatcher matcher, Action action) + { + commands.put(matcher, action); + } + + public void clear() + { + commands.clear(); + } +} diff --git a/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/ActionMatcher.java b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/ActionMatcher.java new file mode 100644 index 00000000..064c9b4e --- /dev/null +++ b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/ActionMatcher.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.command.action; + +/** + * Created by The eXo Platform SAS. + * + * @author Gennady Azarenkov + * @version $Id: $ + */ + +public interface ActionMatcher +{ + boolean match(Condition conditions); +} diff --git a/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/ActionService.java b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/ActionService.java new file mode 100644 index 00000000..3e6ecf0f --- /dev/null +++ b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/ActionService.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.command.action; + +import java.util.HashMap; + +/** + * Created by The eXo Platform SAS. + * + * @author Gennady Azarenkov + * @version $Id: $ + */ + +public class ActionService extends HashMap +{ + + // public void addPlugin(ComponentPlugin plugin) { + // if (plugin instanceof AddActionCatalogPlugin) { + // AddActionCatalogPlugin cplugin = (AddActionCatalogPlugin) plugin; + // CatalogConfiguration conf = cplugin.getCatalogConfiguration(); + // ActionCatalog catalog = new ActionCatalog(); + // for(ActionConfiguration ac:(List )conf.getActions()) { + // try { + // ActionMatcher matcher = + // (ActionMatcher)Class.forName(ac.getMatcherFQCN()).newInstance(); + // Action action = (Action)Class.forName(ac.getActionFQCN()).newInstance(); + // catalog.addAction(matcher, action); + // } catch (Exception e) { + // e.printStackTrace(); + // } + // } + // put(conf.getName(), catalog); + // } + // } + +} diff --git a/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/Condition.java b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/Condition.java new file mode 100644 index 00000000..38f6f003 --- /dev/null +++ b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/Condition.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.command.action; + +import java.util.HashMap; + +/** + * Created by The eXo Platform SAS. + * + * @author Gennady Azarenkov + * @version $Id: $ + */ + +public class Condition extends HashMap +{ + +} diff --git a/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/package-info.java b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/package-info.java new file mode 100644 index 00000000..0941b965 --- /dev/null +++ b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/action/package-info.java @@ -0,0 +1,4 @@ +/** + * This package provides classes to manage {@link org.exoplatform.services.command.action.Action} + */ +package org.exoplatform.services.command.action; \ No newline at end of file diff --git a/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/impl/CommandLineParser.java b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/impl/CommandLineParser.java new file mode 100644 index 00000000..929a761b --- /dev/null +++ b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/impl/CommandLineParser.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.command.impl; + +import org.apache.commons.chain.Context; + +/** + * Created by The eXo Platform SAS. + * + * @author Gennady Azarenkov + * @version $Id: $ + */ +public interface CommandLineParser +{ + + /** + * parses command line and puts some parameters (if any) to the Context by + * some rules + * + * @param commandLine + * @param context + * @return command name + */ + String parse(String commandLine, Context context); +} diff --git a/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/impl/CommandService.java b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/impl/CommandService.java new file mode 100644 index 00000000..5bfe05eb --- /dev/null +++ b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/impl/CommandService.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.command.impl; + +import org.apache.commons.chain.Catalog; +import org.apache.commons.chain.CatalogFactory; +import org.apache.commons.chain.config.ConfigParser; +import org.apache.commons.chain.impl.CatalogFactoryBase; +import org.apache.commons.digester.Digester; +import org.exoplatform.container.component.ComponentPlugin; +import org.exoplatform.container.spi.DefinitionByType; +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; + +/** + * Created by The eXo Platform SAS. + * + * @author Gennady + * Azarenkov + * @version $Id: CommandService.java 12832 2007-02-15 12:41:32Z geaz $ + */ +@DefinitionByType +public class CommandService +{ + + // protected Catalog catalog; + + protected CatalogFactory catalogFactory; + + protected Digester digester; + + public CommandService() + { + this.catalogFactory = CatalogFactoryBase.getInstance(); + + final ConfigParser parser = new ConfigParser(); + this.digester = parser.getDigester(); + } + + public void addPlugin(ComponentPlugin plugin) + { + // no needs to do anything as CatalogFactory is initialized in plugin + + // if (plugin instanceof CommonsXMLConfigurationPlugin) { + // CommonsXMLConfigurationPlugin cplugin = (CommonsXMLConfigurationPlugin) + // plugin; + // can just reinitialize it every time as have single instance + // catalog = cplugin.getCatalog(); + // Iterator names = cplugin.getCatalogNames(); + // while(names.hasNext()) { + // String name = (String)names.next(); + // catalogs.put(name, cplugin.getCatalog(name)); + // } + // } + } + + /** + * puts catalog (add or update) using XML input stream + * + * @param xml + * @throws IOException + * @throws SAXException + */ + public void putCatalog(final InputStream xml) throws IOException, SAXException + { + digester.clear(); + digester.parse(xml); + } + + /** + * @return default catalog + */ + public Catalog getCatalog() + { + Catalog catalog = catalogFactory.getCatalog(); + return catalog; + } + + /** + * @param name + * @return named catalog + */ + public Catalog getCatalog(String name) + { + Catalog catalog = catalogFactory.getCatalog(name); + return catalog; + + } + + /** + * @return iterator of catalog names. default catalog is not listed here! + */ + public Iterator getCatalogNames() + { + return catalogFactory.getNames(); + } + +} diff --git a/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/impl/CommonsXMLConfigurationPlugin.java b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/impl/CommonsXMLConfigurationPlugin.java new file mode 100644 index 00000000..defb60e7 --- /dev/null +++ b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/impl/CommonsXMLConfigurationPlugin.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.command.impl; + +import org.apache.commons.chain.config.ConfigParser; +import org.exoplatform.container.component.BaseComponentPlugin; +import org.exoplatform.container.configuration.ConfigurationManager; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.container.xml.ValueParam; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; + +import java.net.URL; + +/** + * Created by The eXo Platform SAS.
The plugin for configuring + * command/chain catalog using "native" Apache Commons Chain's XML file + * + * @author Gennady + * Azarenkov + * @version $Id: CommonsXMLConfigurationPlugin.java 9846 2006-10-27 11:03:37Z + * geaz $ + */ + +public class CommonsXMLConfigurationPlugin extends BaseComponentPlugin +{ + + // protected Catalog defaultCatalog; + + private static final Log LOG = ExoLogger.getLogger("exo.kernel.component.cache.CommonsXMLConfigurationPlugin"); + + public CommonsXMLConfigurationPlugin(InitParams params, ConfigurationManager configurationManager) throws Exception + { + ValueParam confFile = params.getValueParam("config-file"); + if (confFile != null) + { + final String path = confFile.getValue(); + final ConfigParser parser = new ConfigParser(); + // may work for StandaloneContainer + + URL res = Thread.currentThread().getContextClassLoader().getResource(path); + + // for PortalContainer + if (res == null) + res = configurationManager.getResource(path); + if (res == null) + throw new Exception("Resource not found " + path); + LOG.info("Catalog configuration found at " + res); + + final URL fRes = res; + parser.parse(fRes); + } + + } +} diff --git a/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/impl/SimpleCommandLineParser.java b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/impl/SimpleCommandLineParser.java new file mode 100644 index 00000000..59bfe5d8 --- /dev/null +++ b/exo.kernel.component.command/src/main/java/org/exoplatform/services/command/impl/SimpleCommandLineParser.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.command.impl; + +import org.apache.commons.chain.Context; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +/** + * Created by The eXo Platform SAS. + * + * @author Gennady Azarenkov + * @version $Id: $ + */ + +public class SimpleCommandLineParser implements CommandLineParser +{ + + protected final String parametersPropertyName; + + public SimpleCommandLineParser(String parametersPropertyName) + { + this.parametersPropertyName = parametersPropertyName; + } + + /* + * (non-Javadoc) + * @see + * org.exoplatform.services.command.impl.CommandLineParser#parse(java.lang + * .String, org.apache.commons.chain.Context) + */ + public String parse(String commandLine, Context context) + { + + context.remove(parametersPropertyName); + + // the rules: + // first word is command name (should be returned) + // else are parameters of command (should be put into Context under name == + // parametersPropertyName as array of Strings) + // mind contained string parameters - should be quoted (" or ') + // ///////////////////// + + StringTokenizer parser = new StringTokenizer(commandLine); + String commandName = null; + List params = new ArrayList(); + + while (parser.hasMoreTokens()) + { + String str = parser.nextToken(); + if (commandName == null) + commandName = str; + else + params.add(str); + } + // ////////////////////// + context.put(parametersPropertyName, params); + return commandName; + } + + /** + * @return parameters Property Name + */ + public String getParametersPropertyName() + { + return parametersPropertyName; + } + +} diff --git a/exo.kernel.component.command/src/test/java/org/exoplatform/services/command/CommandServiceTest.java b/exo.kernel.component.command/src/test/java/org/exoplatform/services/command/CommandServiceTest.java new file mode 100644 index 00000000..e2c09318 --- /dev/null +++ b/exo.kernel.component.command/src/test/java/org/exoplatform/services/command/CommandServiceTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.command; + +import junit.framework.TestCase; + +import org.apache.commons.chain.Catalog; +import org.apache.commons.chain.Command; +import org.apache.commons.chain.Context; +import org.apache.commons.chain.impl.CatalogFactoryBase; +import org.apache.commons.chain.impl.ContextBase; +import org.exoplatform.container.StandaloneContainer; +import org.exoplatform.services.command.impl.CommandService; + +import java.io.ByteArrayInputStream; + +/** + * Created by The eXo Platform SAS . + * + * @author Gennady Azarenkov + * @version $Id: CommandServiceTest.java 9296 2006-10-04 13:13:29Z geaz $ + */ +public class CommandServiceTest extends TestCase +{ + + private static final String IS = "" + "" + ""; + + private StandaloneContainer container; + + public void setUp() throws Exception + { + StandaloneContainer.setConfigurationPath("src/test/resources/conf/standalone/test-configuration.xml"); + + container = StandaloneContainer.getInstance(); + } + + public void testPluginConf() throws Exception + { + + CommandService cservice = (CommandService)container.getComponentInstanceOfType(CommandService.class); + assertNotNull(cservice); + + // preconfigured commands + assertTrue(cservice.getCatalog().getNames().hasNext()); + assertNotNull(cservice.getCatalog().getNames().next()); + + } + + public void testStringConf() throws Exception + { + CommandService cservice = (CommandService)container.getComponentInstanceOfType(CommandService.class); + Catalog c = cservice.getCatalog(); + + assertNull(c.getCommand("StrCommand")); + cservice.putCatalog(new ByteArrayInputStream(IS.getBytes())); + Catalog c1 = cservice.getCatalog(); + assertNotNull(c1.getCommand("StrCommand")); + + } + + public void testInitWithFile() throws Exception + { + CommandService cservice = (CommandService)container.getComponentInstanceOfType(CommandService.class); + cservice.putCatalog(getClass().getResourceAsStream("/conf/test-commands3.xml")); + assertTrue(cservice.getCatalogNames().hasNext()); + Catalog c1 = cservice.getCatalog("catalog1"); + assertNotNull(c1.getCommand("Command2")); + + } + + public void testExcecute() throws Exception + { + + CommandService cservice = (CommandService)container.getComponentInstanceOfType(CommandService.class); + Command c1 = cservice.getCatalog().getCommand("Execute2"); + Command c2 = cservice.getCatalog().getCommand("Command1"); + + Catalog c = cservice.getCatalog(); + + Context ctx = new ContextBase(); + ctx.put("test", Integer.valueOf(0)); + c1.execute(ctx); + c2.execute(ctx); + assertEquals(3, ((Integer)ctx.get("test")).intValue()); + + } + + public void tearDown() { + CatalogFactoryBase.clear(); + container.stop(); + + } + +} diff --git a/exo.kernel.component.command/src/test/java/org/exoplatform/services/command/MultiConfigServiceTest.java b/exo.kernel.component.command/src/test/java/org/exoplatform/services/command/MultiConfigServiceTest.java new file mode 100644 index 00000000..10f865be --- /dev/null +++ b/exo.kernel.component.command/src/test/java/org/exoplatform/services/command/MultiConfigServiceTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.command; + +import junit.framework.TestCase; + +import org.apache.commons.chain.impl.CatalogFactoryBase; +import org.exoplatform.container.ExoContainer; +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.RootContainer; +import org.exoplatform.container.StandaloneContainer; +import org.exoplatform.services.command.impl.CommandService; + +import java.io.ByteArrayInputStream; +import java.util.Iterator; + +/** + * Created by The eXo Platform SAS . + * + * @author Gennady Azarenkov + * @version $Id:$ + */ +public class MultiConfigServiceTest extends TestCase +{ + + private StandaloneContainer container; + + private static final String IS = + "" + "" + ""; + + // amount of commands configured in multiple plugins + // CHECK it if change test-multi-configuration.xml !!! + private final int NUMBER_OF_COMMANDS_IN_DEF = 4; + + private final int NUMBER_OF_COMMANDS_IN_CATALOG1 = 2; + + public void setUp() throws Exception + { + StandaloneContainer.setConfigurationPath("src/test/resources/conf/standalone/test-multi-configuration.xml"); + + container = StandaloneContainer.getInstance(); + } + + /** + * Tests if multiple configuration is allowed + * + * @throws Exception + */ + public void testMultiConfig() throws Exception + { + + CommandService cservice = (CommandService)container.getComponentInstanceOfType(CommandService.class); + assertNotNull(cservice); + + assertTrue(cservice.getCatalogNames().hasNext()); + + Iterator commands = cservice.getCatalog().getNames(); + int cnt = 0; + while (commands.hasNext()) + { + commands.next(); + cnt++; + } + + assertEquals(NUMBER_OF_COMMANDS_IN_DEF, cnt); + + commands = cservice.getCatalog("catalog1").getNames(); + cnt = 0; + while (commands.hasNext()) + { + commands.next(); + cnt++; + } + + assertEquals(NUMBER_OF_COMMANDS_IN_CATALOG1, cnt); + + } + + public void testIfPutCatalogDoesNotRemoveCommands() throws Exception + { + + CommandService cservice = (CommandService)container.getComponentInstanceOfType(CommandService.class); + assertNotNull(cservice); + + assertTrue(cservice.getCatalogNames().hasNext()); + Iterator commands = cservice.getCatalog().getNames(); + int cnt = 0; + while (commands.hasNext()) + { + commands.next(); + cnt++; + } + + ByteArrayInputStream is = new ByteArrayInputStream(IS.getBytes()); + + cservice.putCatalog(is); + + assertTrue(cservice.getCatalogNames().hasNext()); + commands = cservice.getCatalog().getNames(); + cnt = 0; + while (commands.hasNext()) + { + commands.next(); + cnt++; + } + + commands = cservice.getCatalog("fromput").getNames(); + cnt = 0; + while (commands.hasNext()) + { + commands.next(); + cnt++; + } + + } + + public void tearDown() { + CatalogFactoryBase.clear(); + container.stop(); + + } +} diff --git a/exo.kernel.component.command/src/test/java/org/exoplatform/services/command/TestCommand1.java b/exo.kernel.component.command/src/test/java/org/exoplatform/services/command/TestCommand1.java new file mode 100644 index 00000000..0613a380 --- /dev/null +++ b/exo.kernel.component.command/src/test/java/org/exoplatform/services/command/TestCommand1.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.command; + +import org.apache.commons.chain.Command; +import org.apache.commons.chain.Context; + +/** + * Created by The eXo Platform SAS. + * + * @author Gennady + * Azarenkov + * @version $Id: TestCommand1.java 5799 2006-05-28 17:55:42Z geaz $ + */ + +public class TestCommand1 implements Command +{ + + public boolean execute(Context ctx) throws Exception + { + int tval = ((Integer)ctx.get("test")).intValue() + 1; + ctx.put("test", Integer.valueOf(tval)); + return false; + } + +} diff --git a/exo.kernel.component.command/src/test/resources/conf/standalone/test-configuration.xml b/exo.kernel.component.command/src/test/resources/conf/standalone/test-configuration.xml new file mode 100644 index 00000000..628a0cfd --- /dev/null +++ b/exo.kernel.component.command/src/test/resources/conf/standalone/test-configuration.xml @@ -0,0 +1,46 @@ + + + + + + org.exoplatform.services.command.impl.CommandService + org.exoplatform.services.command.impl.CommandService + + + config.catalog + addPlugin + org.exoplatform.services.command.impl.CommonsXMLConfigurationPlugin + + + config-file + conf/test-commands.xml + + + + + + + + org.exoplatform.services.command.action.ActionService + + + diff --git a/exo.kernel.component.command/src/test/resources/conf/standalone/test-multi-configuration.xml b/exo.kernel.component.command/src/test/resources/conf/standalone/test-multi-configuration.xml new file mode 100644 index 00000000..99a3cb54 --- /dev/null +++ b/exo.kernel.component.command/src/test/resources/conf/standalone/test-multi-configuration.xml @@ -0,0 +1,64 @@ + + + + + + org.exoplatform.services.command.impl.CommandService + org.exoplatform.services.command.impl.CommandService + + + config.catalog + addPlugin + org.exoplatform.services.command.impl.CommonsXMLConfigurationPlugin + + + config-file + conf/test-commands.xml + + + + + config.catalog + addPlugin + org.exoplatform.services.command.impl.CommonsXMLConfigurationPlugin + + + config-file + conf/test-commands2.xml + + + + + config.catalog + addPlugin + org.exoplatform.services.command.impl.CommonsXMLConfigurationPlugin + + + config-file + conf/test-commands3.xml + + + + + + + diff --git a/exo.kernel.component.command/src/test/resources/conf/test-commands.xml b/exo.kernel.component.command/src/test/resources/conf/test-commands.xml new file mode 100644 index 00000000..9c6dfc72 --- /dev/null +++ b/exo.kernel.component.command/src/test/resources/conf/test-commands.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + diff --git a/exo.kernel.component.command/src/test/resources/conf/test-commands2.xml b/exo.kernel.component.command/src/test/resources/conf/test-commands2.xml new file mode 100644 index 00000000..fa5fc842 --- /dev/null +++ b/exo.kernel.component.command/src/test/resources/conf/test-commands2.xml @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/exo.kernel.component.command/src/test/resources/conf/test-commands3.xml b/exo.kernel.component.command/src/test/resources/conf/test-commands3.xml new file mode 100644 index 00000000..4d94991f --- /dev/null +++ b/exo.kernel.component.command/src/test/resources/conf/test-commands3.xml @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/exo.kernel.component.command/src/test/resources/test.policy b/exo.kernel.component.command/src/test/resources/test.policy new file mode 100644 index 00000000..4eb23700 --- /dev/null +++ b/exo.kernel.component.command/src/test/resources/test.policy @@ -0,0 +1,27 @@ +grant codeBase "@MAVEN_REPO@-"{ + permission java.security.AllPermission; +}; + +grant codeBase "@MAIN_CLASSES@-"{ + permission java.security.AllPermission; +}; + +grant codeBase "@TEST_CLASSES@-"{ + permission java.lang.RuntimePermission "manageContainer"; +}; + +grant codeBase "@MAIN_CLASSES@../../../exo.kernel.commons.test/-"{ + permission java.security.AllPermission; +}; + +grant codeBase "@MAIN_CLASSES@../../../exo.kernel.commons/-"{ + permission java.security.AllPermission; +}; + +grant codeBase "@MAIN_CLASSES@../../../exo.kernel.container/-"{ + permission java.security.AllPermission; +}; + + + + diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/pom.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/pom.xml new file mode 100644 index 00000000..d53f37b0 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/pom.xml @@ -0,0 +1,153 @@ + + + + + 4.0.0 + + org.exoplatform.commons-exo + commons-exo + 7.0.x-SNAPSHOT + + exo.kernel.component.ext.cache.impl.infinispan.v8 + Meeds:: PLF:: Kernel :: Cache Extension :: Infinispan Implementation + Infinispan Implementation of Cache Service for Exoplatform SAS 'eXo Kernel' project. + + 0.6 + + + + io.meeds.kernel + exo.kernel.component.cache + + + io.meeds.kernel + exo.kernel.component.common + + + ${project.groupId} + exo.core.component.database + + + org.infinispan + infinispan-core + + + org.jboss.spec.javax.transaction + jboss-transaction-api_1.1_spec + + + org.jboss.logging + jboss-logging + + + org.jgroups + jgroups + + + + + org.jboss.logging + jboss-logging + + + org.jboss.jbossts + jbossjta + + + org.jgroups + jgroups + + + io.meeds.kernel + exo.kernel.container + + + io.meeds.kernel + exo.kernel.commons.test + test + + + junit + junit + test + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + @{argLine} @{surefire.argLine} -Djava.net.preferIPv4Stack=true + + + + jgroups.bind_addr + 127.0.0.1 + + + jgroups.stack + udp + + + + + + maven-antrun-plugin + + + prepare-test-policy + process-test-resources + + + Creating Access Policy for tests + + + + + + + + + + + + + + + + + run + + + + + + ant + ant-optional + 1.5.3-1 + + + + + + diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/container/util/TemplateConfigurationHelper.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/container/util/TemplateConfigurationHelper.java new file mode 100644 index 00000000..223b0aac --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/container/util/TemplateConfigurationHelper.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.container.util; + +import org.exoplatform.container.configuration.ConfigurationManager; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; + +/** + * Builds configuration from template using map of template-variables -- value. + * Class provides extra functionality for filtering parameters by pattern, excluding + * unnecessary parameters. + * + * @author Nikolay Zamosenchuk + * @version $Id: TemplateConfigurationHelper.java 34360 2009-07-22 23:58:59Z nzamosenchuk $ + * + */ +public class TemplateConfigurationHelper +{ + + private static final Log LOG = ExoLogger.getLogger("exo.kernel.container.TemplateConfigurationHelper"); + + // list with include-patterns + private List includes = new ArrayList(); + + // list with exclude-patterns + private List excludes = new ArrayList(); + + private ConfigurationManager cfm; + + /** + * Creates instance of template configuration helper with given lists of filtering + * patterns. Parameter will be included only if it matches any include-pattern and + * doesn't match any exclude-pattern. I.e. You can include "extended-*" and exclude + * "extended-type". Please refer to Java regexp documentation. Filtering for this + * example, should be defined as following: + * include: "^extended-.*" + * exclude: "^extended-type" + * + * @param includes Array with string representation of include reg-exp patterns + * @param excludes Array with string representation of exclude reg-exp patterns + * @param cfm instance for looking up resources + */ + public TemplateConfigurationHelper(String[] includes, String[] excludes, ConfigurationManager cfm) + { + super(); + this.cfm = cfm; + // compile include patterns + for (String regex : includes) + { + this.includes.add(Pattern.compile(regex)); + } + // compile exclude patterns + for (String regex : excludes) + { + this.excludes.add(Pattern.compile(regex)); + } + } + + /** + * Reads configuration file from a stream and replaces all the occurrences of template-variables + * (like : "${parameter.name}") with values provided in the map. + * + * @param inputStream + * @param parameters + * @return + * @throws IOException + */ + public InputStream fillTemplate(InputStream inputStream, Map parameters) throws IOException + { + if (inputStream == null || parameters == null || parameters.size() == 0) + { + return inputStream; + } + // parameters filtering + Map preparedParams = prepareParameters(parameters); + // read stream + String configuration = Utils.readStream(inputStream); + for (Entry entry : preparedParams.entrySet()) + { + configuration = configuration.replace(entry.getKey(), entry.getValue()); + } + // create new stream + InputStream configurationStream = new ByteArrayInputStream(configuration.getBytes()); + return configurationStream; + } + + /** + * Reads configuration file from a stream and replaces all the occurrences of template-variables + * (like : "${parameter.name}") with values provided in the map. + * + * @param filename + * @param parameters + * @return + * @throws IOException + */ + public InputStream fillTemplate(String filename, Map parameters) throws IOException + { + InputStream inputStream = getInputStream(cfm, filename); + // inputStream still remains null, so file was not opened + if (inputStream == null) + { + throw new IOException("Can't find or open file:" + filename); + } + return fillTemplate(inputStream, parameters); + } + + /** + * Tries first to get the file content using the configuration manager, if it cannot + * be found it will then try to get it from the context class loader of the current thread, + * if it cannot be found it will try to get it from the class loader of the current class and + * finally it still cannot be found it will try to use the file name as a file path. + * @param cfm the configuration manager from which we want to try to find the file content + * @param filename the name of the file to found + * @return the {@link InputStream} corresponding to the file content if it can be found + * null otherwise + */ + public static InputStream getInputStream(ConfigurationManager cfm, String filename) + { + InputStream inputStream = null; + // try to get using configuration manager + try + { + inputStream = cfm.getInputStream(filename); + } + catch (Exception e) + { + if (LOG.isTraceEnabled()) + { + LOG.trace("An exception occurred: " + e.getMessage()); + } + } + + // try to get resource by class loader + if (inputStream == null) + { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + inputStream = cl == null ? null : cl.getResourceAsStream(filename); + } + + // check system class loader + if (inputStream == null) + { + inputStream = TemplateConfigurationHelper.class.getClassLoader().getResourceAsStream(filename); + } + + // try to get as file stream + if (inputStream == null) + { + try + { + inputStream = new FileInputStream(filename); + } + catch (IOException e) + { + if (LOG.isTraceEnabled()) + { + LOG.trace("An exception occurred: " + e.getMessage()); + } + } + } + return inputStream; + } + + /** + * Checks if String mathes to any pattern from the list + * + * @param patterns + * @param parameter + * @return + */ + private boolean matches(List patterns, String parameter) + { + for (Pattern pattern : patterns) + { + if (pattern.matcher(parameter).matches()) + { + // string matched + return true; + } + } + return false; + } + + /** + * Filters the map of parameters, leaving only those than matches filtering regular expressions. + * Also adds "${}" to the parameter key:
+ * I.e. such map provided on input: + * + * "foo-cache.loader":"org.exoplatform" + * "foo-configuration":"/conf/test.xml" + * "max-volatile-size":"100Kb" + * + * the output will be like: + * + * "${foo-cache.loader}":"org.exoplatform" + * + * Other will be ignored (depending on includes/excludes lists provided in constructor). + * + * @param parameters + * @return + */ + protected Map prepareParameters(Map parameters) + { + Map map = new HashMap(); + for (Entry entry : parameters.entrySet()) + { + if (matches(includes, entry.getKey()) && !matches(excludes, entry.getKey())) + { + map.put("${" + entry.getKey() + "}", entry.getValue()); + } + } + return map; + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/AbstractExoCache.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/AbstractExoCache.java new file mode 100644 index 00000000..25b2d5c7 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/AbstractExoCache.java @@ -0,0 +1,752 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.cache.impl.infinispan; + +import org.exoplatform.services.cache.CacheInfo; +import org.exoplatform.services.cache.CacheListener; +import org.exoplatform.services.cache.CacheListenerContext; +import org.exoplatform.services.cache.CacheMode; +import org.exoplatform.services.cache.CachedObjectSelector; +import org.exoplatform.services.cache.ExoCache; +import org.exoplatform.services.cache.ExoCacheConfig; +import org.exoplatform.services.cache.ObjectCacheInfo; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.infinispan.AdvancedCache; +import org.infinispan.Cache; +import org.infinispan.container.entries.InternalCacheEntry; +import org.infinispan.context.Flag; +import org.infinispan.notifications.Listener; +import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted; +import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated; +import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified; +import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved; +import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent; +import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent; +import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent; +import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * An {@link org.exoplatform.services.cache.ExoCache} implementation based on {@link Cache}. + * + * @author Nicolas Filotto + * @version $Id$ + * + */ +public abstract class AbstractExoCache implements ExoCache +{ + + /** + * Logger. + */ + private static final Log LOG = ExoLogger.getLogger(AbstractExoCache.class); + + private final AtomicInteger hits = new AtomicInteger(0); + + private final AtomicInteger misses = new AtomicInteger(0); + + private String label; + + private String name; + + private boolean distributed; + + private boolean replicated; + + private boolean asynchronous; + + private boolean logEnabled; + + private final CopyOnWriteArrayList> listeners; + + protected final AdvancedCache cache; + + public AbstractExoCache(ExoCacheConfig config, Cache cache) + { + this.cache = cache.getAdvancedCache(); + this.listeners = new CopyOnWriteArrayList>(); + setDistributed(config.isDistributed()); + setLabel(config.getLabel()); + setName(config.getName()); + setLogEnabled(config.isLogEnabled()); + setReplicated(config.isRepicated()); + CacheMode cacheMode = config.getCacheMode(); + setAsynchronous(cacheMode != null && !cacheMode.isSync()); + cache.addListener(new CacheEventListener()); + } + + /** + * {@inheritDoc} + */ + public void addCacheListener(CacheListener listener) + { + if (listener == null) + { + throw new IllegalArgumentException("The listener cannot be null"); + } + listeners.add(new ListenerContext(listener, this)); + } + + /** + * {@inheritDoc} + */ + public void clearCache() + { + cache.withFlags(Flag.CACHE_MODE_LOCAL).clear(); + onClearCache(); + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("unchecked") + public V get(Serializable name) + { + if (name == null) + { + return null; + } + final V result = cache.get(name); + if (result == null) + { + misses.incrementAndGet(); + } + else + { + hits.incrementAndGet(); + } + onGet((K)name, result); + return result; + } + + /** + * {@inheritDoc} + */ + public int getCacheHit() + { + return hits.get(); + } + + /** + * {@inheritDoc} + */ + public int getCacheMiss() + { + return misses.get(); + } + + /** + * {@inheritDoc} + */ + public int getCacheSize() + { + return cache.withFlags(Flag.CACHE_MODE_LOCAL).size(); + } + + /** + * {@inheritDoc} + */ + public List getCachedObjects() + { + Collection values = cache.withFlags(Flag.CACHE_MODE_LOCAL).values(); + if (values == null || values.isEmpty()) + { + return Collections.emptyList(); + } + else + { + return new ArrayList(values); + } + } + + /** + * {@inheritDoc} + */ + public String getLabel() + { + return label; + } + + /** + * {@inheritDoc} + */ + public String getName() + { + return name; + } + + /** + * {@inheritDoc} + */ + public boolean isDistributed() + { + return distributed; + } + + /** + * {@inheritDoc} + */ + public boolean isLogEnabled() + { + return logEnabled; + } + + /** + * {@inheritDoc} + */ + public boolean isReplicated() + { + return replicated; + } + + public void setAsynchronous(boolean asynchronous) { + this.asynchronous = asynchronous; + } + + public boolean isAsynchronous() { + return asynchronous; + } + + private void putOnlyAsync(K key, V value) + { + cache.withFlags(Flag.SKIP_REMOTE_LOOKUP, Flag.IGNORE_RETURN_VALUES, Flag.FORCE_ASYNCHRONOUS).putAsync(key, value); + } + + + /** + * {@inheritDoc} + */ + public void put(final K key, final V value) throws IllegalArgumentException + { + if (key == null) + { + throw new IllegalArgumentException("No null cache key accepted"); + } + else if (value == null) + { + // ignore null values + return; + } + if(LOG.isDebugEnabled() && cache.getDataContainer().containsKey(key)) { + InternalCacheEntry internalCacheEntry = cache.getDataContainer().get(key); + if(internalCacheEntry != null) { + Object oldValue = internalCacheEntry.getValue(); + if(oldValue != null) { + if(oldValue.equals(value)) { + LOG.debug("Need to optimize top layer cache management propably (depends on ValueClass.equals method pertinence). The same value putted into cache, cache = " + cache.getName() + ", key : class= " + key.getClass() + ", hashcode= " + key.hashCode() + "/ old hashcode: " + internalCacheEntry.getKey().hashCode()); + } else { + try { + value.getClass().getDeclaredMethod("equals"); + } catch (NoSuchMethodException e) { + LOG.debug("Need to implement equals method in " + value.getClass().getCanonicalName() + ". cache = " + cache.getName() + ", key : class= " + key.getClass() + ", hashcode= " + key.hashCode() + "/ old hashcode: " + internalCacheEntry.getKey().hashCode()); + } + } + } + } + } + putOnly(key, value, false); + onPut(key, value); + } + + @Override + public void putLocal(final K key, final V value) throws IllegalArgumentException { + if (key == null) + { + throw new IllegalArgumentException("No null cache key accepted"); + } + else if (value == null) + { + // ignore null values + return; + } + + putOnly(key, value, true); + + onPutLocal(key, value); + } + + /** + * Only puts the data into the cache nothing more + */ + protected void putOnly(K key, V value, boolean isLocal) + { + if (isLocal) + { + if(isAsynchronous()) { + cache.withFlags(Flag.CACHE_MODE_LOCAL, Flag.SKIP_REMOTE_LOOKUP, Flag.IGNORE_RETURN_VALUES, Flag.FORCE_ASYNCHRONOUS).putAsync(key, value); + } else { + cache.withFlags(Flag.CACHE_MODE_LOCAL, Flag.SKIP_REMOTE_LOOKUP, Flag.IGNORE_RETURN_VALUES).put(key, value); + } + } + else + { + if(isAsynchronous()) { + cache.withFlags(Flag.SKIP_REMOTE_LOOKUP, Flag.IGNORE_RETURN_VALUES, Flag.FORCE_ASYNCHRONOUS).putAsync(key, value); + } else { + cache.withFlags(Flag.SKIP_REMOTE_LOOKUP, Flag.IGNORE_RETURN_VALUES).put(key, value); + } + } + } + + /** + * {@inheritDoc} + */ + public void putMap(final Map objs) throws IllegalArgumentException + { + if (objs == null) + { + throw new IllegalArgumentException("No null map accepted"); + } + for (Serializable name : objs.keySet()) + { + if (name == null) + { + throw new IllegalArgumentException("No null cache key accepted"); + } + } + // Start transaction + if(cache.getTransactionManager() != null) { + cache.startBatch(); + } + try + { + // Make sure that the key and the value are valid + Map map = new LinkedHashMap(); + for (Map.Entry entry : objs.entrySet()) + { + map.put(entry.getKey(), entry.getValue()); + } + cache.putAll(map); + if(cache.getTransactionManager() != null) { + cache.endBatch(true); + } + // End transaction + for (Map.Entry entry : objs.entrySet()) + { + onPut(entry.getKey(), entry.getValue()); + } + } + catch (Exception e) //NOSONAR + { + if(cache.getTransactionManager() != null) { + cache.endBatch(false); + } + LOG.warn("An error occurs while executing the putMap method", e); + } + } + + /** + * {@inheritDoc} + */ + public void putAsyncMap(final Map objs) throws IllegalArgumentException + { + if (objs == null) + { + throw new IllegalArgumentException("No null map accepted"); + } + for (Serializable name : objs.keySet()) + { + if (name == null) + { + throw new IllegalArgumentException("No null cache key accepted"); + } + } + try + { + // Make sure that the key and the value are valid + Map map = new LinkedHashMap(); + for (Map.Entry entry : objs.entrySet()) + { + map.put(entry.getKey(), entry.getValue()); + } + cache.putAllAsync(map); + // End transaction + for (Map.Entry entry : objs.entrySet()) + { + onPut(entry.getKey(), entry.getValue()); + } + } + catch (Exception e) //NOSONAR + { + LOG.warn("An error occurs while executing the putMap method", e); + } + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("unchecked") + public V remove(final Serializable key) throws NullPointerException + { + if (key == null) + { + throw new IllegalArgumentException("No null cache key accepted"); + } + V result = cache.remove(key); + onRemove((K)key, result); + return result; + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("unchecked") + public void removeLocal(final Serializable key) throws NullPointerException + { + if (key == null) + { + throw new IllegalArgumentException("No null cache key accepted"); + } + cache.withFlags(Flag.CACHE_MODE_LOCAL).removeAsync(key); + onRemove((K)key, null); + } + + /** + * {@inheritDoc} + */ + public List removeCachedObjects() + { + final List list = getCachedObjects(); + clearCache(); + return list; + } + + /** + * {@inheritDoc} + */ + public void select(CachedObjectSelector selector) throws Exception + { + if (selector == null) + { + throw new IllegalArgumentException("No null selector"); + } + for (Map.Entry entry : cache.withFlags(Flag.CACHE_MODE_LOCAL).entrySet()) + { + K key = entry.getKey(); + if (key == null) + { + continue; + } + final V value = entry.getValue(); + ObjectCacheInfo info = new ObjectCacheInfo() + { + public V get() + { + return value; + } + + public long getExpireTime() + { + // Cannot know: The expire time is managed by Infinispan itself + return -1; + } + }; + if (selector.select(key, info)) + { + selector.onSelect(this, key, info); + } + } + } + + /** + * {@inheritDoc} + */ + public void setDistributed(boolean distributed) + { + this.distributed = distributed; + } + + /** + * {@inheritDoc} + */ + public void setLabel(String label) + { + this.label = label; + } + + /** + * {@inheritDoc} + */ + public void setLogEnabled(boolean logEnabled) + { + this.logEnabled = logEnabled; + } + + /** + * {@inheritDoc} + */ + public void setName(String name) + { + this.name = name; + } + + /** + * {@inheritDoc} + */ + public void setReplicated(boolean replicated) + { + this.replicated = replicated; + } + + public void onExpire(K key, V obj) + { + if (listeners.isEmpty()) + { + return; + } + for (ListenerContext context : listeners) + { + try + { + context.onExpire(key, obj); + } + catch (Exception e)//NOSONAR + { + if (LOG.isWarnEnabled()) + LOG.warn("Cannot execute the CacheListener properly", e); + } + } + } + + public void onRemove(K key, V obj) + { + if (listeners.isEmpty()) + { + return; + } + for (ListenerContext context : listeners) + { + try + { + context.onRemove(key, obj); + } + catch (Exception e)//NOSONAR + { + if (LOG.isWarnEnabled()) + LOG.warn("Cannot execute the CacheListener properly", e); + } + } + } + + public void onPut(K key, V obj) + { + if (listeners.isEmpty()) + { + return; + } + for (ListenerContext context : listeners) + try + { + context.onPut(key, obj); + } + catch (Exception e)//NOSONAR + { + if (LOG.isWarnEnabled()) + LOG.warn("Cannot execute the CacheListener properly", e); + } + } + + public void onPutLocal(K key, V value) + { + if (listeners.isEmpty()) + { + return; + } + for (ListenerContext context : listeners) + try + { + context.onPutLocal(key, value); + } + catch (Exception e)//NOSONAR + { + if (LOG.isWarnEnabled()) + LOG.warn("Cannot execute the CacheListener properly", e); + } + } + + public void onGet(K key, V obj) + { + if (listeners.isEmpty()) + { + return; + } + for (ListenerContext context : listeners) + try + { + context.onGet(key, obj); + } + catch (Exception e)//NOSONAR + { + if (LOG.isWarnEnabled()) + LOG.warn("Cannot execute the CacheListener properly", e); + } + } + + public void onClearCache() + { + if (listeners.isEmpty()) + { + return; + } + for (ListenerContext context : listeners) + try + { + context.onClearCache(); + } + catch (Exception e)//NOSONAR + { + if (LOG.isWarnEnabled()) + LOG.warn("Cannot execute the CacheListener properly", e); + } + } + + @Listener + public class CacheEventListener + { + /** + * Warning Infinispan triggers a CacheEntryEvictedEvent only at explicit eviction + * that is done lazily which is not exactly what we expect, we still use it to be + * able to use it with avoidValueReplication set to true. + */ + @CacheEntriesEvicted + public void cacheEntryEvicted(CacheEntriesEvictedEvent evt) + { + if (evt.isPre()) + { + for (Map.Entry entry : evt.getEntries().entrySet()) + { + onExpire(entry.getKey(), entry.getValue()); + } + } + } + + @CacheEntryRemoved + public void cacheEntryRemoved(CacheEntryRemovedEvent evt) + { + if (evt.isPre() && !evt.isOriginLocal()) + { + final K key = evt.getKey(); + final V value = evt.getValue(); + onRemove(key, value); + } + } + + @CacheEntryModified + public void cacheEntryModified(CacheEntryModifiedEvent evt) + { + if (!evt.isOriginLocal() && !evt.isPre()) + { + final K key = evt.getKey(); + final V value = evt.getValue(); + onPut(key, value); + } + } + + @CacheEntryCreated + public void CacheEntryCreated(CacheEntryCreatedEvent event) { + if (!event.isOriginLocal() && !event.isPre()) + { + final K key = event.getKey(); + final V value = event.getValue(); + onPut(key, value);; + } + } + } + + private static class ListenerContext implements CacheListenerContext, CacheInfo + { + + /** . */ + private final ExoCache cache; + + /** . */ + final CacheListener listener; + + public ListenerContext(CacheListener listener, ExoCache cache) + { + this.listener = listener; + this.cache = cache; + } + + public CacheInfo getCacheInfo() + { + return this; + } + + public String getName() + { + return cache.getName(); + } + + public int getMaxSize() + { + return cache.getMaxSize(); + } + + public long getLiveTime() + { + return cache.getLiveTime(); + } + + public int getSize() + { + return cache.getCacheSize(); + } + + void onExpire(K key, V obj) throws Exception + { + listener.onExpire(this, key, obj); + } + + void onRemove(K key, V obj) throws Exception + { + listener.onRemove(this, key, obj); + } + + void onPut(K key, V obj) throws Exception + { + listener.onPut(this, key, obj); + } + + void onPutLocal(K key, V obj) throws Exception + { + listener.onPutLocal(this, key, obj); + } + + void onGet(K key, V obj) throws Exception + { + listener.onGet(this, key, obj); + } + + void onClearCache() throws Exception + { + listener.onClearCache(this); + } + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/ExoCacheCreator.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/ExoCacheCreator.java new file mode 100644 index 00000000..8c2f9f4d --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/ExoCacheCreator.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.cache.impl.infinispan; + +import org.exoplatform.services.cache.ExoCache; +import org.exoplatform.services.cache.ExoCacheConfig; +import org.exoplatform.services.cache.ExoCacheInitException; +import org.infinispan.Cache; +import org.infinispan.configuration.cache.ConfigurationBuilder; + +import java.io.Serializable; +import java.util.Set; +import java.util.concurrent.Callable; + +/** + * This class is used to create the cache according to the given + * configuration {@link org.exoplatform.services.cache.ExoCacheConfig} + * + * @author Nicolas Filotto + * @version $Id$ + */ +public interface ExoCacheCreator +{ + + /** + * Creates an eXo cache according to the given configuration {@link org.exoplatform.services.cache.ExoCacheConfig} + * @param config the configuration of the cache to apply + * @param confBuilder the configuration builder of the infinispan cache + * @param cacheGetter a {@link Callable} instance from which we can get the cache + * @exception ExoCacheInitException if an exception happens while initializing the cache + */ + public ExoCache create(ExoCacheConfig config, ConfigurationBuilder confBuilder, + Callable> cacheGetter) throws ExoCacheInitException; + + /** + * Returns the type of {@link org.exoplatform.services.cache.ExoCacheConfig} expected by the creator + * @return the expected type + */ + public Class getExpectedConfigType(); + + /** + * Returns a set of all the implementations expected by the creator. This is mainly used to be backward compatible + * @return the expected by the creator + */ + public Set getExpectedImplementations(); +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/ExoCacheCreatorPlugin.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/ExoCacheCreatorPlugin.java new file mode 100644 index 00000000..bd772095 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/ExoCacheCreatorPlugin.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.cache.impl.infinispan; + +import org.exoplatform.container.component.BaseComponentPlugin; +import org.exoplatform.container.xml.InitParams; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class allows us to define new creators + * @author Nicolas Filotto + * @version $Id$ + */ +public class ExoCacheCreatorPlugin extends BaseComponentPlugin +{ + + /** + * The list of all the creators defined for this ComponentPlugin + */ + private final List creators; + + public ExoCacheCreatorPlugin(InitParams params) + { + creators = new ArrayList(); + List configs = params.getObjectParamValues(ExoCacheCreator.class); + for (int i = 0; i < configs.size(); i++) + { + ExoCacheCreator config = (ExoCacheCreator)configs.get(i); + creators.add(config); + } + } + + /** + * Returns all the creators defined for this ComponentPlugin + */ + public List getCreators() + { + return creators; + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/ExoCacheFactoryConfigPlugin.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/ExoCacheFactoryConfigPlugin.java new file mode 100644 index 00000000..8cbaf5e7 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/ExoCacheFactoryConfigPlugin.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.cache.impl.infinispan; + +import org.exoplatform.container.component.BaseComponentPlugin; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.container.xml.ValueParam; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * This class is used to define custom configurations + * + * @author Nicolas Filotto + * @version $Id$ + */ +public class ExoCacheFactoryConfigPlugin extends BaseComponentPlugin +{ + + /** + * The map of all the creators defined for this ComponentPlugin + */ + private final Map configs; + + public ExoCacheFactoryConfigPlugin(InitParams params) + { + configs = new HashMap(); + for (Iterator iterator = params.getValueParamIterator(); iterator.hasNext();) + { + ValueParam vParam = iterator.next(); + configs.put(vParam.getName(), vParam.getValue()); + } + } + + /** + * Returns all the configurations defined for this ComponentPlugin + */ + public Map getConfigs() + { + return configs; + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/ExoCacheFactoryImpl.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/ExoCacheFactoryImpl.java new file mode 100644 index 00000000..a6c41c80 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/ExoCacheFactoryImpl.java @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.cache.impl.infinispan; + +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.container.configuration.ConfigurationManager; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.container.xml.ValueParam; +import org.exoplatform.services.cache.ExoCache; +import org.exoplatform.services.cache.ExoCacheConfig; +import org.exoplatform.services.cache.ExoCacheFactory; +import org.exoplatform.services.cache.ExoCacheInitException; +import org.exoplatform.services.cache.impl.infinispan.distributed.DistributedExoCache; +import org.exoplatform.services.cache.impl.infinispan.generic.GenericExoCacheCreator; +import org.exoplatform.services.ispn.DistributedCacheManager; +import org.exoplatform.services.ispn.Utils; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.infinispan.Cache; +import org.infinispan.configuration.cache.CacheMode; +import org.infinispan.configuration.cache.Configuration; +import org.infinispan.configuration.cache.ConfigurationBuilder; +import org.infinispan.configuration.global.GlobalConfiguration; +import org.infinispan.configuration.global.GlobalConfigurationBuilder; +import org.infinispan.configuration.parsing.ConfigurationBuilderHolder; +import org.infinispan.configuration.parsing.ParserRegistry; +import org.infinispan.eviction.EvictionStrategy; +import org.infinispan.jmx.MBeanServerLookup; +import org.infinispan.manager.DefaultCacheManager; +import org.picocontainer.Startable; + +import java.io.InputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.Callable; + +import javax.management.MBeanServer; + +/** + * This class is the Infinispan implementation of the {@link org.exoplatform.services.cache.ExoCacheFactory} + * + * @author Nicolas Filotto + * @version $Id$ + * + */ +public class ExoCacheFactoryImpl implements ExoCacheFactory, Startable { + + /** + * The logger + */ + private static final Log LOG = ExoLogger.getLogger(ExoCacheFactoryImpl.class); + + /** + * The initial parameter key that defines the full path of the configuration template + */ + private static final String CACHE_CONFIG_TEMPLATE_KEY = "cache.config.template"; + + /** + * The initial parameter key that defines the full path of the optional async configuration template + */ + private static final String CACHE_ASYNC_TEMPLATE_KEY = "cache.async.config.template"; + + /** + * The cache manager for the distributed cache + */ + private final DistributedCacheManager distributedCacheManager; + + /** + * The current {@link ExoContainerContext} + */ + private final ExoContainerContext ctx; + + /** + * The configuration manager that allows us to retrieve a configuration file in several different + * manners + */ + private final ConfigurationManager configManager; + + /** + * The {@link DefaultCacheManager} used for all sync cache regions + */ + private final DefaultCacheManager cacheManager; + + /** + * The {@link DefaultCacheManager} used for all async cache regions + */ + private DefaultCacheManager asyncCacheManager = null; + + /** + * The mapping between the configuration types and the creators + */ + private final Map, ExoCacheCreator> mappingConfigTypeCreators = + new HashMap, ExoCacheCreator>(); + + /** + * The mapping between the implementations and the creators. This is mainly used for backward compatibility + */ + private final Map mappingImplCreators = new HashMap(); + + /** + * The mapping between the cache names and the configuration paths + */ + private final Map mappingCacheNameConfig = new HashMap(); + + /** + * The async cache template path + */ + private final String asyncCacheTemplate; + + /** + * The mapping between the cluster name and the cache managers + */ + private final Map mappingGlobalConfigCacheManager = + new HashMap(); + + /** + * The default creator + */ + private final ExoCacheCreator defaultCreator = new GenericExoCacheCreator(); + + private static final MBeanServerLookup MBEAN_SERVER_LOOKUP = new MBeanServerLookup() + { + public MBeanServer getMBeanServer(Properties properties) + { + return ExoContainerContext.getTopContainer().getMBeanServer(); + } + }; + + public ExoCacheFactoryImpl(ExoContainerContext ctx, InitParams params, ConfigurationManager configManager) + throws ExoCacheInitException + { + this(ctx, getValueParam(params, CACHE_CONFIG_TEMPLATE_KEY), getValueParam(params, CACHE_ASYNC_TEMPLATE_KEY), configManager, null); + } + + public ExoCacheFactoryImpl(ExoContainerContext ctx, InitParams params, ConfigurationManager configManager, + DistributedCacheManager dcm) throws ExoCacheInitException + { + this(ctx, getValueParam(params, CACHE_CONFIG_TEMPLATE_KEY), getValueParam(params, CACHE_ASYNC_TEMPLATE_KEY), configManager, dcm); + } + + public ExoCacheFactoryImpl(ExoContainerContext ctx, String cacheConfigTemplate, String cacheAsyncConfigTemplate, ConfigurationManager configManager, + DistributedCacheManager dcm) throws ExoCacheInitException + { + this.distributedCacheManager = dcm; + this.ctx = ctx; + this.configManager = configManager; + if (cacheConfigTemplate == null) + { + throw new IllegalArgumentException("The parameter '" + CACHE_CONFIG_TEMPLATE_KEY + "' must be set"); + } + // Initialize the main cache manager + this.cacheManager = initCacheManager(cacheConfigTemplate); + + this.asyncCacheTemplate = cacheAsyncConfigTemplate; + } + + /** + * Initializes the {@link DefaultCacheManager} + * @throws ExoCacheInitException if the cache manager cannot be initialized + */ + private DefaultCacheManager initCacheManager(final String cacheConfigTemplate) throws ExoCacheInitException + { + InputStream is = null; + try + { + // Read the configuration file of the cache + is = configManager.getInputStream(cacheConfigTemplate); + } + catch (Exception e)//NOSONAR + { + throw new ExoCacheInitException("The configuration of the CacheManager cannot be loaded from '" + + cacheConfigTemplate + "'", e); + } + if (is == null) + { + throw new ExoCacheInitException("The configuration of the CacheManager cannot be found at '" + + cacheConfigTemplate + "'"); + } + GlobalConfigurationBuilder configBuilder; + Configuration config; + try + { + ParserRegistry parser = new ParserRegistry(Thread.currentThread().getContextClassLoader()); + // Loads the configuration from the input stream + ConfigurationBuilderHolder holder = parser.parse(is); + configBuilder = holder.getGlobalConfigurationBuilder(); + config = holder.getDefaultConfigurationBuilder().build(); + } + catch (RuntimeException e) //NOSONAR + { + throw new ExoCacheInitException("Cannot parse the configuration '" + cacheConfigTemplate + "'", e); + } + configureCacheManager(configBuilder); + DefaultCacheManager cacheManager; + try + { + // Create the CacheManager from the new configuration + cacheManager = new DefaultCacheManager(configBuilder.build(), config); + } + catch (RuntimeException e) //NOSONAR + { + throw new ExoCacheInitException( + "Cannot initialize the CacheManager corresponding to the configuration '" + cacheConfigTemplate + + "'", e); + } + // Register the main cache manager + mappingGlobalConfigCacheManager.put(cacheManager.getCacheManagerConfiguration().transport().clusterName(), + cacheManager); + return cacheManager; + } + + /** + * Configure the cache manager + * + * @param configBuilder the configuration builder on which we applied all the required changes + * @throws ExoCacheInitException + */ + private void configureCacheManager(GlobalConfigurationBuilder configBuilder) throws ExoCacheInitException + { + GlobalConfiguration config = configBuilder.build(); + // Configure JGroups + configureJGroups(config, configBuilder); + // Configure the name of the cache manager + configBuilder.globalJmxStatistics().enable() + .cacheManagerName(config.globalJmxStatistics().cacheManagerName() + "_" + ctx.getName()). + // Configure the MBeanServerLookup + mBeanServerLookup(MBEAN_SERVER_LOOKUP); + } + + /** + * If some JGoups properties has been set, it will load the configuration and set + * the cluster name by adding as suffix the name of the {@link ExoContainerContext} + * + * @param config the global configuration from which the JGroups config will be extracted + * @param configBuilder the related configuration builder + * @throws ExoCacheInitException if any exception occurs while configuring JGroups + */ + private void configureJGroups(GlobalConfiguration config, GlobalConfigurationBuilder configBuilder) + throws ExoCacheInitException + { + if (loadJGroupsConfig(config, configBuilder)) + { + // The JGroups Config could be loaded which means that the configuration is for a cluster + configBuilder.transport().clusterName(config.transport().clusterName() + "-" + ctx.getName()); + } + } + + /** + * Load the JGroups configuration file thanks to the {@link ConfigurationManager} + * @param config the global configuration from which the JGroups config will be extracted + * @param configBuilder the related configuration builder + * @return true if the JGoups config could be loaded successfully, + * false if there were no JGroups config to load + * @throws ExoCacheInitException if the JGroups config could not be loaded + */ + private boolean loadJGroupsConfig(GlobalConfiguration config, GlobalConfigurationBuilder configBuilder) + throws ExoCacheInitException + { + return Utils.loadJGroupsConfig(configManager, config, configBuilder); + } + + /** + * To create a new cache instance according to the given configuration, we follow the steps below: + * + * We first try to find if a specific location of the cache configuration has been defined thanks + * to an external component plugin of type ExoCacheFactoryConfigPlugin. If so we use the default cache + * configuration defined in this file otherwise we use the default cache configuration defined in + * "${CACHE_CONFIG_TEMPLATE_KEY}" + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public ExoCache createCache(final ExoCacheConfig config) throws ExoCacheInitException + { + final String region = config.getName(); + String CacheConfig = mappingCacheNameConfig.get(region); + if(CacheConfig == null && config.isAsync() && asyncCacheTemplate !=null && asyncCacheManager == null) + { + //use async template if cache use async mode + this.asyncCacheManager = initCacheManager(asyncCacheTemplate); + } + final String customConfig = CacheConfig; + final ExoCache eXoCache; + final DefaultCacheManager cacheManager; + try + { + final ConfigurationBuilder confBuilder = new ConfigurationBuilder(); + if (customConfig != null) + { + // A custom configuration has been set + if (LOG.isInfoEnabled()) + LOG.info("A custom configuration has been set for the cache '" + region + "'."); + ParserRegistry parser = new ParserRegistry(Thread.currentThread().getContextClassLoader()); + // Load the configuration + ConfigurationBuilderHolder holder = parser.parse(configManager.getInputStream(customConfig)); + GlobalConfigurationBuilder configBuilder = holder.getGlobalConfigurationBuilder(); + // Configure JGroups and JMX since it could affect the state of the Global Config + configureCacheManager(configBuilder); + GlobalConfiguration gc = configBuilder.build(); + + // Check if a CacheManager with the same GlobalConfiguration exists + DefaultCacheManager currentCacheManager = + mappingGlobalConfigCacheManager.get(gc.transport().clusterName()); + if (currentCacheManager == null) + { + // Use a different cache manager name to prevent naming conflict + configBuilder.globalJmxStatistics().cacheManagerName( + gc.globalJmxStatistics().cacheManagerName() + "_" + region + "_" + ctx.getName()); + // No cache manager has been defined so far for this Cache Configuration + currentCacheManager = + new DefaultCacheManager(configBuilder.build(), holder.getDefaultConfigurationBuilder() + .build(), false); + for (Entry entry : holder.getNamedConfigurationBuilders().entrySet()) + { + currentCacheManager.defineConfiguration(entry.getKey(), entry.getValue().build()); + } + currentCacheManager.start(); + // We register this new cache manager + mappingGlobalConfigCacheManager.put(gc.transport().clusterName(), currentCacheManager); + } + cacheManager = currentCacheManager; + confBuilder.read(cacheManager.getDefaultCacheConfiguration()); + } + else if (config.isDistributed()) + { + // We expect a distributed cache + if (distributedCacheManager == null) + { + throw new IllegalArgumentException( + "The DistributedCacheManager has not been defined in the configuration," + + " please configure it at root container level if you want to use a distributed cache."); + } + return new DistributedExoCache(ctx, config, + distributedCacheManager.getCache(DistributedExoCache.CACHE_NAME)); + } + else + { + cacheManager = (config.isAsync() && asyncCacheManager != null) ? this.asyncCacheManager : this.cacheManager; + // No custom configuration has been found, a configuration template will be used + if (LOG.isInfoEnabled()) + LOG.info("The configuration template will be used for the cache '" + region + "'."); + confBuilder.read(cacheManager.getDefaultCacheConfiguration()); + if (!config.isRepicated()) + { + // The cache is local + confBuilder.clustering().cacheMode(CacheMode.LOCAL); + } + } + // Reset the configuration to avoid conflicts + resetConfiguration(confBuilder); + final ExoCacheCreator creator = getExoCacheCreator(config); + // Create the cache + eXoCache = creator.create(config, confBuilder, new Callable>() + { + public Cache call() throws Exception + { + cacheManager.defineConfiguration(region, confBuilder.build()); + // create and start the cache + return cacheManager.getCache(region); + } + }); + } + catch (Exception e) //NOSONAR + { + throw new ExoCacheInitException("The cache '" + region + "' could not be initialized", e); + } + return eXoCache; + } + + /** + * Add a list of creators to register + * @param plugin the plugin that contains the creators + */ + public void addCreator(ExoCacheCreatorPlugin plugin) + { + final List creators = plugin.getCreators(); + for (ExoCacheCreator creator : creators) + { + mappingConfigTypeCreators.put(creator.getExpectedConfigType(), creator); + Set implementations = creator.getExpectedImplementations(); + if (implementations == null) + { + throw new IllegalArgumentException("The set of implementations cannot be null"); + } + for (String imp : implementations) + { + mappingImplCreators.put(imp, creator); + } + } + } + + /** + * Add a list of custom configuration to register + * @param plugin the plugin that contains the configs + */ + public void addConfig(ExoCacheFactoryConfigPlugin plugin) + { + final Map configs = plugin.getConfigs(); + mappingCacheNameConfig.putAll(configs); + } + + @Override + public void start() { + // Nothing to start + } + + @Override + public void stop() { + if (cacheManager != null) { + cacheManager.stop(); + } + } + /** + * Returns the value of the ValueParam if and only if the value is not empty + */ + private static String getValueParam(InitParams params, String key) + { + if (params == null) + { + return null; + } + final ValueParam vp = params.getValueParam(key); + String result; + if (vp == null || (result = vp.getValue()) == null || (result = result.trim()).length() == 0) + { + return null; + } + return result; + } + + /** + * Returns the most relevant ExoCacheCreator according to the give configuration + */ + protected ExoCacheCreator getExoCacheCreator(ExoCacheConfig config) + { + ExoCacheCreator creator = mappingConfigTypeCreators.get(config.getClass()); + if (creator == null) + { + // No creator for this type has been found, let's try the implementation field + creator = mappingImplCreators.get(config.getImplementation()); + if (creator == null) + { + // No creator can be found, we will use the default creator + if (LOG.isInfoEnabled()) + LOG.info("No cache creator has been found for the cache '" + config.getName() + + "', the default one will be used."); + return defaultCreator; + } + } + if (LOG.isInfoEnabled()) + LOG.info("The cache '" + config.getName() + "' will be created with '" + creator.getClass() + "'."); + return creator; + } + + /** + * Clean the configuration template to prevent conflicts + */ + protected void resetConfiguration(ConfigurationBuilder confBuilder) + { + confBuilder.eviction().strategy(EvictionStrategy.NONE).size(-1).expiration() + .lifespan(-1L).maxIdle(-1L).wakeUpInterval(60000L); + } + +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/distributed/DistributedExoCache.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/distributed/DistributedExoCache.java new file mode 100644 index 00000000..1e74abb4 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/distributed/DistributedExoCache.java @@ -0,0 +1,1126 @@ +/* + * Copyright (C) 2011 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.cache.impl.infinispan.distributed; + +import org.exoplatform.container.ExoContainer; +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.management.annotations.Managed; +import org.exoplatform.management.annotations.ManagedDescription; +import org.exoplatform.management.annotations.ManagedName; +import org.exoplatform.services.cache.CacheInfo; +import org.exoplatform.services.cache.CacheListener; +import org.exoplatform.services.cache.CacheListenerContext; +import org.exoplatform.services.cache.CachedObjectSelector; +import org.exoplatform.services.cache.ExoCache; +import org.exoplatform.services.cache.ExoCacheConfig; +import org.exoplatform.services.cache.ObjectCacheInfo; +import org.exoplatform.services.ispn.AbstractMapper; +import org.exoplatform.services.ispn.DistributedCacheManager; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.infinispan.AdvancedCache; +import org.infinispan.Cache; +import org.infinispan.context.Flag; +import org.infinispan.distexec.mapreduce.Collector; +import org.infinispan.distexec.mapreduce.MapReduceTask; +import org.infinispan.distexec.mapreduce.Reducer; +import org.infinispan.notifications.Listener; +import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted; +import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated; +import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified; +import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved; +import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent; +import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent; +import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent; +import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Nicolas Filotto + * @version $Id$ + * + */ +public class DistributedExoCache implements ExoCache +{ + + /** + * Logger. + */ + private static final Log LOG = ExoLogger.getLogger(DistributedExoCache.class);//NOSONAR + + public static final String CACHE_NAME = "eXoCache"; + + private final AtomicInteger hits = new AtomicInteger(0); + + private final AtomicInteger misses = new AtomicInteger(0); + + private String label; + + private String name; + + private final String fullName; + + private boolean distributed; + + private boolean replicated; + + private boolean logEnabled; + + @SuppressWarnings("rawtypes") + private static final ConcurrentMap>> ALL_LISTENERS = + new ConcurrentHashMap>>(); + + protected final AdvancedCache, V> cache; + + @SuppressWarnings("unchecked") + public DistributedExoCache(ExoContainerContext ctx, ExoCacheConfig config, Cache cache) + { + this.fullName = ctx.getName() + "-" + config.getName(); + this.cache = (AdvancedCache, V>)cache.getAdvancedCache(); + setDistributed(config.isDistributed()); + setLabel(config.getLabel()); + setName(config.getName()); + setLogEnabled(config.isLogEnabled()); + setReplicated(config.isRepicated()); + } + + AdvancedCache, V> getCache() + { + return cache; + } + + /** + * @return the fullName + */ + String getFullName() + { + return fullName; + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("rawtypes") + public void addCacheListener(CacheListener listener) + { + if (listener == null) + { + throw new IllegalArgumentException("The listener cannot be null"); + } + List lListeners = getListeners(fullName); + if (lListeners == null) + { + lListeners = new CopyOnWriteArrayList(); + boolean alreadyAdded = false; + ConcurrentMap> listeners = getOrCreateListeners(); + if (listeners.isEmpty()) + { + synchronized (listeners) + { + if (listeners.isEmpty()) + { + // Ensure that the listener is added only once + cache.addListener(new CacheEventListener()); + listeners.put(fullName, lListeners); + alreadyAdded = true; + } + } + } + if (!alreadyAdded) + { + List oldValue = listeners.putIfAbsent(fullName, lListeners); + if (oldValue != null) + { + lListeners = oldValue; + } + } + } + lListeners.add(new ListenerContext(listener, this)); + } + + @SuppressWarnings("rawtypes") + private ConcurrentMap> getOrCreateListeners() + { + ConcurrentMap> listeners = ALL_LISTENERS.get(cache); + if (listeners == null) + { + listeners = new ConcurrentHashMap>(); + ConcurrentMap> oldValue = ALL_LISTENERS.putIfAbsent(cache, listeners); + if (oldValue != null) + { + listeners = oldValue; + } + } + return listeners; + } + + @SuppressWarnings("rawtypes") + private List getListeners(String fullName) + { + ConcurrentMap> listeners = ALL_LISTENERS.get(cache); + return listeners == null ? null : listeners.get(fullName); + } + + /** + * {@inheritDoc} + */ + public void clearCache() + { + MapReduceTask, V, Void, Void> task = new MapReduceTask, V, Void, Void>(cache); + task.mappedWith(new ClearCacheMapper(fullName)).reducedWith(new ClearCacheReducer()); + task.execute(); + onClearCache(); + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("unchecked") + public V get(Serializable name) + { + if (name == null) + { + return null; + } + @SuppressWarnings("rawtypes") + final CacheKey key = new CacheKey(fullName, name); + final V result = cache.get(key); + if (result == null) + { + misses.incrementAndGet(); + } + else + { + hits.incrementAndGet(); + } + onGet(key, result); + return result; + } + + /** + * {@inheritDoc} + */ + public int getCacheHit() + { + return hits.get(); + } + + /** + * {@inheritDoc} + */ + public int getCacheMiss() + { + return misses.get(); + } + + /** + * {@inheritDoc} + */ + public int getCacheSize() + { + MapReduceTask, V, String, Integer> task = new MapReduceTask, V, String, Integer>(cache); + task.mappedWith(new GetSizeMapper(fullName)).reducedWith(new GetSizeReducer()); + Map map = task.execute(); + int sum = 0; + for (Integer i : map.values()) + { + sum += i; + } + return sum; + } + + /** + * {@inheritDoc} + */ + public List getCachedObjects() + { + MapReduceTask, V, String, List> task = + new MapReduceTask, V, String, List>(cache); + task.mappedWith(new GetCachedObjectsMapper(fullName)).reducedWith( + new GetCachedObjectsReducer()); + Map> map = task.execute(); + List result = new ArrayList(); + for (List vals : map.values()) + { + result.addAll(vals); + } + return result; + } + + /** + * {@inheritDoc} + */ + public String getLabel() + { + return label; + } + + /** + * {@inheritDoc} + */ + public String getName() + { + return name; + } + + /** + * {@inheritDoc} + */ + public boolean isDistributed() + { + return distributed; + } + + /** + * {@inheritDoc} + */ + public boolean isLogEnabled() + { + return logEnabled; + } + + /** + * {@inheritDoc} + */ + public boolean isReplicated() + { + return replicated; + } + + /** + * {@inheritDoc} + */ + public void put(final K key, final V value) throws IllegalArgumentException + { + if (key == null) + { + throw new IllegalArgumentException("No null cache key accepted"); + } + else if (value == null) + { + // ignore null values + return; + } + putOnly(key, value); + onPut(key, value); + } + + /** + * Only puts the data into the cache nothing more + */ + protected void putOnly(K key, V value) + { + cache.withFlags(Flag.SKIP_REMOTE_LOOKUP, Flag.IGNORE_RETURN_VALUES).put(new CacheKey(fullName, key), value); + } + + /** + * {@inheritDoc} + */ + public void putMap(final Map objs) throws IllegalArgumentException + { + if (objs == null) + { + throw new IllegalArgumentException("No null map accepted"); + } + for (Serializable name : objs.keySet()) + { + if (name == null) + { + throw new IllegalArgumentException("No null cache key accepted"); + } + } + // Start transaction + cache.startBatch(); + try + { + // Wrap the key into a CacheKey and make sure that the key and the value + // are valid + Map, V> map = new LinkedHashMap, V>(); + for (Map.Entry entry : objs.entrySet()) + { + map.put(new CacheKey(fullName, entry.getKey()), entry.getValue()); + } + cache.putAll(map); + cache.endBatch(true); + // End transaction + for (Map.Entry entry : objs.entrySet()) + { + onPut(entry.getKey(), entry.getValue()); + } + } + catch (Exception e)//NOSONAR + { + cache.endBatch(false); + LOG.warn("An error occurs while executing the putMap method", e); + } + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("unchecked") + public V remove(Serializable name) throws IllegalArgumentException + { + if (name == null) + { + throw new IllegalArgumentException("No null cache key accepted"); + } + @SuppressWarnings("rawtypes") + final CacheKey key = new CacheKey(fullName, name); + V result = cache.remove(key); + onRemove(key, result); + return result; + } + + /** + * {@inheritDoc} + */ + public List removeCachedObjects() + { + final List list = getCachedObjects(); + clearCache(); + return list; + } + + /** + * {@inheritDoc} + */ + public void select(CachedObjectSelector selector) throws Exception + { + if (selector == null) + { + throw new IllegalArgumentException("No null selector"); + } + MapReduceTask, V, K, V> task = new MapReduceTask, V, K, V>(cache); + task.mappedWith(new GetEntriesMapper(fullName)).reducedWith(new GetEntriesReducer()); + Map map = task.execute(); + + for (K key : map.keySet()) + { + if (key == null) + { + continue; + } + final V value = map.get(key); + ObjectCacheInfo info = new ObjectCacheInfo() + { + public V get() + { + return value; + } + + public long getExpireTime() + { + // Cannot know: The expire time is managed by Infinispan itself + return -1; + } + }; + if (selector.select(key, info)) + { + selector.onSelect(this, key, info); + } + } + } + + /** + * {@inheritDoc} + */ + public void setDistributed(boolean distributed) + { + this.distributed = distributed; + } + + /** + * {@inheritDoc} + */ + public void setLabel(String label) + { + this.label = label; + } + + /** + * {@inheritDoc} + */ + public void setLogEnabled(boolean logEnabled) + { + this.logEnabled = logEnabled; + } + + /** + * {@inheritDoc} + */ + public void setName(String name) + { + this.name = name; + } + + /** + * {@inheritDoc} + */ + public void setReplicated(boolean replicated) + { + this.replicated = replicated; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + void onExpire(CacheKey key, V obj) + { + List listeners = getListeners(key.getFullName()); + if (listeners == null || listeners.isEmpty()) + { + return; + } + for (ListenerContext context : listeners) + { + try + { + context.onExpire(key.getKey(), obj); + } + catch (Exception e)//NOSONAR + { + if (LOG.isWarnEnabled()) + LOG.warn("Cannot execute the CacheListener properly", e); + } + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + void onRemove(CacheKey key, V obj) + { + List listeners = getListeners(key.getFullName()); + if (listeners == null || listeners.isEmpty()) + { + return; + } + for (ListenerContext context : listeners) + { + try + { + context.onRemove(key, obj); + } + catch (Exception e)//NOSONAR + { + if (LOG.isWarnEnabled()) + LOG.warn("Cannot execute the CacheListener properly", e); + } + } + } + + void onPut(CacheKey key, V obj) + { + onPut(key.getFullName(), key.getKey(), obj); + } + + public void onPut(K key, V obj) + { + onPut(fullName, key, obj); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + void onPut(String fullName, K key, V obj) + { + List listeners = getListeners(fullName); + if (listeners == null || listeners.isEmpty()) + { + return; + } + for (ListenerContext context : listeners) + { + try + { + context.onPut(key, obj); + } + catch (Exception e)//NOSONAR + { + if (LOG.isWarnEnabled()) + LOG.warn("Cannot execute the CacheListener properly", e); + } + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + void onGet(CacheKey key, V obj) + { + List listeners = getListeners(key.getFullName()); + if (listeners == null || listeners.isEmpty()) + { + return; + } + for (ListenerContext context : listeners) + { + try + { + context.onGet(key, obj); + } + catch (Exception e)//NOSONAR + { + if (LOG.isWarnEnabled()) + LOG.warn("Cannot execute the CacheListener properly", e); + } + } + } + + @SuppressWarnings("rawtypes") + public void onClearCache() + { + List listeners = getListeners(fullName); + if (listeners == null || listeners.isEmpty()) + { + return; + } + for (ListenerContext context : listeners) + { + try + { + context.onClearCache(); + } + catch (Exception e)//NOSONAR + { + if (LOG.isWarnEnabled()) + LOG.warn("Cannot execute the CacheListener properly", e); + } + } + } + + @Listener + public class CacheEventListener + { + /** + * Warning Infinispan triggers a CacheEntryEvictedEvent only at explicit eviction + * that is done lazily which is not exactly what we expect, we still use it to be + * able to use it with avoidValueReplication set to true. + */ + @CacheEntriesEvicted + public void cacheEntryEvicted(CacheEntriesEvictedEvent, V> evt) + { + if (evt.isPre()) + { + for (Map.Entry, V> entry : evt.getEntries().entrySet()) + { + onExpire(entry.getKey(), entry.getValue()); + } + } + } + + @CacheEntryRemoved + public void cacheEntryRemoved(CacheEntryRemovedEvent, V> evt) + { + if (evt.isPre() && !evt.isOriginLocal()) + { + final CacheKey key = evt.getKey(); + final V value = evt.getValue(); + onRemove(key, value); + } + } + + @CacheEntryModified + public void cacheEntryModified(CacheEntryModifiedEvent, V> evt) + { + if (!evt.isOriginLocal() && !evt.isPre()) + { + final CacheKey key = evt.getKey(); + final V value = evt.getValue(); + onPut(key, value); + } + } + + @CacheEntryCreated + public void cacheEntrCreated(CacheEntryCreatedEvent, V> evt) + { + if (!evt.isOriginLocal() && !evt.isPre()) + { + final CacheKey key = evt.getKey(); + final V value = evt.getValue(); + onPut(key, value); + } + } + } + + private static class ListenerContext implements CacheListenerContext, CacheInfo + { + + /** . */ + private final ExoCache cache; + + /** . */ + final CacheListener listener; + + public ListenerContext(CacheListener listener, ExoCache cache) + { + this.listener = listener; + this.cache = cache; + } + + public CacheInfo getCacheInfo() + { + return this; + } + + public String getName() + { + return cache.getName(); + } + + public int getMaxSize() + { + return cache.getMaxSize(); + } + + public long getLiveTime() + { + return cache.getLiveTime(); + } + + public int getSize() + { + return cache.getCacheSize(); + } + + void onExpire(K key, V obj) throws Exception + { + listener.onExpire(this, key, obj); + } + + void onRemove(K key, V obj) throws Exception + { + listener.onRemove(this, key, obj); + } + + void onPut(K key, V obj) throws Exception + { + listener.onPut(this, key, obj); + } + + void onGet(K key, V obj) throws Exception + { + listener.onGet(this, key, obj); + } + + void onClearCache() throws Exception + { + listener.onClearCache(this); + } + } + + public void setMaxSize(int max) + { + throw new UnsupportedOperationException("The configuration of the cache cannot not be modified"); + } + + public void setLiveTime(long period) + { + throw new UnsupportedOperationException("The configuration of the cache cannot not be modified"); + } + + @ManagedName("MaxEntries") + @ManagedDescription("Maximum number of entries in a cache instance. -1 means no limit.") + public int getMaxSize() + { + return Math.toIntExact(cache.getCacheConfiguration().eviction().maxEntries()); + } + + @ManagedName("Lifespan") + @ManagedDescription("Maximum lifespan of a cache entry, after which the entry is expired cluster-wide." + + " -1 means the entries never expire.") + public long getLiveTime() + { + return cache.getCacheConfiguration().expiration().lifespan(); + } + + @Managed + @ManagedName("MaxIdle") + @ManagedDescription("Maximum idle time a cache entry will be maintained in the cache. " + + "If the idle time is exceeded, the entry will be expired cluster-wide. -1 means the entries never expire.") + public long getMaxIdle() + { + return cache.getCacheConfiguration().expiration().maxIdle(); + } + + @Managed + @ManagedName("WakeUpInterval") + @ManagedDescription("Interval between subsequent eviction runs. If you wish to disable the periodic eviction " + + "process altogether, set wakeupInterval to -1.") + public long getWakeUpInterval() + { + return cache.getCacheConfiguration().expiration().wakeUpInterval(); + } + + public static class CacheKey implements Externalizable + { + private K key; + + private String fullName; + + public CacheKey() + { + } + + public CacheKey(String fullName, K key) + { + this.fullName = fullName; + this.key = key; + } + + /** + * @return the nested key + */ + K getKey() + { + return key; + } + + /** + * @return the fullName + */ + String getFullName() + { + return fullName; + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((fullName == null) ? 0 : fullName.hashCode()); + result = prime * result + ((key == null) ? 0 : key.hashCode()); + return result; + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + @SuppressWarnings("rawtypes") + CacheKey other = (CacheKey)obj; + if (fullName == null) + { + if (other.fullName != null) + return false; + } + else if (!fullName.equals(other.fullName)) + return false; + if (key == null) + { + if (other.key != null) + return false; + } + else if (!key.equals(other.key)) + return false; + return true; + } + + /** + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return "CacheKey [fullName=" + fullName + ", key=" + key + "]"; + } + + /** + * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) + */ + public void writeExternal(ObjectOutput out) throws IOException + { + byte[] buf = fullName.getBytes("UTF-8"); + out.writeInt(buf.length); + out.write(buf); + out.writeObject(key); + } + + /** + * @see java.io.Externalizable#readExternal(java.io.ObjectInput) + */ + @SuppressWarnings("unchecked") + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException + { + byte[] buf = new byte[in.readInt()]; + in.readFully(buf); + fullName = new String(buf, "UTF-8"); + key = (K)in.readObject(); + } + } + + private abstract static class AbstractExoCacheMapper extends + AbstractMapper, V, KOut, VOut> implements Externalizable + { + /** + * The full name of the cache instance + */ + private String fullName; + + public AbstractExoCacheMapper() + { + } + + public AbstractExoCacheMapper(String fullName) + { + this.fullName = fullName; + } + + /** + * The serial version UID + */ + private static final long serialVersionUID = 7962676854308932222L; + + /** + * @see org.exoplatform.services.ispn.AbstractMapper#isValid(java.lang.Object) + */ + @Override + protected boolean isValid(CacheKey key) + { + return fullName.equals(key.getFullName()); + } + + /** + * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) + */ + public void writeExternal(ObjectOutput out) throws IOException + { + byte[] buf = fullName.getBytes("UTF-8"); + out.writeInt(buf.length); + out.write(buf); + } + + /** + * @see java.io.Externalizable#readExternal(java.io.ObjectInput) + */ + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException + { + byte[] buf = new byte[in.readInt()]; + in.readFully(buf); + fullName = new String(buf, "UTF-8"); + } + } + + public static class GetSizeMapper extends AbstractExoCacheMapper + { + + public GetSizeMapper() + { + } + + public GetSizeMapper(String fullName) + { + super(fullName); + } + + /** + * {@inheritDoc} + */ + @Override + protected void _map(CacheKey key, V value, Collector collector) + { + collector.emit("total", Integer.valueOf(1)); + } + + } + + public static class GetSizeReducer implements Reducer + { + + /** + * The serial version UID + */ + private static final long serialVersionUID = -5264142863835473112L; + + /** + * @see org.infinispan.distexec.mapreduce.Reducer#reduce(java.lang.Object, java.util.Iterator) + */ + @Override + public Integer reduce(K reducedKey, Iterator iter) + { + int sum = 0; + while (iter.hasNext()) + { + Integer i = iter.next(); + sum += i; + } + return sum; + } + } + + public static class GetCachedObjectsMapper extends AbstractExoCacheMapper> + { + + public GetCachedObjectsMapper() + { + } + + public GetCachedObjectsMapper(String fullName) + { + super(fullName); + } + + /** + * {@inheritDoc} + */ + @Override + protected void _map(CacheKey key, V value, Collector> collector) + { + collector.emit("values", Collections.singletonList(value)); + } + + } + + public static class GetCachedObjectsReducer implements Reducer> + { + + /** + * The serial version UID + */ + private static final long serialVersionUID = 8069024420056440405L; + + /** + * @see org.infinispan.distexec.mapreduce.Reducer#reduce(java.lang.Object, java.util.Iterator) + */ + @Override + public List reduce(K reducedKey, Iterator> iter) + { + List values = new ArrayList(); + while (iter.hasNext()) + { + List vals = iter.next(); + values.addAll(vals); + } + return values; + } + } + + public static class ClearCacheMapper extends AbstractExoCacheMapper + { + + public ClearCacheMapper() + { + } + + public ClearCacheMapper(String fullName) + { + super(fullName); + } + + /** + * {@inheritDoc} + */ + @Override + protected void _map(CacheKey key, V value, Collector collector) + { + ExoContainer container = ExoContainerContext.getTopContainer(); + if (container == null) + { + LOG.error("The top container could not be found"); + return; + } + DistributedCacheManager dcm = + (DistributedCacheManager)container.getComponentInstanceOfType(DistributedCacheManager.class); + if (dcm == null) + { + LOG.error("The DistributedCacheManager could not be found at top container level, please configure it."); + return; + } + Cache, V> cache = dcm.getCache(CACHE_NAME); + cache.getAdvancedCache().withFlags(Flag.SKIP_REMOTE_LOOKUP, Flag.FAIL_SILENTLY).remove(key); + } + } + + public static class ClearCacheReducer implements Reducer + { + + /** + * The serial version UID + */ + private static final long serialVersionUID = -8111087186325793256L; + + /** + * @see org.infinispan.distexec.mapreduce.Reducer#reduce(java.lang.Object, java.util.Iterator) + */ + @Override + public Void reduce(Void reducedKey, Iterator iter) + { + return null; + } + } + + public static class GetEntriesMapper extends AbstractExoCacheMapper + { + public GetEntriesMapper() + { + } + + public GetEntriesMapper(String fullName) + { + super(fullName); + } + + /** + * {@inheritDoc} + */ + @Override + protected void _map(CacheKey key, V value, Collector collector) + { + collector.emit(key.getKey(), value); + } + } + + public static class GetEntriesReducer implements Reducer + { + + /** + * The serial version UID + */ + private static final long serialVersionUID = 5153826700048219537L; + + /** + * @see org.infinispan.distexec.mapreduce.Reducer#reduce(java.lang.Object, java.util.Iterator) + */ + @Override + public V reduce(K reducedKey, Iterator iter) + { + return iter == null || !iter.hasNext() ? null : iter.next(); + } + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/generic/GenericExoCacheConfig.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/generic/GenericExoCacheConfig.java new file mode 100644 index 00000000..a2556e3b --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/generic/GenericExoCacheConfig.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.cache.impl.infinispan.generic; + +import org.exoplatform.services.cache.ExoCacheConfig; + +/** + * The {@link org.exoplatform.services.cache.ExoCacheConfig} for all the eviction algorithms + * available in infinispan + * + * @author Nicolas Filotto + * @version $Id$ + * + */ +public class GenericExoCacheConfig extends ExoCacheConfig +{ + + private String strategy; + + private long maxIdle; + + private long wakeUpInterval; + + /** + * @return the strategy + */ + public String getStrategy() + { + return strategy; + } + + /** + * @param strategy the strategy to set + */ + public void setStrategy(String strategy) + { + this.strategy = strategy; + } + + /** + * @return the wakeUpInterval + */ + public long getWakeUpInterval() + { + return wakeUpInterval; + } + + /** + * @param wakeUpInterval the wakeUpInterval to set + */ + public void setWakeUpInterval(long wakeUpInterval) + { + this.wakeUpInterval = wakeUpInterval; + } + + /** + * @return the maxIdle + */ + public long getMaxIdle() + { + return maxIdle; + } + + /** + * @param maxIdle the maxIdle to set + */ + public void setMaxIdle(long maxIdle) + { + this.maxIdle = maxIdle; + } +} \ No newline at end of file diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/generic/GenericExoCacheCreator.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/generic/GenericExoCacheCreator.java new file mode 100644 index 00000000..6e4a90a4 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/cache/impl/infinispan/generic/GenericExoCacheCreator.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.cache.impl.infinispan.generic; + +import org.exoplatform.management.annotations.Managed; +import org.exoplatform.management.annotations.ManagedDescription; +import org.exoplatform.management.annotations.ManagedName; +import org.exoplatform.services.cache.ExoCache; +import org.exoplatform.services.cache.ExoCacheConfig; +import org.exoplatform.services.cache.ExoCacheInitException; +import org.exoplatform.services.cache.impl.infinispan.AbstractExoCache; +import org.exoplatform.services.cache.impl.infinispan.ExoCacheCreator; +import org.infinispan.Cache; +import org.infinispan.configuration.cache.ConfigurationBuilder; +import org.infinispan.eviction.EvictionStrategy; + +import java.io.Serializable; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.Callable; + +/** + * The generic {@link ExoCacheCreator} for all the expiration available in infinispan. + * + * @author Nicolas Filotto + * @version $Id$ + * + */ +public class GenericExoCacheCreator implements ExoCacheCreator +{ + + /** + * The default value for the eviction strategy + */ + protected String defaultStrategy = "LRU"; + + /** + * The default value for maxIdle + */ + protected long defaultMaxIdle = -1; + + /** + * The default value for wakeUpInterval + */ + protected long defaultWakeUpInterval = 5000; + + /** + * A set of all the implementations supported by this creator + */ + protected Set implementations; + + /** + * {@inheritDoc} + */ + public Set getExpectedImplementations() + { + return implementations; + } + + /** + * {@inheritDoc} + */ + public Class getExpectedConfigType() + { + return GenericExoCacheConfig.class; + } + + /** + * {@inheritDoc} + */ + public ExoCache create(ExoCacheConfig config, ConfigurationBuilder confBuilder, + Callable> cacheGetter) throws ExoCacheInitException + { + if (config instanceof GenericExoCacheConfig) + { + final GenericExoCacheConfig gConfig = (GenericExoCacheConfig)config; + return create(config, confBuilder, cacheGetter, gConfig.getStrategy(), gConfig.getMaxSize(), + gConfig.getLiveTime(), gConfig.getMaxIdle() == 0 ? defaultMaxIdle : gConfig.getMaxIdle(), + gConfig.getWakeUpInterval() == 0 ? defaultWakeUpInterval : gConfig.getWakeUpInterval()); + } + else + { + final long period = config.getLiveTime(); + return create(config, confBuilder, cacheGetter, + config.getImplementation() == null ? defaultStrategy : config.getImplementation(), config.getMaxSize(), + period > 0 ? period * 1000 : -1, defaultMaxIdle, defaultWakeUpInterval); + } + } + + /** + * Creates a new ExoCache instance with the relevant parameters + * @throws ExoCacheInitException If any exception occurs while creating the cache + */ + private ExoCache create(ExoCacheConfig config, ConfigurationBuilder confBuilder, + Callable> cacheGetter, String strategy, int maxEntries, long lifespan, long maxIdle, + long wakeUpInterval) throws ExoCacheInitException + { + EvictionStrategy es = + strategy == null || strategy.length() == 0 ? null : EvictionStrategy.valueOf(strategy + .toUpperCase(Locale.ENGLISH)); + if (es == null) + { + es = EvictionStrategy.LRU; + } + confBuilder.eviction().strategy(EvictionStrategy.valueOf(strategy)).maxEntries(maxEntries).expiration() + .lifespan(lifespan).maxIdle(maxIdle).wakeUpInterval(wakeUpInterval); + try + { + return new GenericExoCache(config, cacheGetter.call()); + } + catch (Exception e)//NOSONAR + { + throw new ExoCacheInitException("Cannot create the cache '" + config.getName() + "'", e); + } + } + + /** + * The Generic implementation of an ExoCache + */ + public static class GenericExoCache extends AbstractExoCache + { + + public GenericExoCache(ExoCacheConfig config, Cache cache) + { + super(config, cache); + } + + public void setMaxSize(int max) + { + throw new UnsupportedOperationException("The configuration of the cache cannot not be modified"); + } + + public void setLiveTime(long period) + { + throw new UnsupportedOperationException("The configuration of the cache cannot not be modified"); + } + + public int getMaxSize() + { + return Math.toIntExact(cache.getCacheConfiguration().eviction().maxEntries()); + } + + public long getLiveTime() + { + return cache.getCacheConfiguration().expiration().lifespan(); + } + + @Managed + @ManagedName("MaxIdle") + @ManagedDescription("Maximum idle time a cache entry will be maintained in the cache. " + + "If the idle time is exceeded, the entry will be expired cluster-wide. -1 means the entries never expire.") + public long getMaxIdle() + { + return cache.getCacheConfiguration().expiration().maxIdle(); + } + + @Managed + @ManagedName("WakeUpInterval") + @ManagedDescription("Interval between subsequent eviction runs. If you wish to disable the periodic eviction " + + "process altogether, set wakeupInterval to -1.") + public long getWakeUpInterval() + { + return cache.getCacheConfiguration().expiration().wakeUpInterval(); + } + } +} \ No newline at end of file diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/ispn/AbstractMapper.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/ispn/AbstractMapper.java new file mode 100644 index 00000000..ecd30455 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/ispn/AbstractMapper.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2011 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.ispn; + +import org.infinispan.distexec.mapreduce.Collector; +import org.infinispan.distexec.mapreduce.Mapper; + +/** + * The main class of all the mappers. + * + * @author Nicolas Filotto + * @version $Id$ + * + */ +public abstract class AbstractMapper implements Mapper +{ + + /** + * The serial version UID + */ + private static final long serialVersionUID = 7118530772747505976L; + + /** + * {@inheritDoc} + */ + @Override + public void map(KIn key, VIn value, Collector collector) + { + if (isValid(key)) + { + _map(key, value, collector); + } + } + + /** + * This method is in fact an internal mapping, it will be called by the map method in + * case the given key matches with the context + */ + protected abstract void _map(KIn key, VIn value, Collector collector); + + /** + * Indicates if the given key matches with the current context, indeed as the cache instances are + * shared it is needed to check each key to know if it is part of the targeted scope or not. + * + * @param key the key to check + * @return true if the key matches with the scope, false otherwise. + */ + protected abstract boolean isValid(KIn key); + +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/ispn/DistributedCacheManager.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/ispn/DistributedCacheManager.java new file mode 100644 index 00000000..570c5365 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/ispn/DistributedCacheManager.java @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2011 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.ispn; + +import org.exoplatform.container.ExoContainer; +import org.exoplatform.container.configuration.ConfigurationManager; +import org.exoplatform.container.util.TemplateConfigurationHelper; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.container.xml.PropertiesParam; +import org.exoplatform.container.xml.ValueParam; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.exoplatform.services.transaction.TransactionService; +import org.infinispan.Cache; +import org.infinispan.configuration.cache.ConfigurationBuilder; +import org.infinispan.configuration.global.GlobalConfigurationBuilder; +import org.infinispan.configuration.parsing.ConfigurationBuilderHolder; +import org.infinispan.configuration.parsing.ParserRegistry; +import org.infinispan.manager.DefaultCacheManager; +import org.infinispan.manager.EmbeddedCacheManager; +import org.infinispan.transaction.lookup.TransactionManagerLookup; +import org.picocontainer.Startable; + +import java.util.Map; +import java.util.Map.Entry; + +import javax.transaction.TransactionManager; + +/** + * This class is used to allow to use infinispan in distribution mode with + * the ability to launch infinispan instances in standalone mode, in other + * words outside an application server. To make it possible we will need to share + * the same cache instance whatever the related {@link ExoContainer} because + * to be able to launch ispn instances in standalone mode we need to have a static + * configuration file. + * + * @author Nicolas Filotto + * @version $Id$ + * + */ +public class DistributedCacheManager implements Startable +{ + /** + * The logger + */ + private static final Log LOG = ExoLogger //NOSONAR + .getLogger("exo.kernel.component.ext.cache.impl.infinispan.v8.DistributedCacheManager");//NOSONAR + + /** + * The parameter name corresponding to the infinispan configuration + */ + private static final String CONFIG_FILE_PARAMETER_NAME = "infinispan-configuration"; + + /** + * The parameter name corresponding to the parameters to inject + * into the infinispan configuration file + */ + private static final String PARAMS_PARAMETER_NAME = "parameters"; + + /** + * The infinispan cache manager + */ + protected final EmbeddedCacheManager manager; + + /** + * Default constructor + */ + public DistributedCacheManager(String configurationFile, Map parameters, + ConfigurationManager configManager) + { + this.manager = init(configurationFile, parameters, configManager, null); + } + + /** + * Default constructor + */ + public DistributedCacheManager(InitParams params, ConfigurationManager configManager) + { + this(params, configManager, null); + } + + /** + * Default constructor + */ + public DistributedCacheManager(InitParams params, ConfigurationManager configManager, TransactionService ts) + { + ValueParam vp; + final String result; + if (params != null && (vp = params.getValueParam(CONFIG_FILE_PARAMETER_NAME)) != null + && (result = vp.getValue()) != null && !result.isEmpty()) + { + PropertiesParam pp = params.getPropertiesParam(PARAMS_PARAMETER_NAME); + this.manager = + init(result, pp == null ? null : pp.getProperties(), configManager, + ts == null ? null : ts.getTransactionManager()); + } + else + { + throw new IllegalArgumentException("The parameter '" + CONFIG_FILE_PARAMETER_NAME + "' must be set"); + } + } + + /** + * Initializes and created the CacheManager + * @param configurationFile the path of the configuration file + * @param parameters the parameters to inject into the configuration file + * @param configManager the configuration manager used to get the configuration file + * @param tm the transaction manager + * @return the CacheManager initialized + */ + private EmbeddedCacheManager init(final String configurationFile, final Map parameters, + final ConfigurationManager configManager, final TransactionManager tm) + { + try + { + if (configurationFile == null || configurationFile.isEmpty()) + { + throw new IllegalArgumentException("The parameter 'configurationFile' must be set"); + } + if (LOG.isDebugEnabled()) + { + LOG.debug("The configuration file of the DistributedCacheManager will be loaded from " + configurationFile); + } + final TemplateConfigurationHelper helper = + new TemplateConfigurationHelper(new String[]{"^.*"}, new String[]{}, configManager); + if (LOG.isDebugEnabled() && parameters != null && !parameters.isEmpty()) + { + LOG.debug("The parameters to use while processing the configuration file are " + parameters); + } + ParserRegistry parser = new ParserRegistry(Thread.currentThread().getContextClassLoader()); + // Load the configuration + ConfigurationBuilderHolder holder = parser.parse(helper.fillTemplate(configurationFile, parameters)); + GlobalConfigurationBuilder configBuilder = holder.getGlobalConfigurationBuilder(); + Utils.loadJGroupsConfig(configManager, configBuilder.build(), configBuilder); + // Create the CacheManager from the new configuration + EmbeddedCacheManager manager = + new DefaultCacheManager(configBuilder.build(), holder.getDefaultConfigurationBuilder().build()); + TransactionManagerLookup tml = new TransactionManagerLookup() + { + public TransactionManager getTransactionManager() throws Exception + { + return tm; + } + }; + for (Entry entry : holder.getNamedConfigurationBuilders().entrySet()) + { + ConfigurationBuilder b = entry.getValue(); + if (tm != null) + { + b.transaction().transactionManagerLookup(tml); + } + manager.defineConfiguration(entry.getKey(), b.build()); + } + for( String cacheName : manager.getCacheNames()) + { + manager.getCache(cacheName); + } + return manager; + } + catch (Exception e)//NOSONAR + { + throw new IllegalStateException( + "Could not initialize the cache manager corresponding to the configuration file " + configurationFile, e); + } + } + + /** + * Gives the cache corresponding to the given name if it doesn't exist + * a {@link NullPointerException} will be thrown + */ + public Cache getCache(String cacheName) + { + Cache cache = manager.getCache(cacheName); + if (cache == null) + { + throw new IllegalArgumentException("The expected cache named '" + cacheName + + "' has not been defined in the configuration of infinispan as named cache."); + } + return cache; + } + + /** + * @see org.picocontainer.Startable#start() + */ + @Override + public void start() + { + } + + /** + * @see org.picocontainer.Startable#stop() + */ + @Override + public void stop() + { + manager.stop(); + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/ispn/Utils.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/ispn/Utils.java new file mode 100644 index 00000000..c8b31d9f --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/ispn/Utils.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.ispn; + +import org.exoplatform.container.configuration.ConfigurationManager; +import org.exoplatform.container.util.TemplateConfigurationHelper; +import org.exoplatform.services.cache.ExoCacheInitException; +import org.infinispan.configuration.global.GlobalConfiguration; +import org.infinispan.configuration.global.GlobalConfigurationBuilder; +import org.infinispan.remoting.transport.jgroups.JGroupsTransport; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +/** + * @author Nicolas Filotto + * @version $Id$ + * + */ +public class Utils +{ + + private Utils() + { + } + + /** + * Load the JGroups configuration file thanks to the {@link ConfigurationManager} + * @param config the global configuration from which the JGroups config will be extracted + * @param configBuilder the related configuration builder + * @return true if the JGoups config could be loaded successfully, + * false if there were no JGroups config to load + * @throws IllegalStateException if the JGroups config could not be loaded + */ + public static boolean loadJGroupsConfig(ConfigurationManager cfm, GlobalConfiguration config, + GlobalConfigurationBuilder configBuilder) throws ExoCacheInitException + { + Properties properties = config.transport().properties(); + if (properties == null || !properties.containsKey(JGroupsTransport.CONFIGURATION_FILE)) + { + return false; + } + String filename = properties.getProperty(JGroupsTransport.CONFIGURATION_FILE); + InputStream inputStream = TemplateConfigurationHelper.getInputStream(cfm, filename); + + // inputStream still remains null, so file was not opened + if (inputStream == null) + { + throw new IllegalStateException("The jgroups configuration cannot be loaded from '" + filename + "'"); + } + try + { + // Set the jgroups configuration as XML + properties.setProperty(JGroupsTransport.CONFIGURATION_XML, + org.exoplatform.container.util.Utils.readStream(inputStream)); + } + catch (IOException e) + { + throw new IllegalStateException("The jgroups configuration cannot be read from '" + filename + "'", e); + } + // Remove the property corresponding to the configuration file + properties.remove(JGroupsTransport.CONFIGURATION_FILE); + configBuilder.transport().withProperties(properties); + return true; + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/transaction/infinispan/GenericTransactionService.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/transaction/infinispan/GenericTransactionService.java new file mode 100644 index 00000000..646ce67b --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/transaction/infinispan/GenericTransactionService.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.transaction.infinispan; + +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.services.transaction.impl.AbstractTransactionService; +import org.infinispan.transaction.lookup.TransactionManagerLookup; + +import javax.transaction.TransactionManager; + +/** + * @author Dmytro Katayev + * @version $Id: GenericTransactionService.java -1 $ + */ +public class GenericTransactionService extends AbstractTransactionService +{ + + /** + * TransactionManagerLookup. + */ + protected final TransactionManagerLookup tmLookup; + + /** + * JBossTransactionManagerLookup constructor. + * + * @param tmLookup TransactionManagerLookup + */ + public GenericTransactionService(TransactionManagerLookup tmLookup) + { + this(tmLookup, null); + } + + public GenericTransactionService(TransactionManagerLookup tmLookup, InitParams params) + { + super(params); + this.tmLookup = tmLookup; + } + + /** + * {@inheritDoc} + */ + public TransactionManager findTransactionManager() throws Exception + { + return tmLookup.getTransactionManager(); + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/transaction/infinispan/JBossStandaloneJTAManagerLookup.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/transaction/infinispan/JBossStandaloneJTAManagerLookup.java new file mode 100644 index 00000000..a9b62a7b --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/transaction/infinispan/JBossStandaloneJTAManagerLookup.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2011 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.transaction.infinispan; + +import org.exoplatform.commons.utils.ClassLoading; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.exoplatform.services.transaction.TransactionService; +import org.infinispan.transaction.lookup.TransactionManagerLookup; + +import java.lang.reflect.Method; + +import javax.transaction.TransactionManager; +import javax.transaction.UserTransaction; + +/** + * This class is used to replace the one provided by default in ISPN 5.0 since + * it implicitly requires that ISPN is initialized before the rest which is actually + * the exact opposite. Indeed Arjuna accessors are initialized in the init method which is + * called at ISPN initialization but we expect to get the {@link TransactionManager} + * from it through the {@link TransactionService} before initializing the JCR so before + * initializing ISPN. + * + * The code below is a simple copy/paste of the code of + * {@link org.infinispan.transaction.lookup.JBossStandaloneJTAManagerLookup} of ISPN 4. + * + * @author Nicolas Filotto + * @version $Id$ + * + */ +public class JBossStandaloneJTAManagerLookup implements TransactionManagerLookup +{ + /** + * The logger + */ + private static final Log LOG = //NOSONAR + ExoLogger.getLogger("exo.jcr.component.core.JBossStandaloneJTAManagerLookup");//NOSONAR + + private Method manager, user; + + public JBossStandaloneJTAManagerLookup() + { + try + { + manager = loadClassStrict("com.arjuna.ats.jta.TransactionManager").getMethod("transactionManager"); + user = loadClassStrict("com.arjuna.ats.jta.UserTransaction").getMethod("userTransaction"); + } + catch (ClassNotFoundException e) + { + throw new RuntimeException(e);//NOSONAR + } + catch (SecurityException e) + { + throw new RuntimeException(e);//NOSONAR + } + catch (NoSuchMethodException e) + { + throw new RuntimeException(e);//NOSONAR + } + } + + public TransactionManager getTransactionManager() throws Exception + { + TransactionManager tm = (TransactionManager)manager.invoke(null); + if (tm == null && LOG.isWarnEnabled()) + { + LOG.warn("The transaction manager could not be found"); + } + return tm; + } + + public UserTransaction getUserTransaction() throws Exception + { + UserTransaction ut = (UserTransaction)user.invoke(null); + if (ut == null && LOG.isWarnEnabled()) + { + LOG.warn("The user transaction could not be found"); + } + return ut; + } + + /** + * Loads the specified class using this class's classloader, or, if it is null (i.e. this class was + * loaded by the bootstrap classloader), the system classloader.
If loadtime instrumentation via + * GenerateInstrumentedClassLoader is used, this class may be loaded by the bootstrap classloader.
+ * + * @param classname name of the class to load + * @return the class + * @throws ClassNotFoundException + */ + private static Class loadClassStrict(String classname) throws ClassNotFoundException + { + return ClassLoading.loadClass(classname, JBossStandaloneJTAManagerLookup.class); + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/transaction/infinispan/JBossTransactionsService.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/transaction/infinispan/JBossTransactionsService.java new file mode 100644 index 00000000..64a55015 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/exoplatform/services/transaction/infinispan/JBossTransactionsService.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.transaction.infinispan; + +import org.exoplatform.container.xml.InitParams; +import org.infinispan.transaction.lookup.TransactionManagerLookup; + +import javax.transaction.UserTransaction; + +/** + * Add the specific part for Arjuna + * + * @author Nicolas Filotto + * @version $Id$ + * + */ +public class JBossTransactionsService extends GenericTransactionService +{ + + public JBossTransactionsService(TransactionManagerLookup tmLookup) + { + super(tmLookup); + } + + public JBossTransactionsService(TransactionManagerLookup tmLookup, InitParams params) + { + super(tmLookup, params); + } + + /** + * {@inheritDoc} + */ + @Override + protected UserTransaction findUserTransaction() throws Exception + { + return com.arjuna.ats.jta.UserTransaction.userTransaction(); + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/gatein/common/transaction/JTAUserTransactionLifecycleListener.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/gatein/common/transaction/JTAUserTransactionLifecycleListener.java new file mode 100644 index 00000000..eb1102fa --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/gatein/common/transaction/JTAUserTransactionLifecycleListener.java @@ -0,0 +1,45 @@ +/* + * JBoss, a division of Red Hat + * Copyright 2012, Red Hat Middleware, LLC, and individual + * contributors as indicated by the @authors tag. See the + * copyright.txt in the distribution for a full listing of + * individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.gatein.common.transaction; + +/** + * Listener for perform some actions at the specified point of JTA transaction lifecycle.
+ * Transaction lifecycle needs to be managed through {@link JTAUserTransactionLifecycleService} to have listeners executed.
+ * + * For now, we have shared instance of one registered listener for all transactions, so listener implementations need to be + * thread-safe + * + * @author Marek Posolda + */ +public interface JTAUserTransactionLifecycleListener { + /** + * Callback method to be executed before start of JTA transaction + */ + void beforeBegin(); + + /** + * Callback method to be executed after start of JTA transaction + */ + void afterBegin(); +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/gatein/common/transaction/JTAUserTransactionLifecycleService.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/gatein/common/transaction/JTAUserTransactionLifecycleService.java new file mode 100644 index 00000000..8365f5ef --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/gatein/common/transaction/JTAUserTransactionLifecycleService.java @@ -0,0 +1,57 @@ +/* + * JBoss, a division of Red Hat + * Copyright 2012, Red Hat Middleware, LLC, and individual + * contributors as indicated by the @authors tag. See the + * copyright.txt in the distribution for a full listing of + * individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.gatein.common.transaction; + +import javax.transaction.UserTransaction; + + +/** + * Service provides methods for managing lifecycle of JTA transaction + * + * @author Marek Posolda + */ +public interface JTAUserTransactionLifecycleService { + + /** + * @return instance of UserTransaction + */ + UserTransaction getUserTransaction(); + + /** + * Commit or Rollback JTA transaction according to it's current status + */ + void finishJTATransaction(); + + /** + * Starts JTA transaction if not already started + */ + void beginJTATransaction(); + + /** + * Register listener to perform some operations during transaction lifecycle + * + * @param listener to be registered + */ + void registerListener(JTAUserTransactionLifecycleListener listener); +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/gatein/common/transaction/JTAUserTransactionLifecycleServiceImpl.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/gatein/common/transaction/JTAUserTransactionLifecycleServiceImpl.java new file mode 100644 index 00000000..f73d4a0d --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/java/org/gatein/common/transaction/JTAUserTransactionLifecycleServiceImpl.java @@ -0,0 +1,157 @@ +/* + * JBoss, a division of Red Hat + * Copyright 2012, Red Hat Middleware, LLC, and individual + * contributors as indicated by the @authors tag. See the + * copyright.txt in the distribution for a full listing of + * individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.gatein.common.transaction; + +import java.util.LinkedList; +import java.util.List; + +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.transaction.Status; +import javax.transaction.UserTransaction; + +import org.exoplatform.services.transaction.TransactionService; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; + +/** + * Base implementation of {@link JTAUserTransactionLifecycleService} . + * + * @author Marek Posolda + */ +public class JTAUserTransactionLifecycleServiceImpl implements JTAUserTransactionLifecycleService { + private static final Log log = ExoLogger.getLogger(JTAUserTransactionLifecycleServiceImpl.class); + + private UserTransaction userTransaction; + + private TransactionService transactionService; + + // For now, we have one listener instance for all transactions + private List listeners = new LinkedList<>(); + + public JTAUserTransactionLifecycleServiceImpl(TransactionService transactionService) { + this.transactionService = transactionService; + } + + public JTAUserTransactionLifecycleServiceImpl() { + } + + /** + * {@inheritDoc} + */ + public void beginJTATransaction() { + UserTransaction tx = getUserTransaction(); + + try { + if (tx == null) { + // for test scope, this may happen + log.debug("UserTransaction is null, ignore transactional behavior"); + } else if (tx.getStatus() == Status.STATUS_NO_TRANSACTION) { + executeListenersBeforeBegin(); + tx.begin(); + executeListenersAfterBegin(); + } else { + log.warn("UserTransaction not started as it's in state " + tx.getStatus()); + } + } catch (Exception e) { + throw new IllegalStateException("Error when starting transaction", e); + } + } + + /** + * {@inheritDoc} + */ + public void finishJTATransaction() { + UserTransaction tx = getUserTransaction(); + + try { + if (tx == null) { + // for test scope, this may happen + log.debug("UserTransaction is null, ignore transactional behavior"); + } else { + int txStatus = tx.getStatus(); + if (txStatus == Status.STATUS_NO_TRANSACTION) { + log.warn("UserTransaction can't be finished as it wasn't started"); + } else if (txStatus == Status.STATUS_MARKED_ROLLBACK || txStatus == Status.STATUS_ROLLEDBACK + || txStatus == Status.STATUS_ROLLING_BACK) { + log.warn("Going to rollback UserTransaction as it's status is " + txStatus); + tx.rollback(); + } else { + tx.commit(); + } + } + } catch (Exception e) { + throw new IllegalStateException("Error when committing transaction", e); + } + } + + /** + * Obtain {@link UserTransaction} via JNDI call or via {@link TransactionService} if that fails + * + * @return transaction + */ + public UserTransaction getUserTransaction() { + // It's fine to reuse same instance of UserTransaction as UserTransaction is singleton in JBoss and most other AS. + // And new InitialContext().lookup("java:comp/UserTransaction") is quite expensive operation + if (userTransaction == null) { + synchronized (this) { + if (userTransaction == null) { + try { + userTransaction = (UserTransaction) new InitialContext().lookup("java:comp/UserTransaction"); + } catch (NamingException ne) { + log.debug("UserTransaction not found via JNDI. Trying TransactionService"); + userTransaction = transactionService.getUserTransaction(); + } + } + } + } + return userTransaction; + } + + /** + * {@inheritDoc} + */ + public void registerListener(JTAUserTransactionLifecycleListener listener) { + log.info("Registered listener " + listener); + listeners.add(listener); + } + + protected void executeListenersBeforeBegin() { + for (JTAUserTransactionLifecycleListener listener : listeners) { + if (log.isTraceEnabled()) { + log.trace("Execute listener " + listener + " before begin of JTA transaction"); + } + listener.beforeBegin(); + } + } + + protected void executeListenersAfterBegin() { + for (JTAUserTransactionLifecycleListener listener : listeners) { + if (log.isTraceEnabled()) { + log.trace("Execute listener " + listener + " after begin of JTA transaction"); + } + listener.afterBegin(); + } + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/cache/infinispan/cluster/cache-async-config.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/cache/infinispan/cluster/cache-async-config.xml new file mode 100644 index 00000000..b4d9b064 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/cache/infinispan/cluster/cache-async-config.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/cache/infinispan/cluster/cache-config.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/cache/infinispan/cluster/cache-config.xml new file mode 100644 index 00000000..4278133f --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/cache/infinispan/cluster/cache-config.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/cache/infinispan/local/cache-config.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/cache/infinispan/local/cache-config.xml new file mode 100644 index 00000000..b3b78ded --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/cache/infinispan/local/cache-config.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/jgroups/jgroups-service-tcp.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/jgroups/jgroups-service-tcp.xml new file mode 100644 index 00000000..791e709f --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/jgroups/jgroups-service-tcp.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/jgroups/jgroups-service-udp.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/jgroups/jgroups-service-udp.xml new file mode 100644 index 00000000..780adc67 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/jgroups/jgroups-service-udp.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/portal/cache-configuration-template.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/portal/cache-configuration-template.xml new file mode 100644 index 00000000..ca8d883b --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/portal/cache-configuration-template.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/portal/configuration.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/portal/configuration.xml new file mode 100644 index 00000000..b9783cdb --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/conf/portal/configuration.xml @@ -0,0 +1,34 @@ + + + + + org.exoplatform.services.cache.ExoCacheFactory + org.exoplatform.services.cache.impl.infinispan.ExoCacheFactoryImpl + + + cache.config.template + jar:/conf/portal/cache-configuration-template.xml + + + + diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/no-aop b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/main/resources/no-aop new file mode 100644 index 00000000..e69de29b diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/TestAbstractExoCache.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/TestAbstractExoCache.java new file mode 100644 index 00000000..5bc85214 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/TestAbstractExoCache.java @@ -0,0 +1,692 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.cache.impl.infinispan; + +import junit.framework.TestCase; + +import org.exoplatform.container.ExoContainer; +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.RootContainer; +import org.exoplatform.container.configuration.ConfigurationManager; +import org.exoplatform.services.cache.CacheListener; +import org.exoplatform.services.cache.CacheListenerContext; +import org.exoplatform.services.cache.CacheService; +import org.exoplatform.services.cache.CachedObjectSelector; +import org.exoplatform.services.cache.ExoCache; +import org.exoplatform.services.cache.ExoCacheConfig; +import org.exoplatform.services.cache.ExoCacheFactory; +import org.exoplatform.services.cache.ExoCacheInitException; +import org.exoplatform.services.cache.ObjectCacheInfo; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Nicolas Filotto + * @version $Id$ + * + */ +public class TestAbstractExoCache extends TestCase +{ + + PortalContainer container; + + CacheService service; + + AbstractExoCache cache; + + public TestAbstractExoCache(String name) + { + super(name); + } + + public void setUp() throws Exception + { + + ExoContainer topContainer = ExoContainerContext.getTopContainer(); + if(topContainer != null) { + topContainer.stop(); + } + RootContainer.setInstance(null); + ExoContainerContext.setCurrentContainer(null); + + this.container = PortalContainer.getInstance(); + this.service = container.getComponentInstanceOfType(CacheService.class); + this.cache = (AbstractExoCache)service.getCacheInstance("myCache"); + } + + public void testGet() throws Exception + { + cache.put(new MyKey("a"), "a"); + assertEquals("a", cache.get(new MyKey("a"))); + cache.put(new MyKey("a"), "c"); + assertEquals("c", cache.get(new MyKey("a"))); + cache.remove(new MyKey("a")); + assertEquals(null, cache.get(new MyKey("a"))); + assertEquals(null, cache.get(new MyKey("x"))); + + cache.clearCache(); + } + + public void testRemove() throws Exception + { + cache.put(new MyKey("a"), 1); + cache.put(new MyKey("b"), 2); + cache.put(new MyKey("c"), 3); + assertEquals(3, cache.getCacheSize()); + assertEquals(1, cache.remove(new MyKey("a"))); + assertEquals(2, cache.getCacheSize()); + assertEquals(2, cache.remove(new MyKey("b"))); + assertEquals(1, cache.getCacheSize()); + assertEquals(null, cache.remove(new MyKey("x"))); + assertEquals(1, cache.getCacheSize()); + + cache.clearCache(); + } + + public void testPutMap() throws Exception + { + Map values = new HashMap(); + values.put(new MyKey("a"), "a"); + values.put(new MyKey("b"), "b"); + assertEquals(0, cache.getCacheSize()); + cache.putMap(values); + assertEquals(2, cache.getCacheSize()); + values = new HashMap() + { + private static final long serialVersionUID = 1L; + + public Set> entrySet() + { + Set> set = new LinkedHashSet>(super.entrySet()); + set.add(new Entry() + { + + public Object setValue(Object paramV) + { + return null; + } + + public Object getValue() + { + throw new RuntimeException("An exception"); + } + + public Serializable getKey() + { + return "c"; + } + }); + return set; + } + }; + values.put(new MyKey("e"), "e"); + values.put(new MyKey("d"), "d"); + cache.putMap(values); + assertEquals(2, cache.getCacheSize()); + + cache.clearCache(); + } + + public void testGetCachedObjects() throws Exception + { + cache.put(new MyKey("a"), "a"); + cache.put(new MyKey("b"), "b"); + cache.put(new MyKey("c"), "c"); + cache.put(new MyKey("d"), null); + assertEquals(3, cache.getCacheSize()); + List values = cache.getCachedObjects(); + assertEquals(3, values.size()); + assertTrue(values.contains("a")); + assertTrue(values.contains("b")); + assertTrue(values.contains("c")); + + cache.clearCache(); + } + + public void testRemoveCachedObjects() throws Exception + { + cache.put(new MyKey("a"), "a"); + cache.put(new MyKey("b"), "b"); + cache.put(new MyKey("c"), "c"); + cache.put(new MyKey("d"), null); + assertEquals(3, cache.getCacheSize()); + List values = cache.removeCachedObjects(); + assertEquals(3, values.size()); + assertTrue(values.contains("a")); + assertTrue(values.contains("b")); + assertTrue(values.contains("c")); + assertEquals(0, cache.getCacheSize()); + + cache.clearCache(); + } + + public void testSelect() throws Exception + { + cache.put(new MyKey("a"), 1); + cache.put(new MyKey("b"), 2); + cache.put(new MyKey("c"), 3); + final AtomicInteger count = new AtomicInteger(); + CachedObjectSelector selector = new CachedObjectSelector() + { + + public void onSelect(ExoCache cache, Serializable key, + ObjectCacheInfo ocinfo) throws Exception + { + assertTrue(key.equals(new MyKey("a")) || key.equals(new MyKey("b")) || key.equals(new MyKey("c"))); + assertTrue(ocinfo.get().equals(1) || ocinfo.get().equals(2) || ocinfo.get().equals(3)); + count.incrementAndGet(); + } + + public boolean select(Serializable key, ObjectCacheInfo ocinfo) + { + return true; + } + }; + cache.select(selector); + assertEquals(3, count.intValue()); + + cache.clearCache(); + } + + public void testGetHitsNMisses() throws Exception + { + int hits = cache.getCacheHit(); + int misses = cache.getCacheMiss(); + cache.put(new MyKey("a"), "a"); + cache.get(new MyKey("a")); + cache.remove(new MyKey("a")); + cache.get(new MyKey("a")); + cache.get(new MyKey("z")); + assertEquals(1, cache.getCacheHit() - hits); + assertEquals(2, cache.getCacheMiss() - misses); + + cache.clearCache(); + } + + private ExoCacheFactory getExoCacheFactoryInstance() throws ExoCacheInitException + { + PortalContainer pc = PortalContainer.getInstance(); + return new ExoCacheFactoryImpl((ExoContainerContext)pc.getComponentInstanceOfType(ExoContainerContext.class), + "jar:/conf/portal/cache-configuration-template.xml", null, (ConfigurationManager)pc + .getComponentInstanceOfType(ConfigurationManager.class), null); + } + + public void testMultiThreading() throws Exception + { + final ExoCache cache = service.getCacheInstance("test-multi-threading"); + final int totalElement = 100; + final int totalTimes = 20; + int reader = 20; + int writer = 10; + int remover = 5; + int cleaner = 1; + final CountDownLatch startSignalWriter = new CountDownLatch(1); + final CountDownLatch startSignalOthers = new CountDownLatch(1); + final CountDownLatch doneSignal = new CountDownLatch(reader + writer + remover); + final List errors = Collections.synchronizedList(new ArrayList()); + for (int i = 0; i < writer; i++) + { + final int index = i; + Thread thread = new Thread() + { + public void run() + { + try + { + startSignalWriter.await(); + for (int j = 0; j < totalTimes; j++) + { + for (int i = 0; i < totalElement; i++) + { + cache.put(new MyKey("key" + i), "value" + i); + } + if (index == 0 && j == 0) + { + // The cache is full, we can launch the others + startSignalOthers.countDown(); + } + sleep(50); + } + } + catch (Exception e) + { + errors.add(e); + } + finally + { + doneSignal.countDown(); + } + } + }; + thread.start(); + } + startSignalWriter.countDown(); + for (int i = 0; i < reader; i++) + { + Thread thread = new Thread() + { + public void run() + { + try + { + startSignalOthers.await(); + for (int j = 0; j < totalTimes; j++) + { + for (int i = 0; i < totalElement; i++) + { + cache.get(new MyKey("key" + i)); + } + sleep(50); + } + } + catch (Exception e) + { + errors.add(e); + } + finally + { + doneSignal.countDown(); + } + } + }; + thread.start(); + } + for (int i = 0; i < remover; i++) + { + Thread thread = new Thread() + { + public void run() + { + try + { + startSignalOthers.await(); + for (int j = 0; j < totalTimes; j++) + { + for (int i = 0; i < totalElement; i++) + { + cache.remove(new MyKey("key" + i)); + } + sleep(50); + } + } + catch (Exception e) + { + errors.add(e); + } + finally + { + doneSignal.countDown(); + } + } + }; + thread.start(); + } + doneSignal.await(); + for (int i = 0; i < totalElement; i++) + { + cache.put(new MyKey("key" + i), "value" + i); + } + assertEquals(totalElement, cache.getCacheSize()); + final CountDownLatch startSignal = new CountDownLatch(1); + final CountDownLatch doneSignal2 = new CountDownLatch(writer + cleaner); + for (int i = 0; i < writer; i++) + { + Thread thread = new Thread() + { + public void run() + { + try + { + startSignal.await(); + for (int j = 0; j < totalTimes; j++) + { + for (int i = 0; i < totalElement; i++) + { + cache.put(new MyKey("key" + i), "value" + i); + } + sleep(50); + } + } + catch (Exception e) + { + errors.add(e); + } + finally + { + doneSignal2.countDown(); + } + } + }; + thread.start(); + } + for (int i = 0; i < cleaner; i++) + { + Thread thread = new Thread() + { + public void run() + { + try + { + startSignal.await(); + for (int j = 0; j < totalTimes; j++) + { + sleep(150); + cache.clearCache(); + } + } + catch (Exception e) + { + errors.add(e); + } + finally + { + doneSignal2.countDown(); + } + } + }; + thread.start(); + } + cache.clearCache(); + assertEquals(0, cache.getCacheSize()); + if (!errors.isEmpty()) + { + for (Exception e : errors) + { + e.printStackTrace(); + } + throw errors.get(0); + } + cache.clearCache(); + } + + public static class MyCacheListener implements CacheListener + { + + public int clearCache; + + public int expire; + + public int get; + + public int put; + + public int remove; + + public void onClearCache(CacheListenerContext context) throws Exception + { + clearCache++; + } + + public void onExpire(CacheListenerContext context, Serializable key, Object obj) throws Exception + { + expire++; + } + + public void onGet(CacheListenerContext context, Serializable key, Object obj) throws Exception + { + get++; + } + + public void onPut(CacheListenerContext context, Serializable key, Object obj) throws Exception + { + put++; + } + + public void onRemove(CacheListenerContext context, Serializable key, Object obj) throws Exception + { + remove++; + } + } + + public static class MyKey implements Serializable + { + private static final long serialVersionUID = 1L; + + public String value; + + public MyKey(String value) + { + this.value = value; + } + + @Override + public boolean equals(Object paramObject) + { + return paramObject instanceof MyKey && ((MyKey)paramObject).value.equals(value); + } + + @Override + public int hashCode() + { + return value.hashCode(); + } + + @Override + public String toString() + { + return value; + } + } + + /** + * WARNING: For Linux distributions the following JVM parameter must be set to true: java.net.preferIPv4Stack + */ + @SuppressWarnings("unchecked") + public void testDistributedCache() throws Exception + { + // If the cache is still alive this test fails due to a TimeoutException. +// cache.cache.getCacheManager().stop(); + ExoCacheConfig config = new ExoCacheConfig(); + config.setName("MyCacheDistributed"); + config.setMaxSize(8); + config.setLiveTime(1); + config.setImplementation("LRU"); + config.setReplicated(true); + ExoCacheConfig config2 = new ExoCacheConfig(); + config2.setName("MyCacheDistributed2"); + config2.setMaxSize(8); + config2.setLiveTime(1); + config2.setImplementation("LRU"); + config2.setReplicated(true); + AbstractExoCache cache1 = + (AbstractExoCache)getExoCacheFactoryInstance().createCache(config); + MyCacheListener listener1 = new MyCacheListener(); + cache1.addCacheListener(listener1); + AbstractExoCache cache2 = + (AbstractExoCache)getExoCacheFactoryInstance().createCache(config); + MyCacheListener listener2 = new MyCacheListener(); + cache2.addCacheListener(listener2); + try + { + cache1.put(new MyKey("a"), "b"); + assertEquals(1, cache1.getCacheSize()); + assertEquals("b", cache2.get(new MyKey("a"))); + assertEquals(1, cache2.getCacheSize()); + assertEquals(1, listener1.put); + assertEquals(1, listener2.put); + assertEquals(0, listener1.get); + assertEquals(1, listener2.get); + cache2.put(new MyKey("b"), "c"); + assertEquals(2, cache1.getCacheSize()); + assertEquals(2, cache2.getCacheSize()); + assertEquals("c", cache1.get(new MyKey("b"))); + assertEquals(2, listener1.put); + assertEquals(2, listener2.put); + assertEquals(1, listener1.get); + assertEquals(1, listener2.get); + assertEquals(2, cache1.getCacheSize()); + assertEquals(2, cache2.getCacheSize()); + assertEquals(2, listener1.put); + assertEquals(2, listener2.put); + assertEquals(1, listener1.get); + assertEquals(1, listener2.get); + cache2.put(new MyKey("a"), "a"); + assertEquals(2, cache1.getCacheSize()); + assertEquals(2, cache2.getCacheSize()); + assertEquals("a", cache1.get(new MyKey("a"))); + assertEquals(3, listener1.put); + assertEquals(3, listener2.put); + assertEquals(2, listener1.get); + assertEquals(1, listener2.get); + cache2.remove(new MyKey("a")); + assertEquals(1, cache1.getCacheSize()); + assertEquals(1, cache2.getCacheSize()); + assertEquals(3, listener1.put); + assertEquals(3, listener2.put); + assertEquals(2, listener1.get); + assertEquals(1, listener2.get); + assertEquals(1, listener1.remove); + assertEquals(1, listener2.remove); + cache1.put(new MyKey("c"), "c"); + cache1.clearCache(); + assertEquals(0, cache1.getCacheSize()); + assertEquals(null, cache1.get(new MyKey("b"))); + assertEquals("c", cache2.get(new MyKey("b"))); + assertEquals("c", cache2.get(new MyKey("c"))); + assertEquals(2, cache2.getCacheSize()); + assertEquals(4, listener1.put); + assertEquals(4, listener2.put); + assertEquals(3, listener1.get); + assertEquals(3, listener2.get); + assertEquals(1, listener1.remove); + assertEquals(1, listener2.remove); + assertEquals(1, listener1.clearCache); + assertEquals(0, listener2.clearCache); + Map values = new HashMap(); + values.put(new MyKey("a"), "a"); + values.put(new MyKey("b"), "b"); + cache1.putMap(values); + assertEquals(2, cache1.getCacheSize()); + Thread.sleep(40); + assertEquals("a", cache2.get(new MyKey("a"))); + assertEquals("b", cache2.get(new MyKey("b"))); + assertEquals(3, cache2.getCacheSize()); + assertEquals(6, listener1.put); + assertEquals(6, listener2.put); + assertEquals(3, listener1.get); + assertEquals(5, listener2.get); + assertEquals(1, listener1.remove); + assertEquals(1, listener2.remove); + assertEquals(1, listener1.clearCache); + assertEquals(0, listener2.clearCache); + values = new HashMap() + { + private static final long serialVersionUID = 1L; + + public Set> entrySet() + { + Set> set = new LinkedHashSet>(super.entrySet()); + set.add(new Entry() + { + + public Object setValue(Object paramV) + { + return null; + } + + public Object getValue() + { + throw new RuntimeException("An exception"); + } + + public Serializable getKey() + { + return "c"; + } + }); + return set; + } + }; + values.put(new MyKey("e"), "e"); + values.put(new MyKey("d"), "d"); + cache1.putMap(values); + assertEquals(2, cache1.getCacheSize()); + assertEquals(3, cache2.getCacheSize()); + assertEquals(6, listener1.put); + assertEquals(6, listener2.put); + assertEquals(3, listener1.get); + assertEquals(5, listener2.get); + assertEquals(1, listener1.remove); + assertEquals(1, listener2.remove); + assertEquals(1, listener1.clearCache); + assertEquals(0, listener2.clearCache); + assertEquals(0, listener1.expire); + assertEquals(0, listener2.expire); + Thread.sleep(5600); + // The values are evicted lazily when we call it + cache1.get(new MyKey("a")); + cache1.get(new MyKey("b")); + cache2.get(new MyKey("a")); + cache2.get(new MyKey("b")); + cache2.get(new MyKey("c")); + assertEquals(0, cache1.getCacheSize()); + assertEquals(0, cache2.getCacheSize()); + assertEquals(6, listener1.put); + assertEquals(6, listener2.put); + assertEquals(5, listener1.get); + assertEquals(8, listener2.get); + assertEquals(1, listener1.remove); + assertEquals(1, listener2.remove); + assertEquals(1, listener1.clearCache); + assertEquals(0, listener2.clearCache); + } + finally + { + cache1.cache.getCacheManager().stop(); + cache2.cache.getCacheManager().stop(); + } + } + public void testPut() throws Exception + { + cache.put(new MyKey("a"), "a"); + cache.put(new MyKey("b"), "b"); + cache.put(new MyKey("c"), "c"); + assertEquals(3, cache.getCacheSize()); + cache.put(new MyKey("a"), "c"); + assertEquals(3, cache.getCacheSize()); + cache.put(new MyKey("d"), "c"); + assertEquals(4, cache.getCacheSize()); + + cache.clearCache(); + } + + public void testClearCache() throws Exception + { + cache.put(new MyKey("a"), "a"); + cache.put(new MyKey("b"), "b"); + cache.put(new MyKey("c"), "c"); + assertTrue(cache.getCacheSize() > 0); + cache.clearCache(); + assertTrue(cache.getCacheSize() == 0); + + cache.clearCache(); + } + +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/TestExoCacheConfig.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/TestExoCacheConfig.java new file mode 100644 index 00000000..37d6836d --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/TestExoCacheConfig.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.cache.impl.infinispan; + +import org.exoplatform.services.cache.ExoCacheConfig; + +/** + * @author Nicolas Filotto + * @version $Id$ + * + */ +public class TestExoCacheConfig extends ExoCacheConfig +{ + +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/TestExoCacheCreator.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/TestExoCacheCreator.java new file mode 100644 index 00000000..b6545562 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/TestExoCacheCreator.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.cache.impl.infinispan; + +import org.exoplatform.services.cache.CacheListener; +import org.exoplatform.services.cache.CachedObjectSelector; +import org.exoplatform.services.cache.ExoCache; +import org.exoplatform.services.cache.ExoCacheConfig; +import org.exoplatform.services.cache.ExoCacheInitException; +import org.infinispan.Cache; +import org.infinispan.configuration.cache.ConfigurationBuilder; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; + +/** + * @author Nicolas Filotto + * @version $Id$ + * + */ +public class TestExoCacheCreator implements ExoCacheCreator +{ + + /** + * {@inheritDoc} + */ + @Override + public ExoCache create(ExoCacheConfig config, ConfigurationBuilder confBuilder, + Callable> cacheGetter) throws ExoCacheInitException + { + return new TestExoCache(); + } + + public Class getExpectedConfigType() + { + return TestExoCacheConfig.class; + } + + public Set getExpectedImplementations() + { + return Collections.singleton("TEST"); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public static class TestExoCache implements ExoCache + { + + public void addCacheListener(CacheListener listener) + { + } + + public int getCacheHit() + { + return 0; + } + + public int getCacheMiss() + { + return 0; + } + + public int getCacheSize() + { + return 0; + } + + public List getCachedObjects() + { + return null; + } + + public String getLabel() + { + return null; + } + + public long getLiveTime() + { + return 0; + } + + public int getMaxSize() + { + return 0; + } + + public String getName() + { + return "name"; + } + + public boolean isDistributed() + { + return false; + } + + public boolean isLogEnabled() + { + return false; + } + + public boolean isReplicated() + { + return false; + } + + public void select(CachedObjectSelector selector) throws Exception + { + + } + + public void setDistributed(boolean b) + { + + } + + public void setLabel(String s) + { + + } + + public void setLiveTime(long period) + { + + } + + public void setLogEnabled(boolean b) + { + + } + + public void setMaxSize(int max) + { + + } + + public void setName(String name) + { + + } + + public void setReplicated(boolean b) + { + + } + + public void clearCache() + { + + } + + public Object get(Serializable key) + { + return null; + } + + public void put(Serializable key, Object value) throws NullPointerException + { + + } + + public void putMap(Map objs) throws NullPointerException, IllegalArgumentException + { + + } + + public Object remove(Serializable key) throws NullPointerException + { + return null; + } + + public List removeCachedObjects() + { + return null; + } + + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/TestExoCacheFactoryImpl.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/TestExoCacheFactoryImpl.java new file mode 100644 index 00000000..fbfcbcac --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/TestExoCacheFactoryImpl.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.cache.impl.infinispan; + +import junit.framework.TestCase; + +import org.exoplatform.container.PortalContainer; +import org.exoplatform.services.cache.CacheService; +import org.exoplatform.services.cache.ExoCache; +import org.exoplatform.services.cache.impl.infinispan.TestExoCacheCreator.TestExoCache; +import org.infinispan.configuration.cache.CacheMode; +import org.infinispan.manager.CacheContainer; + +/** + * @author Nicolas Filotto + * @version $Id$ + * + */ +@SuppressWarnings("rawtypes") +public class TestExoCacheFactoryImpl extends TestCase +{ + + CacheService service_; + + public TestExoCacheFactoryImpl(String name) + { + super(name); + } + + public void setUp() throws Exception + { + service_ = (CacheService)PortalContainer.getInstance().getComponentInstanceOfType(CacheService.class); + } + + public void testCacheFactory() + { + ExoCache cache = service_.getCacheInstance("myCache"); + assertTrue("expect an instance of AbstractExoCache", cache instanceof AbstractExoCache); + AbstractExoCache aCache = (AbstractExoCache)cache; + assertTrue("expect a local cache", + aCache.cache.getCacheConfiguration().clustering().cacheMode() == CacheMode.LOCAL); + aCache.cache.stop(); + cache = service_.getCacheInstance("cacheDistributed"); + assertTrue("expect an instance of AbstractExoCache", cache instanceof AbstractExoCache); + aCache = (AbstractExoCache)cache; + assertTrue("expect a distributed cache", + aCache.cache.getCacheConfiguration().clustering().cacheMode() == CacheMode.REPL_SYNC); + aCache.cache.stop(); + cache = service_.getCacheInstance("myCustomCache"); + assertTrue("expect an instance of AbstractExoCache", cache instanceof AbstractExoCache); + aCache = (AbstractExoCache)cache; + assertTrue("expect a distributed cache", + aCache.cache.getCacheConfiguration().clustering().cacheMode() == CacheMode.REPL_SYNC); + aCache.cache.stop(); + } + + public void testExoCacheCreator() + { + ExoCache cache = service_.getCacheInstance("test-default-impl"); + assertTrue("expect an instance of AbstractExoCache", cache instanceof AbstractExoCache); + AbstractExoCache aCache = (AbstractExoCache)cache; + aCache.cache.stop(); + cache = service_.getCacheInstance("test-custom-impl-with-old-config"); + assertTrue("expect an instance of TestExoCache", cache instanceof TestExoCache); + cache = service_.getCacheInstance("test-custom-impl-with-new-config"); + assertTrue("expect an instance of TestExoCache", cache instanceof TestExoCache); + } + + public void testSameCacheManager() + { + ExoCache cache1 = service_.getCacheInstance("myCustomCache"); + assertTrue("expect an instance of AbstractExoCache", cache1 instanceof AbstractExoCache); + AbstractExoCache aCache1 = (AbstractExoCache)cache1; + CacheContainer cacheContainer1 = aCache1.cache.getCacheManager(); + + ExoCache cache2 = service_.getCacheInstance("myCustomCache-Bis"); + assertTrue("expect an instance of AbstractExoCache", cache2 instanceof AbstractExoCache); + AbstractExoCache aCache2 = (AbstractExoCache)cache2; + CacheContainer cacheContainer2 = aCache2.cache.getCacheManager(); + assertTrue("The CacheContainer should be the same", cacheContainer1 == cacheContainer2); + + ExoCache cache3 = service_.getCacheInstance("myCustomCache-Bis2"); + assertTrue("expect an instance of AbstractExoCache", cache3 instanceof AbstractExoCache); + AbstractExoCache aCache3 = (AbstractExoCache)cache3; + CacheContainer cacheContainer3 = aCache3.cache.getCacheManager(); + assertTrue("The CacheContainer should be the same", cacheContainer1 == cacheContainer3); + + aCache1.cache.stop(); + aCache2.cache.stop(); + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/distributed/TestDistributedExoCache.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/distributed/TestDistributedExoCache.java new file mode 100644 index 00000000..632f1644 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/exoplatform/services/cache/impl/infinispan/distributed/TestDistributedExoCache.java @@ -0,0 +1,858 @@ +/* + * Copyright (C) 2010 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.exoplatform.services.cache.impl.infinispan.distributed; + +import junit.framework.TestCase; + +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.configuration.ConfigurationManager; +import org.exoplatform.services.cache.CacheListener; +import org.exoplatform.services.cache.CacheListenerContext; +import org.exoplatform.services.cache.CacheService; +import org.exoplatform.services.cache.CachedObjectSelector; +import org.exoplatform.services.cache.ExoCache; +import org.exoplatform.services.cache.ExoCacheConfig; +import org.exoplatform.services.cache.ExoCacheFactory; +import org.exoplatform.services.cache.ObjectCacheInfo; +import org.exoplatform.services.cache.impl.infinispan.ExoCacheFactoryImpl; +import org.exoplatform.services.ispn.DistributedCacheManager; +import org.infinispan.affinity.KeyAffinityService; +import org.infinispan.affinity.KeyAffinityServiceFactory; +import org.infinispan.affinity.KeyGenerator; +import org.infinispan.distribution.DistributionManager; +import org.junit.Ignore; +import org.junit.Test; + + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Nicolas Filotto + * @version $Id$ + * + */ +public class TestDistributedExoCache extends TestCase +{ + + CacheService service; + + DistributedExoCache cache; + + DistributedExoCache cache2; + + public TestDistributedExoCache(String name) + { + super(name); + } + + public void setUp() throws Exception + { + this.service = (CacheService)PortalContainer.getInstance().getComponentInstanceOfType(CacheService.class); + this.cache = (DistributedExoCache)service.getCacheInstance("cache-distributed"); + this.cache2 = (DistributedExoCache)service.getCacheInstance("cache-distributed2"); + cache2.put(new MyKey("a"), "a"); + } + + protected void tearDown() throws Exception + { + cache.clearCache(); + cache2.clearCache(); + } + + public void testPut() throws Exception + { + cache.put(new MyKey("a"), "a"); + cache.put(new MyKey("b"), "b"); + cache.put(new MyKey("c"), "c"); + assertEquals(3, cache.getCacheSize()); + cache.put(new MyKey("a"), "c"); + assertEquals(3, cache.getCacheSize()); + cache.put(new MyKey("d"), "c"); + assertEquals(4, cache.getCacheSize()); + } + + public void testClearCache() throws Exception + { + cache.put(new MyKey("a"), "a"); + cache.put(new MyKey("b"), "b"); + cache.put(new MyKey("c"), "c"); + assertTrue(cache.getCacheSize() > 0); + cache.clearCache(); + assertTrue(cache.getCacheSize() == 0); + } + + public void testGet() throws Exception + { + cache.put(new MyKey("a"), "a"); + assertEquals("a", cache.get(new MyKey("a"))); + cache.put(new MyKey("a"), "c"); + assertEquals("c", cache.get(new MyKey("a"))); + cache.remove(new MyKey("a")); + assertEquals(null, cache.get(new MyKey("a"))); + assertEquals(null, cache.get(new MyKey("x"))); + } + + public void testRemove() throws Exception + { + cache.put(new MyKey("a"), 1); + cache.put(new MyKey("b"), 2); + cache.put(new MyKey("c"), 3); + assertEquals(3, cache.getCacheSize()); + assertEquals(1, cache.remove(new MyKey("a"))); + assertEquals(2, cache.getCacheSize()); + assertEquals(2, cache.remove(new MyKey("b"))); + assertEquals(1, cache.getCacheSize()); + assertEquals(null, cache.remove(new MyKey("x"))); + assertEquals(1, cache.getCacheSize()); + } + + public void testPutMap() throws Exception + { + Map values = new HashMap(); + values.put(new MyKey("a"), "a"); + values.put(new MyKey("b"), "b"); + assertEquals(0, cache.getCacheSize()); + cache.putMap(values); + assertEquals(2, cache.getCacheSize()); + values = new HashMap() + { + private static final long serialVersionUID = 1L; + + public Set> entrySet() + { + Set> set = new LinkedHashSet>(super.entrySet()); + set.add(new Entry() + { + + public Object setValue(Object paramV) + { + return null; + } + + public Object getValue() + { + throw new RuntimeException("An exception"); + } + + public Serializable getKey() + { + return "c"; + } + }); + return set; + } + }; + values.put(new MyKey("e"), "e"); + values.put(new MyKey("d"), "d"); + cache.putMap(values); + assertEquals(2, cache.getCacheSize()); + } + + public void testGetCachedObjects() throws Exception + { + cache.put(new MyKey("a"), "a"); + cache.put(new MyKey("b"), "b"); + cache.put(new MyKey("c"), "c"); + cache.put(new MyKey("d"), null); + assertEquals(3, cache.getCacheSize()); + List values = cache.getCachedObjects(); + assertEquals(3, values.size()); + assertTrue(values.contains("a")); + assertTrue(values.contains("b")); + assertTrue(values.contains("c")); + } + + public void testRemoveCachedObjects() throws Exception + { + cache.put(new MyKey("a"), "a"); + cache.put(new MyKey("b"), "b"); + cache.put(new MyKey("c"), "c"); + cache.put(new MyKey("d"), null); + assertEquals(3, cache.getCacheSize()); + List values = cache.removeCachedObjects(); + assertEquals(3, values.size()); + assertTrue(values.contains("a")); + assertTrue(values.contains("b")); + assertTrue(values.contains("c")); + assertEquals(0, cache.getCacheSize()); + } + + public void testSelect() throws Exception + { + cache.put(new MyKey("a"), 1); + cache.put(new MyKey("b"), 2); + cache.put(new MyKey("c"), 3); + final AtomicInteger count = new AtomicInteger(); + CachedObjectSelector selector = new CachedObjectSelector() + { + + public void onSelect(ExoCache cache, Serializable key, + ObjectCacheInfo ocinfo) throws Exception + { + assertTrue(key.equals(new MyKey("a")) || key.equals(new MyKey("b")) || key.equals(new MyKey("c"))); + assertTrue(ocinfo.get().equals(1) || ocinfo.get().equals(2) || ocinfo.get().equals(3)); + count.incrementAndGet(); + } + + public boolean select(Serializable key, ObjectCacheInfo ocinfo) + { + return true; + } + }; + cache.select(selector); + assertEquals(3, count.intValue()); + } + + public void testGetHitsNMisses() throws Exception + { + int hits = cache.getCacheHit(); + int misses = cache.getCacheMiss(); + cache.put(new MyKey("a"), "a"); + cache.get(new MyKey("a")); + cache.remove(new MyKey("a")); + cache.get(new MyKey("a")); + cache.get(new MyKey("z")); + assertEquals(1, cache.getCacheHit() - hits); + assertEquals(2, cache.getCacheMiss() - misses); + } + + public void testMultiThreading() throws Exception + { + final int totalElement = 100; + final int totalTimes = 20; + int reader = 20; + int writer = 10; + int remover = 5; + int cleaner = 1; + final CountDownLatch startSignalWriter = new CountDownLatch(1); + final CountDownLatch startSignalOthers = new CountDownLatch(1); + final CountDownLatch doneSignal = new CountDownLatch(reader + writer + remover); + final List errors = Collections.synchronizedList(new ArrayList()); + for (int i = 0; i < writer; i++) + { + final int index = i; + Thread thread = new Thread() + { + public void run() + { + try + { + startSignalWriter.await(); + for (int j = 0; j < totalTimes; j++) + { + for (int i = 0; i < totalElement; i++) + { + cache.put(new MyKey("key" + i), "value" + i); + } + if (index == 0 && j == 0) + { + // The cache is full, we can launch the others + startSignalOthers.countDown(); + } + sleep(50); + } + } + catch (Exception e) + { + errors.add(e); + } + finally + { + doneSignal.countDown(); + } + } + }; + thread.start(); + } + startSignalWriter.countDown(); + for (int i = 0; i < reader; i++) + { + Thread thread = new Thread() + { + public void run() + { + try + { + startSignalOthers.await(); + for (int j = 0; j < totalTimes; j++) + { + for (int i = 0; i < totalElement; i++) + { + cache.get(new MyKey("key" + i)); + } + sleep(50); + } + } + catch (Exception e) + { + errors.add(e); + } + finally + { + doneSignal.countDown(); + } + } + }; + thread.start(); + } + for (int i = 0; i < remover; i++) + { + Thread thread = new Thread() + { + public void run() + { + try + { + startSignalOthers.await(); + for (int j = 0; j < totalTimes; j++) + { + for (int i = 0; i < totalElement; i++) + { + cache.remove(new MyKey("key" + i)); + } + sleep(50); + } + } + catch (Exception e) + { + errors.add(e); + } + finally + { + doneSignal.countDown(); + } + } + }; + thread.start(); + } + doneSignal.await(); + for (int i = 0; i < totalElement; i++) + { + cache.put(new MyKey("key" + i), "value" + i); + } + assertEquals(totalElement, cache.getCacheSize()); + final CountDownLatch startSignal = new CountDownLatch(1); + final CountDownLatch doneSignal2 = new CountDownLatch(writer + cleaner); + for (int i = 0; i < writer; i++) + { + Thread thread = new Thread() + { + public void run() + { + try + { + startSignal.await(); + for (int j = 0; j < totalTimes; j++) + { + for (int i = 0; i < totalElement; i++) + { + cache.put(new MyKey("key" + i), "value" + i); + } + sleep(50); + } + } + catch (Exception e) + { + errors.add(e); + } + finally + { + doneSignal2.countDown(); + } + } + }; + thread.start(); + } + for (int i = 0; i < cleaner; i++) + { + Thread thread = new Thread() + { + public void run() + { + try + { + startSignal.await(); + for (int j = 0; j < totalTimes; j++) + { + sleep(150); + cache.clearCache(); + } + } + catch (Exception e) + { + errors.add(e); + } + finally + { + doneSignal2.countDown(); + } + } + }; + thread.start(); + } + cache.clearCache(); + assertEquals(0, cache.getCacheSize()); + if (!errors.isEmpty()) + { + for (Exception e : errors) + { + e.printStackTrace(); + } + throw errors.get(0); + } + } + + public static class MyCacheListener implements CacheListener + { + + public int clearCache; + + public int expire; + + public int get; + + public int put; + + public int remove; + + public void onClearCache(CacheListenerContext context) throws Exception + { + clearCache++; + } + + public void onExpire(CacheListenerContext context, Serializable key, Object obj) throws Exception + { + expire++; + } + + public void onGet(CacheListenerContext context, Serializable key, Object obj) throws Exception + { + get++; + } + + public void onPut(CacheListenerContext context, Serializable key, Object obj) throws Exception + { + put++; + } + + public void onRemove(CacheListenerContext context, Serializable key, Object obj) throws Exception + { + remove++; + } + } + + public static class MyKey implements Serializable + { + private static final long serialVersionUID = 1L; + + public Object value; + + public String displayValue; + + public MyKey() + { + } + + public MyKey(Object value) + { + this.value = value; + } + + public MyKey(String displayValue, Object value) + { + this.displayValue = displayValue; + this.value = value; + } + + @Override + public boolean equals(Object paramObject) + { + return paramObject instanceof MyKey && ((MyKey)paramObject).value.equals(value); + } + + @Override + public int hashCode() + { + return value.hashCode(); + } + + @Override + public String toString() + { + return displayValue == null ? value.toString() : displayValue; + } + } + + public static class MyKeyGenerator implements KeyGenerator> + { + + public static final Random rnd = new Random(); + + private String fullName; + + public MyKeyGenerator(String fullName) + { + this.fullName = fullName; + } + + @Override + public DistributedExoCache.CacheKey getKey() + { + return new DistributedExoCache.CacheKey(fullName, new MyKey(rnd.nextLong())); + } + } + + /** + * WARNING: For Linux distributions the following JVM parameter must be set to true: java.net.preferIPv4Stack. + * + * @throws Exception + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public void testDistributedCache() throws Exception + { + PortalContainer pc = PortalContainer.getInstance(); + ExoCacheConfig config = new ExoCacheConfig(); + config.setName("MyCacheDistributed"); + config.setMaxSize(5); + config.setLiveTime(1); + config.setImplementation("LRU"); + config.setDistributed(true); + Map params = new HashMap(); + params.put("infinispan-num-owners", "1"); + ConfigurationManager cm = (ConfigurationManager)pc.getComponentInstanceOfType(ConfigurationManager.class); + DistributedCacheManager dcm2 = + new DistributedCacheManager("jar:/conf/portal/distributed-cache-configuration.xml", params, cm); + + DistributedExoCache cache1 = + (DistributedExoCache)((ExoCacheFactory)pc + .getComponentInstanceOfType(ExoCacheFactory.class)).createCache(config); + DistributionManager dm = cache1.getCache().getDistributionManager(); + DistributedExoCache cache2 = + (DistributedExoCache)new ExoCacheFactoryImpl( + (ExoContainerContext)pc.getComponentInstanceOfType(ExoContainerContext.class), + "jar:/conf/portal/cache-configuration-template.xml", null, cm, dcm2).createCache(config); + KeyAffinityService kas1 = + KeyAffinityServiceFactory.newLocalKeyAffinityService(cache1.getCache(), + new MyKeyGenerator(cache1.getFullName()), Executors.newSingleThreadExecutor(), 100); + KeyAffinityService kas2 = + KeyAffinityServiceFactory.newLocalKeyAffinityService(cache2.getCache(), + new MyKeyGenerator(cache1.getFullName()), Executors.newSingleThreadExecutor(), 100); + + try + { + Object a, b, c; + for (int i = 0; i < 2; i++) + { + if (i == 0) + { + a = + new MyKey("a", ((DistributedExoCache.CacheKey)kas1.getKeyForAddress(cache1.getCache() + .getRpcManager().getAddress())).getKey().value); + } + else + { + a = + new MyKey("a", ((DistributedExoCache.CacheKey)kas2.getKeyForAddress(cache2.getCache() + .getRpcManager().getAddress())).getKey().value); + } + for (int j = 0; j < 2; j++) + { + if (j == 0) + { + b = + new MyKey("b", ((DistributedExoCache.CacheKey)kas1.getKeyForAddress(cache1.getCache() + .getRpcManager().getAddress())).getKey().value); + } + else + { + b = + new MyKey("b", ((DistributedExoCache.CacheKey)kas2.getKeyForAddress(cache2.getCache() + .getRpcManager().getAddress())).getKey().value); + } + for (int k = 0; k < 2; k++) + { + if (k == 0) + { + c = + new MyKey("c", ((DistributedExoCache.CacheKey)kas1.getKeyForAddress(cache1.getCache() + .getRpcManager().getAddress())).getKey().value); + } + else + { + c = + new MyKey("c", ((DistributedExoCache.CacheKey)kas2.getKeyForAddress(cache2.getCache() + .getRpcManager().getAddress())).getKey().value); + } + checkUseCase(cache1, cache2, dm, a, b, c); + } + } + } + } + finally + { + dcm2.stop(); + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private void checkUseCase(DistributedExoCache cache1, + DistributedExoCache cache2, DistributionManager dm, Object a, Object b, Object c) + throws InterruptedException + { + MyCacheListener listener1 = new MyCacheListener(); + cache1.addCacheListener(listener1); + MyCacheListener listener2 = new MyCacheListener(); + cache2.addCacheListener(listener2); + boolean isALocal = dm.getLocality(new DistributedExoCache.CacheKey(cache1.getFullName(), new MyKey(a))).isLocal(); + boolean isBLocal = dm.getLocality(new DistributedExoCache.CacheKey(cache1.getFullName(), new MyKey(b))).isLocal(); + boolean isCLocal = dm.getLocality(new DistributedExoCache.CacheKey(cache1.getFullName(), new MyKey(c))).isLocal(); + MyKey key = new MyKey(a); + cache1.put(key, "b"); + assertEquals(1, cache1.getCacheSize()); + assertEquals("b", cache2.get(new MyKey(a))); + assertEquals(1, cache2.getCacheSize()); + + int put1 = 1; + int put2 = isALocal ? 0 : 1; + + assertEquals(put1, listener1.put); + assertEquals(put2, listener2.put); + + assertEquals(0, listener1.get); + assertEquals(1, listener2.get); + + MyKey key2 = new MyKey(b); + cache2.put(key2, "c"); + assertEquals(2, cache1.getCacheSize()); + assertEquals(2, cache2.getCacheSize()); + assertEquals("c", cache1.get(new MyKey(b))); + + put1 += isBLocal ? 1 : 0; + put2++; + + assertEquals(put1, listener1.put); + assertEquals(put2, listener2.put); + + assertEquals(1, listener1.get); + assertEquals(1, listener2.get); + + assertEquals(2, cache1.getCacheSize()); + assertEquals(2, cache2.getCacheSize()); + + assertEquals(put1, listener1.put); + assertEquals(put2, listener2.put); + + assertEquals(1, listener1.get); + assertEquals(1, listener2.get); + + key = new MyKey(a); + cache2.put(key, "a"); + assertEquals(2, cache1.getCacheSize()); + assertEquals(2, cache2.getCacheSize()); + assertEquals("a", cache1.get(new MyKey(a))); + + put1 += isALocal ? 1 : 0; + put2++; + + assertEquals(put1, listener1.put); + assertEquals(put2, listener2.put); + + assertEquals(2, listener1.get); + assertEquals(1, listener2.get); + + key = new MyKey(a); + cache2.remove(key); + assertEquals(1, cache1.getCacheSize()); + assertEquals(1, cache2.getCacheSize()); + + assertEquals(put1, listener1.put); + assertEquals(put2, listener2.put); + + assertEquals(2, listener1.get); + assertEquals(1, listener2.get); + + int remove1 = isALocal ? 1 : 0; + int remove2 = 1; + + assertEquals(remove1, listener1.remove); + assertEquals(remove2, listener2.remove); + + key = new MyKey(c); + cache1.put(key, "c"); + assertEquals(2, cache1.getCacheSize()); + assertEquals(2, cache2.getCacheSize()); + assertEquals("c", cache2.get(new MyKey(c))); + + put1++; + put2 += isCLocal ? 0 : 1; + + assertEquals(put1, listener1.put); + assertEquals(put2, listener2.put); + + assertEquals(2, listener1.get); + assertEquals(2, listener2.get); + + assertEquals(remove1, listener1.remove); + assertEquals(remove2, listener2.remove); + + assertEquals(0, listener1.clearCache); + assertEquals(0, listener2.clearCache); + + cache1.clearCache(); + assertEquals(0, cache1.getCacheSize()); + assertNull(cache1.get(new MyKey(b))); + assertNull(cache1.get(new MyKey(c))); + assertNull(cache2.get(new MyKey(b))); + assertNull(cache2.get(new MyKey(c))); + assertEquals(0, cache2.getCacheSize()); + + assertEquals(put1, listener1.put); + assertEquals(put2, listener2.put); + + assertEquals(4, listener1.get); + assertEquals(4, listener2.get); + + // Since the clear cache map/reduce can only find cache1, + // the remove calls will be applied to cache1 so cache2 + // will be notified on its entries, this is due to the + // hack used to apply modifications within a map/reduce + remove2 += (isBLocal ? 0 : 1) + (isCLocal ? 0 : 1); + + assertEquals(remove1, listener1.remove); + assertEquals(remove2, listener2.remove); + + assertEquals(1, listener1.clearCache); + assertEquals(0, listener2.clearCache); + + Map values = new HashMap(); + key = new MyKey(a); + key2 = new MyKey(b); + values.put(key, "a"); + values.put(key2, "b"); + cache1.putMap(values); + assertEquals(2, cache1.getCacheSize()); + Thread.sleep(40); + assertEquals("a", cache1.get(new MyKey(a))); + assertEquals("b", cache1.get(new MyKey(b))); + assertEquals("a", cache2.get(new MyKey(a))); + assertEquals("b", cache2.get(new MyKey(b))); + assertEquals(2, cache2.getCacheSize()); + + put1 += 2; + put2 += (isALocal ? 0 : 1) + (isBLocal ? 0 : 1); + + assertEquals(put1, listener1.put); + assertEquals(put2, listener2.put); + + assertEquals(6, listener1.get); + assertEquals(6, listener2.get); + + assertEquals(remove1, listener1.remove); + assertEquals(remove2, listener2.remove); + + assertEquals(1, listener1.clearCache); + assertEquals(0, listener2.clearCache); + + values = new HashMap() + { + private static final long serialVersionUID = 1L; + + public Set> entrySet() + { + Set> set = new LinkedHashSet>(super.entrySet()); + set.add(new Entry() + { + + public Object setValue(Object paramV) + { + return null; + } + + public Object getValue() + { + throw new RuntimeException("An exception"); + } + + public Serializable getKey() + { + return "c"; + } + }); + return set; + } + }; + values.put(new MyKey("e"), "e"); + values.put(new MyKey("d"), "d"); + cache1.putMap(values); + assertEquals(2, cache1.getCacheSize()); + assertEquals(2, cache2.getCacheSize()); + + assertEquals(put1, listener1.put); + assertEquals(put2, listener2.put); + + assertEquals(6, listener1.get); + assertEquals(6, listener2.get); + + assertEquals(remove1, listener1.remove); + assertEquals(remove2, listener2.remove); + + assertEquals(1, listener1.clearCache); + assertEquals(0, listener2.clearCache); + + assertEquals(0, listener1.expire); + assertEquals(0, listener2.expire); + + cache2.clearCache(); + assertEquals(0, cache1.getCacheSize()); + assertEquals(0, cache2.getCacheSize()); + + assertEquals(put1, listener1.put); + assertEquals(put2, listener2.put); + + assertEquals(6, listener1.get); + assertEquals(6, listener2.get); + + // Since the clear cache map/reduce can only find cache1, + // the remove calls will be applied to cache1 so cache2 + // will be notified on its entries, this is due to the + // hack used to apply modifications within a map/reduce + remove2 += (isALocal ? 0 : 1) + (isBLocal ? 0 : 1); + + assertEquals(remove1, listener1.remove); + assertEquals(remove2, listener2.remove); + + assertEquals(1, listener1.clearCache); + assertEquals(1, listener2.clearCache); + + assertEquals(0, listener1.expire); + assertEquals(0, listener2.expire); + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/gatein/common/transaction/CounterListener.java b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/gatein/common/transaction/CounterListener.java new file mode 100644 index 00000000..09f30e7d --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/java/org/gatein/common/transaction/CounterListener.java @@ -0,0 +1,55 @@ +/* + * JBoss, a division of Red Hat + * Copyright 2012, Red Hat Middleware, LLC, and individual + * contributors as indicated by the @authors tag. See the + * copyright.txt in the distribution for a full listing of + * individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.gatein.common.transaction; + +import java.util.concurrent.atomic.AtomicInteger; + + +/** + * Mock listener + * + * @author Marek Posolda + */ +public class CounterListener implements JTAUserTransactionLifecycleListener { + private AtomicInteger beforeBeginCounter = new AtomicInteger(0); + private AtomicInteger afterBeginCounter = new AtomicInteger(0); + + @Override + public void beforeBegin() { + beforeBeginCounter.incrementAndGet(); + } + + @Override + public void afterBegin() { + afterBeginCounter.incrementAndGet(); + } + + int getBeforeBeginCounter() { + return beforeBeginCounter.get(); + } + + int getAfterBeginCounter() { + return afterBeginCounter.get(); + } +} diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/configuration.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/configuration.xml new file mode 100644 index 00000000..f65a8b47 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/configuration.xml @@ -0,0 +1,38 @@ + + + + + org.exoplatform.services.ispn.DistributedCacheManager + + + infinispan-configuration + jar:/conf/portal/distributed-cache-configuration.xml + + + parameters + The parameters of the configuration + + + + + diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/jgroups/udp.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/jgroups/udp.xml new file mode 100644 index 00000000..6eccfebf --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/jgroups/udp.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/portal/cache-configuration-template.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/portal/cache-configuration-template.xml new file mode 100644 index 00000000..4df9457b --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/portal/cache-configuration-template.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/portal/distributed-cache-configuration-template.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/portal/distributed-cache-configuration-template.xml new file mode 100644 index 00000000..41abb89d --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/portal/distributed-cache-configuration-template.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/portal/distributed-cache-configuration.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/portal/distributed-cache-configuration.xml new file mode 100644 index 00000000..5bec6064 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/portal/distributed-cache-configuration.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/portal/test-configuration.xml b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/portal/test-configuration.xml new file mode 100644 index 00000000..3945a3a6 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/conf/portal/test-configuration.xml @@ -0,0 +1,179 @@ + + + + + + org.exoplatform.services.cache.CacheService + org.exoplatform.services.cache.impl.CacheServiceImpl + + + + cache.config.default + The default cache configuration + + default + 10 + 2 + + + + cache-distributed + The default cache configuration + + cache-distributed + 5 + 2 + true + + + + cache-distributed2 + The default cache configuration + + cache-distributed2 + 5 + 2 + true + + + test-multi-threading + The default cache configuration + + test-multi-threading + NONE + -1 + -1 + + + + cacheDistributed + The default cache configuration + + cacheDistributed + 5 + 2 + true + + + + test-default-impl + The default cache configuration + + test-default-impl + 5 + 2 + + + + test-custom-impl-with-old-config + The default cache configuration + + test-custom-impl-with-old-config + 5 + 2 + TEST + + + + test-custom-impl-with-new-config + The default cache configuration + + test-custom-impl-with-new-config + 5 + 2 + + + + + + org.exoplatform.services.cache.ExoCacheFactory + org.exoplatform.services.cache.impl.infinispan.ExoCacheFactoryImpl + + + cache.config.template + jar:/conf/portal/cache-configuration-template.xml + + + + + + org.exoplatform.services.cache.ExoCacheFactory + + addConfig + addConfig + org.exoplatform.services.cache.impl.infinispan.ExoCacheFactoryConfigPlugin + add Custom Configurations + + + myCustomCache + jar:/conf/portal/distributed-cache-configuration-template.xml + + + myCustomCache-Bis + classpath:/conf/portal/distributed-cache-configuration-template.xml + + + + + addCreator + addCreator + org.exoplatform.services.cache.impl.infinispan.ExoCacheCreatorPlugin + add Exo Cache Creator + + + Test + The cache creator for testing purpose + + + + GENERIC + The generic cache creator + + + + + NONE + + + FIFO + + + LRU + + + UNORDERED + + + LIRS + + + + LRU + -1 + 500 + + + + + + diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/test.policy b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/test.policy new file mode 100644 index 00000000..28ff5d11 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/test.policy @@ -0,0 +1,30 @@ +grant codeBase "@MAVEN_REPO@-"{ + permission java.security.AllPermission; +}; + +grant codeBase "@MAIN_CLASSES@-"{ + permission java.security.AllPermission; +}; + +grant codeBase "@TEST_CLASSES@-"{ +}; + +grant codeBase "@MAIN_CLASSES@../../../exo.kernel.commons.test/-"{ + permission java.security.AllPermission; +}; + +grant codeBase "@MAIN_CLASSES@../../../exo.kernel.commons/-"{ + permission java.security.AllPermission; +}; + +grant codeBase "@MAIN_CLASSES@../../../exo.kernel.container/-"{ + permission java.security.AllPermission; +}; + +grant codeBase "@MAIN_CLASSES@../../../exo.kernel.component.cache/-"{ + permission java.security.AllPermission; +}; + +grant codeBase "@MAIN_CLASSES@../../../exo.kernel.component.ext.cache.impl.infinispan.v8/-"{ + permission java.security.AllPermission; +}; diff --git a/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/tsm-excludes.properties b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/tsm-excludes.properties new file mode 100644 index 00000000..9a241766 --- /dev/null +++ b/exo.kernel.component.ext.cache.impl.infinispan.v8/src/test/resources/tsm-excludes.properties @@ -0,0 +1 @@ +org.exoplatform.services.cache.impl.infinispan.distributed.TestDistributedExoCache.testDistributedCache=stop \ No newline at end of file diff --git a/pom.xml b/pom.xml index a9e95364..53f51a47 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,9 @@ web commons-exo-extension + exo.kernel.component.command + exo.kernel.component.ext.cache.impl.infinispan.v8 + exo.core.component.database scm:git:git://github.com/exoplatform/commons-exo.git @@ -42,41 +45,56 @@ import - org.exoplatform.commons-exo + ${project.groupId} commons-exo.portal.web.eXoSkin ${project.version} war - org.exoplatform.commons-exo + ${project.groupId} commons-exo.portal.web.eXoSkin ${project.version} sources - org.exoplatform.commons-exo + ${project.groupId} commons-exo.portal.web.eXoResources ${project.version} war - org.exoplatform.commons-exo + ${project.groupId} commons-exo.portal.web.eXoResources ${project.version} sources - org.exoplatform.commons-exo + ${project.groupId} commons-exo-extension ${project.version} war - org.exoplatform.commons-exo + ${project.groupId} commons-exo-extension ${project.version} sources + + ${project.groupId} + exo.kernel.component.ext.cache.impl.infinispan.v8 + ${project.version} + + + ${project.groupId} + exo.core.component.database + ${project.version} + + + ${project.groupId} + exo.kernel.component.command + ${project.version} +