Author: bleny Date: 2014-06-03 17:11:41 +0200 (Tue, 03 Jun 2014) New Revision: 1986 Url: http://forge.codelutin.com/projects/wao/repository/revisions/1986 Log: refactor to allow caching Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/WaoApplicationContext.java trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/SamplingPlanCacheKey.java trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoApplicationContext.java trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoServiceContext.java trunk/wao-web/src/main/java/fr/ifremer/wao/web/DefaultWaoApplicationContext.java Removed: trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoServiceContext.java trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoApplicationContext.java Modified: trunk/pom.xml trunk/wao-services/src/main/java/fr/ifremer/wao/services/DefaultWaoServiceContext.java trunk/wao-services/src/main/java/fr/ifremer/wao/services/WaoServiceContext.java trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java trunk/wao-services/src/test/java/fr/ifremer/wao/services/AbstractWaoServiceTest.java trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerContactsServiceTest.java trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanServiceTest.java trunk/wao-web/pom.xml trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoApplicationListener.java trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoInterceptor.java Modified: trunk/pom.xml =================================================================== --- trunk/pom.xml 2014-05-27 15:31:39 UTC (rev 1985) +++ trunk/pom.xml 2014-06-03 15:11:41 UTC (rev 1986) @@ -5,7 +5,7 @@ <parent> <groupId>org.nuiton</groupId> <artifactId>mavenpom4redmine</artifactId> - <version>5.0.7</version> + <version>5.0.8</version> </parent> <groupId>fr.ifremer</groupId> @@ -111,6 +111,7 @@ <commonsEmailVersion>1.3.2</commonsEmailVersion> <mustacheVersion>0.8.15</mustacheVersion> <commonsCodecVersion>1.9</commonsCodecVersion> + <ehCacheVersion>2.6.9</ehCacheVersion> <nuitonWebVersion>1.16</nuitonWebVersion> <nuitonI18nVersion>3.1</nuitonI18nVersion> @@ -370,6 +371,12 @@ <version>${jsoupVersion}</version> </dependency> + <dependency> + <groupId>net.sf.ehcache</groupId> + <artifactId>ehcache-core</artifactId> + <version>${ehCacheVersion}</version> + </dependency> + </dependencies> </dependencyManagement> Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/DefaultWaoServiceContext.java =================================================================== --- trunk/wao-services/src/main/java/fr/ifremer/wao/services/DefaultWaoServiceContext.java 2014-05-27 15:31:39 UTC (rev 1985) +++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/DefaultWaoServiceContext.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -21,47 +21,43 @@ * #L% */ +import com.google.common.cache.Cache; import fr.ifremer.wao.WaoApplicationConfig; import fr.ifremer.wao.WaoTechnicalException; import fr.ifremer.wao.WaoTopiaPersistenceContext; +import fr.ifremer.wao.services.service.ObsMerSamplingPlan; +import fr.ifremer.wao.services.service.SamplingPlanCacheKey; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Date; import java.util.Locale; import java.util.Random; -import java.util.UUID; public class DefaultWaoServiceContext implements WaoServiceContext { - protected WaoApplicationConfig applicationConfig; + protected WaoApplicationContext waoApplicationContext; protected WaoTopiaPersistenceContext persistenceContext; - protected WaoWebApplicationContext webApplicationContext; - - protected Random random; - protected Locale locale; + public DefaultWaoServiceContext(WaoApplicationContext waoApplicationContext, WaoTopiaPersistenceContext persistenceContext, Locale locale) { + this.waoApplicationContext = waoApplicationContext; + this.persistenceContext = persistenceContext; + this.locale = locale; + } + @Override public WaoApplicationConfig getApplicationConfig() { - return applicationConfig; + return waoApplicationContext.getApplicationConfig(); } - public void setPersistenceContext(WaoTopiaPersistenceContext persistenceContext) { - this.persistenceContext = persistenceContext; - } - @Override public WaoTopiaPersistenceContext getPersistenceContext() { return persistenceContext; } - public void setApplicationConfig(WaoApplicationConfig applicationConfig) { - this.applicationConfig = applicationConfig; - } - @Override public <E extends WaoService> E newService(Class<E> serviceClass) { @@ -99,38 +95,32 @@ @Override public Random getRandom() { - if (random == null) { - random = new Random(); - } - return random; + return waoApplicationContext.getRandom(); } @Override public Date getNow() { - Date now = new Date(); - return now; + return waoApplicationContext.getNow(); } @Override public WaoWebApplicationContext getWebApplicationContext() { - return webApplicationContext; + return waoApplicationContext.getWebApplicationContext(); } - public void setWebApplicationContext(WaoWebApplicationContext webApplicationContext) { - this.webApplicationContext = webApplicationContext; - } - + @Override public Locale getLocale() { return locale; } - public void setLocale(Locale locale) { - this.locale = locale; + @Override + public String newUuid() { + return waoApplicationContext.newUuid(); } @Override - public String newUuid() { - return UUID.randomUUID().toString(); + public Cache<SamplingPlanCacheKey, ObsMerSamplingPlan> getSamplingPlansCache() { + return waoApplicationContext.getSamplingPlansCache(); } } Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/WaoApplicationContext.java =================================================================== --- trunk/wao-services/src/main/java/fr/ifremer/wao/services/WaoApplicationContext.java (rev 0) +++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/WaoApplicationContext.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -0,0 +1,36 @@ +package fr.ifremer.wao.services; + +import com.google.common.cache.Cache; +import fr.ifremer.wao.WaoApplicationConfig; +import fr.ifremer.wao.WaoTopiaPersistenceContext; +import fr.ifremer.wao.services.service.ObsMerSamplingPlan; +import fr.ifremer.wao.services.service.SamplingPlanCacheKey; + +import java.util.Date; +import java.util.Locale; +import java.util.Random; + +/** + * @author bleny + */ +public interface WaoApplicationContext { + + WaoApplicationConfig getApplicationConfig(); + + String newUuid(); + + Date getNow(); + + Random getRandom(); + + WaoTopiaPersistenceContext newPersistenceContext(); + + WaoServiceContext newServiceContext(WaoTopiaPersistenceContext persistenceContext, Locale locale); + + WaoWebApplicationContext getWebApplicationContext(); + + Cache<SamplingPlanCacheKey,ObsMerSamplingPlan> getSamplingPlansCache(); + + void close(); + +} Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/WaoServiceContext.java =================================================================== --- trunk/wao-services/src/main/java/fr/ifremer/wao/services/WaoServiceContext.java 2014-05-27 15:31:39 UTC (rev 1985) +++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/WaoServiceContext.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -21,8 +21,11 @@ * #L% */ +import com.google.common.cache.Cache; import fr.ifremer.wao.WaoApplicationConfig; import fr.ifremer.wao.WaoTopiaPersistenceContext; +import fr.ifremer.wao.services.service.ObsMerSamplingPlan; +import fr.ifremer.wao.services.service.SamplingPlanCacheKey; import java.util.Date; import java.util.Locale; @@ -37,6 +40,8 @@ WaoTopiaPersistenceContext getPersistenceContext(); + Cache<SamplingPlanCacheKey, ObsMerSamplingPlan> getSamplingPlansCache(); + Date getNow(); <E extends WaoService> E newService(Class<E> serviceClass); Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java =================================================================== --- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java 2014-05-27 15:31:39 UTC (rev 1985) +++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -24,6 +24,7 @@ import com.google.common.base.Charsets; import com.google.common.base.Optional; import com.google.common.base.Preconditions; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -400,46 +401,65 @@ AuthenticatedWaoUser authenticatedWaoUser, SampleRowsFilter sampleRowsFilter) { - SampleRowTopiaDao dao = getSampleRowDao(); - - // recuperation des lignes du plan - List<SampleRow> sampleRows = dao.findAll(sampleRowsFilter); - - // creation du plan d'echantillonnage Optional<String> optionalCompanyId = Optional.absent(); if (authenticatedWaoUser.isCoordinatorOrObserver()) { optionalCompanyId = Optional.of(authenticatedWaoUser.getCompany().getTopiaId()); } - ObsMerSamplingPlanBuilder builder = new ObsMerSamplingPlanBuilder(serviceContext.getLocale(), - optionalCompanyId, - sampleRowsFilter); - // begin of month - Date periodFrom = sampleRowsFilter.getPeriodFrom(); - // end of month - Date periodTo = WaoUtils.getEndOfMonth(sampleRowsFilter.getPeriodTo()); + SamplingPlanCacheKey samplingPlanCacheKey = + new SamplingPlanCacheKey( + serviceContext.getLocale(), + optionalCompanyId, + sampleRowsFilter); - for (SampleRow sampleRow : sampleRows) { + Cache<SamplingPlanCacheKey, ObsMerSamplingPlan> samplingPlansCache = + serviceContext.getSamplingPlansCache(); - // calcul effort plannifie - Double observationTimesInDaysExpected = - getObservationTimesInDayExpected(periodFrom, - periodTo, - sampleRow); + ObsMerSamplingPlan result = samplingPlansCache.getIfPresent(samplingPlanCacheKey); - Pair<Long, Long> realAndEstimated = - getSampleRowObservationTimesInDayRealAndEstimated( - sampleRow.getTopiaId(), periodFrom, periodTo); - Long observationTimesInDaysReal = realAndEstimated.getLeft(); - Long observationTimesInDaysEstimated = realAndEstimated.getRight(); + if (result == null) { - // ajout de la ligne au build de plan - builder.addSampleRow(sampleRow, - observationTimesInDaysExpected, - observationTimesInDaysReal, - observationTimesInDaysEstimated); + SampleRowTopiaDao dao = getSampleRowDao(); + + // recuperation des lignes du plan + List<SampleRow> sampleRows = dao.findAll(sampleRowsFilter); + + // creation du plan d'echantillonnage + ObsMerSamplingPlanBuilder builder = new ObsMerSamplingPlanBuilder( + serviceContext.getLocale(), + optionalCompanyId, + sampleRowsFilter); + + // begin of month + Date periodFrom = sampleRowsFilter.getPeriodFrom(); + // end of month + Date periodTo = WaoUtils.getEndOfMonth(sampleRowsFilter.getPeriodTo()); + + for (SampleRow sampleRow : sampleRows) { + + // calcul effort plannifie + Double observationTimesInDaysExpected = + getObservationTimesInDayExpected(periodFrom, + periodTo, + sampleRow); + + Pair<Long, Long> realAndEstimated = + getSampleRowObservationTimesInDayRealAndEstimated( + sampleRow.getTopiaId(), periodFrom, periodTo); + Long observationTimesInDaysReal = realAndEstimated.getLeft(); + Long observationTimesInDaysEstimated = realAndEstimated.getRight(); + + // ajout de la ligne au build de plan + builder.addSampleRow(sampleRow, + observationTimesInDaysExpected, + observationTimesInDaysReal, + observationTimesInDaysEstimated); + } + result = builder.toPlan(); + + samplingPlansCache.put(samplingPlanCacheKey, result); + } - ObsMerSamplingPlan result = builder.toPlan(); return result; Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/SamplingPlanCacheKey.java =================================================================== --- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/SamplingPlanCacheKey.java (rev 0) +++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/SamplingPlanCacheKey.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -0,0 +1,40 @@ +package fr.ifremer.wao.services.service; + +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import fr.ifremer.wao.SampleRowsFilter; + +import java.io.Serializable; +import java.util.Locale; + +public class SamplingPlanCacheKey implements Serializable { + + protected Locale locale; + + protected Optional<String> optionalCompanyId; + + protected SampleRowsFilter sampleRowsFilter; + + public SamplingPlanCacheKey(Locale locale, Optional<String> optionalCompanyId, SampleRowsFilter sampleRowsFilter) { + this.locale = locale; + this.optionalCompanyId = optionalCompanyId; + this.sampleRowsFilter = sampleRowsFilter; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SamplingPlanCacheKey that = (SamplingPlanCacheKey) o; + boolean equals = Objects.equal(locale, that.locale) + && Objects.equal(optionalCompanyId, that.optionalCompanyId) + && Objects.equal(sampleRowsFilter, that.sampleRowsFilter); + return equals; + } + + @Override + public int hashCode() { + return Objects.hashCode(locale, optionalCompanyId, sampleRowsFilter); + } + +} Modified: trunk/wao-services/src/test/java/fr/ifremer/wao/services/AbstractWaoServiceTest.java =================================================================== --- trunk/wao-services/src/test/java/fr/ifremer/wao/services/AbstractWaoServiceTest.java 2014-05-27 15:31:39 UTC (rev 1985) +++ trunk/wao-services/src/test/java/fr/ifremer/wao/services/AbstractWaoServiceTest.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -21,189 +21,44 @@ * #L% */ -import fr.ifremer.wao.WaoApplicationConfig; -import fr.ifremer.wao.WaoTechnicalException; -import fr.ifremer.wao.WaoTopiaApplicationContext; import fr.ifremer.wao.WaoTopiaPersistenceContext; -import fr.ifremer.wao.services.service.InitWaoService; -import org.apache.commons.lang3.SystemUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.hibernate.cfg.Environment; import org.junit.After; -import org.nuiton.util.DateUtil; -import org.nuiton.util.FileUtil; -import java.io.File; -import java.io.IOException; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.util.Locale; import java.util.UUID; public class AbstractWaoServiceTest { private static final Log log = LogFactory.getLog(AbstractWaoServiceTest.class); - protected static final double DELTA = 0.0001; - - protected static final String DATABASE_REF = "DataBaseRef"; - - protected static WaoApplicationConfig applicationConfig; - protected FakeWaoServiceContext serviceContext; - protected WaoTopiaApplicationContext applicationContext; + protected FakeWaoApplicationContext applicationContext; - protected List<WaoTopiaPersistenceContext> openedTransactions = new LinkedList<>(); - - protected WaoTopiaApplicationContext newApplicationContext(String dataBase) { - - Map<String, String> hibernateH2Config = new HashMap<>(); - - hibernateH2Config.putAll(getApplicationConfig().getTopiaProperties()); - - hibernateH2Config.put(Environment.DRIVER, org.h2.Driver.class.getName()); - hibernateH2Config.put(Environment.DIALECT, org.hibernate.dialect.H2Dialect.class.getName()); - hibernateH2Config.put(Environment.USER, "sa"); - hibernateH2Config.put(Environment.PASS, ""); - - File tempDirFile = SystemUtils.getJavaIoTmpDir(); - - File databaseFile = new File(tempDirFile, dataBase); - - String h2dataPath = databaseFile.getAbsolutePath() + File.separator + "h2data"; - - String jdbcUrl = "jdbc:h2:file:" + h2dataPath; - - hibernateH2Config.put(Environment.URL, jdbcUrl); - - if (log.isTraceEnabled()) { - log.trace("will store H2 data in " + h2dataPath); - log.trace("allJpaParameters = " + hibernateH2Config); - } - - if (log.isDebugEnabled()) { - log.debug("jdbc url is\n" + jdbcUrl); - } - - WaoTopiaApplicationContext applicationContext = new WaoTopiaApplicationContext(hibernateH2Config); - - if (log.isTraceEnabled()) { - log.trace("created root context " + applicationContext); - } - - return applicationContext; - } - protected boolean isDatabaseWithReferential() { return false; } - protected WaoTopiaApplicationContext getApplicationContext() { + protected FakeWaoApplicationContext getApplicationContext() { - String databaseName = UUID.randomUUID().toString(); - - if (isDatabaseWithReferential()) { - - File tempDirFile = SystemUtils.getJavaIoTmpDir(); - - File databaseRefFile = new File(tempDirFile, DATABASE_REF); - - if (!databaseRefFile.exists()) { - - if (log.isTraceEnabled()) { - log.trace("create referential data base"); - } - - WaoTopiaApplicationContext applicationContext = newApplicationContext(DATABASE_REF); - - WaoTopiaPersistenceContext persistenceContext; - - persistenceContext = applicationContext.newPersistenceContext(); - - if (log.isTraceEnabled()) { - log.trace("opened transaction " + persistenceContext); - } - - FakeWaoServiceContext serviceContext = new FakeWaoServiceContext(); - - serviceContext.setDate(DateUtil.createDate(1, 11, 2009)); - - serviceContext.setApplicationConfig(getApplicationConfig()); - - serviceContext.setPersistenceContext(persistenceContext); - - InitWaoService initWaoService = serviceContext.newService(InitWaoService.class); - - initWaoService.init(); - - if (log.isTraceEnabled()) { - log.trace("closing transaction " + persistenceContext); - } - - persistenceContext.closeContext(); - - if (log.isTraceEnabled()) { - log.trace("closing transaction " + applicationContext); - } - - applicationContext.closeContext(); - - File lockFile = new File(databaseRefFile, "h2data.lock.db"); - - while (lockFile.exists()) { - if (log.isDebugEnabled()) { - log.debug("referential database is still locked, will wait 5 more seconds"); - } - try { - Thread.sleep(5 * 1000); - } catch (InterruptedException e) { - throw new WaoTechnicalException("can't wait", e); - } - } - - } - - File databaseFile = new File(tempDirFile, databaseName); - - if (log.isTraceEnabled()) { - log.trace("Copy referential database to " + databaseFile.getName()); - } - - try { - FileUtil.copyAndRenameRecursively(databaseRefFile, databaseFile, false, null, null, false); - } catch (IOException e) { - throw new WaoTechnicalException("can't copy database Ref", e); - } - - if (log.isTraceEnabled()) { - log.trace("Copy referential database : Complete"); - } + if (applicationContext == null) { + applicationContext = new FakeWaoApplicationContext(UUID.randomUUID().toString(), isDatabaseWithReferential()); } - return newApplicationContext(databaseName); + return applicationContext; + } protected WaoTopiaPersistenceContext newPersistenceContext() { - if (applicationContext == null) { + WaoTopiaPersistenceContext persistenceContext = getApplicationContext().newPersistenceContext(); - applicationContext = getApplicationContext(); - - } - - WaoTopiaPersistenceContext persistenceContext; - - persistenceContext = applicationContext.newPersistenceContext(); - if (log.isTraceEnabled()) { log.trace("opened transaction " + persistenceContext); } - openedTransactions.add(persistenceContext); - return persistenceContext; } @@ -211,52 +66,24 @@ @After public void tearDown() { - for (WaoTopiaPersistenceContext openedTransaction : openedTransactions) { - - if (log.isTraceEnabled()) { - log.trace("closing transaction " + openedTransaction); - } - - openedTransaction.closeContext(); - - } - if (applicationContext != null) { if (log.isTraceEnabled()) { log.trace("closing transaction " + applicationContext); } - applicationContext.closeContext(); + applicationContext.close(); } } - protected static WaoApplicationConfig getApplicationConfig() { - - if (applicationConfig == null) { - - applicationConfig = new WaoApplicationConfig(); - - } - - return applicationConfig; - - } - protected FakeWaoServiceContext newServiceContext() { - FakeWaoServiceContext serviceContext = new FakeWaoServiceContext(); + WaoTopiaPersistenceContext persistenceContext = getApplicationContext().newPersistenceContext(); - serviceContext.setApplicationConfig(getApplicationConfig()); + serviceContext = getApplicationContext().newServiceContext(persistenceContext, Locale.FRANCE); - WaoTopiaPersistenceContext persistenceContext = newPersistenceContext(); - - serviceContext.setPersistenceContext(persistenceContext); - - serviceContext.setDate(DateUtil.createDate(2, 11, 2009)); - return serviceContext; } Added: trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoApplicationContext.java =================================================================== --- trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoApplicationContext.java (rev 0) +++ trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoApplicationContext.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -0,0 +1,246 @@ +package fr.ifremer.wao.services; + +import com.google.common.base.Preconditions; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import fr.ifremer.wao.WaoApplicationConfig; +import fr.ifremer.wao.WaoTechnicalException; +import fr.ifremer.wao.WaoTopiaApplicationContext; +import fr.ifremer.wao.WaoTopiaPersistenceContext; +import fr.ifremer.wao.services.service.InitWaoService; +import fr.ifremer.wao.services.service.ObsMerSamplingPlan; +import fr.ifremer.wao.services.service.SamplingPlanCacheKey; +import org.apache.commons.lang3.SystemUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.cfg.Environment; +import org.nuiton.util.DateUtil; +import org.nuiton.util.FileUtil; + +import java.io.File; +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.UUID; + +public class FakeWaoApplicationContext implements WaoApplicationContext { + + private static final Log log = LogFactory.getLog(FakeWaoApplicationContext.class); + + protected static final String DATABASE_REF = "DataBaseRef"; + + protected String context; + + protected boolean initWithReferential; + + protected WaoTopiaApplicationContext topiaApplicationContext; + + protected WaoApplicationConfig applicationConfig; + + protected Random random = new Random(540727613); + + protected Date date = DateUtil.createDate(1, 11, 2009); + + protected Cache<SamplingPlanCacheKey, ObsMerSamplingPlan> samplingPlansCache; + + public FakeWaoApplicationContext(String context, boolean initWithReferential) { + this.context = context; + this.initWithReferential = initWithReferential; + } + + @Override + public WaoApplicationConfig getApplicationConfig() { + if (applicationConfig == null) { + applicationConfig = new WaoApplicationConfig(); + } + return applicationConfig; + } + + public WaoTopiaApplicationContext getTopiaApplicationContext() { + + if (topiaApplicationContext == null) { + + if (initWithReferential) { + + File databaseRefFile = getDatabaseFile(DATABASE_REF); + + if (!databaseRefFile.exists()) { + + if (log.isTraceEnabled()) { + log.trace("create referential database"); + } + + WaoTopiaApplicationContext applicationContext = newWaoTopiaApplicationContext(DATABASE_REF); + + WaoTopiaPersistenceContext persistenceContext = applicationContext.newPersistenceContext(); + + if (log.isTraceEnabled()) { + log.trace("opened persistence context " + persistenceContext); + } + + WaoServiceContext serviceContext = new DefaultWaoServiceContext(this, persistenceContext, Locale.FRANCE); + + InitWaoService initWaoService = serviceContext.newService(InitWaoService.class); + + initWaoService.init(); + + if (log.isTraceEnabled()) { + log.trace("closing persistence context " + persistenceContext); + } + + persistenceContext.closeContext(); + + if (log.isTraceEnabled()) { + log.trace("closing application context " + applicationContext); + } + + applicationContext.closeContext(); + + File lockFile = new File(databaseRefFile, "h2data.lock.db"); + + while (lockFile.exists()) { + if (log.isDebugEnabled()) { + log.debug("referential database is still locked, will wait 5 more seconds"); + } + try { + Thread.sleep(5 * 1000); + } catch (InterruptedException e) { + throw new WaoTechnicalException("can't wait", e); + } + } + + } + + File databaseFile = getDatabaseFile(context); + + if (log.isTraceEnabled()) { + log.trace("copy referential database from " + databaseRefFile.getAbsolutePath() + " to " + databaseFile.getAbsolutePath()); + } + + try { + FileUtil.copyAndRenameRecursively(databaseRefFile, databaseFile, false, null, null, false); + } catch (IOException e) { + throw new WaoTechnicalException("cannot copy database from " + databaseRefFile.getAbsolutePath() + " to " + databaseFile.getAbsolutePath(), e); + } + + if (log.isTraceEnabled()) { + log.trace("copy referential database completed"); + } + } + + WaoTopiaApplicationContext applicationContext = newWaoTopiaApplicationContext(context); + + if (log.isTraceEnabled()) { + log.trace("created application context " + applicationContext); + } + + return applicationContext; + } + + return topiaApplicationContext; + + } + + protected WaoTopiaApplicationContext newWaoTopiaApplicationContext(String context) { + + Map<String, String> hibernateH2Config = new HashMap<>(); + + hibernateH2Config.putAll(getApplicationConfig().getTopiaProperties()); + + hibernateH2Config.put(Environment.DRIVER, org.h2.Driver.class.getName()); + hibernateH2Config.put(Environment.DIALECT, org.hibernate.dialect.H2Dialect.class.getName()); + hibernateH2Config.put(Environment.USER, "sa"); + hibernateH2Config.put(Environment.PASS, ""); + + File databaseFile = getDatabaseFile(context); + + String h2dataPath = databaseFile.getAbsolutePath() + File.separator + "h2data"; + + String jdbcUrl = "jdbc:h2:file:" + h2dataPath; + + hibernateH2Config.put(Environment.URL, jdbcUrl); + + if (log.isTraceEnabled()) { + log.trace("will store H2 data in " + h2dataPath); + log.trace("allJpaParameters = " + hibernateH2Config); + } + + if (log.isDebugEnabled()) { + log.debug("jdbc url is\n" + jdbcUrl); + } + + return new WaoTopiaApplicationContext(hibernateH2Config); + + } + + protected File getDatabaseFile(String context) { + File tempDirFile = SystemUtils.getJavaIoTmpDir(); + return new File(tempDirFile, context); + } + + @Override + public Date getNow() { + Preconditions.checkState(date != null, "you must provide a date before running service test"); + if (log.isTraceEnabled()) { + log.trace("injecting fake date in service: " + date); + } + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + @Override + public String newUuid() { + return UUID.randomUUID().toString(); + } + + @Override + public WaoWebApplicationContext getWebApplicationContext() { + throw new UnsupportedOperationException(); + } + + @Override + public Random getRandom() { + return random; + } + + @Override + public Cache<SamplingPlanCacheKey, ObsMerSamplingPlan> getSamplingPlansCache() { + if (samplingPlansCache == null) { + samplingPlansCache = CacheBuilder.newBuilder().build(); + } + return samplingPlansCache; + } + + @Override + public WaoTopiaPersistenceContext newPersistenceContext() { + + WaoTopiaPersistenceContext persistenceContext = getTopiaApplicationContext().newPersistenceContext(); + + return persistenceContext; + + } + + @Override + public FakeWaoServiceContext newServiceContext(WaoTopiaPersistenceContext persistenceContext, Locale locale) { + + FakeWaoServiceContext newServiceContext = + new FakeWaoServiceContext(this, persistenceContext, locale); + + return newServiceContext; + + } + + @Override + public void close() { + if (topiaApplicationContext != null) { + topiaApplicationContext.closeContext(); + } + } + +} Deleted: trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoServiceContext.java =================================================================== --- trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoServiceContext.java 2014-05-27 15:31:39 UTC (rev 1985) +++ trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoServiceContext.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -1,69 +0,0 @@ -package fr.ifremer.wao.services; - -/* - * #%L - * Wao :: Services - * %% - * Copyright (C) 2009 - 2014 Ifremer - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * #L% - */ - -import com.google.common.base.Preconditions; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.util.Date; -import java.util.Locale; -import java.util.Random; - -public class FakeWaoServiceContext extends DefaultWaoServiceContext { - - private static final Log log = LogFactory.getLog(FakeWaoServiceContext.class); - - protected Date date; - - public FakeWaoServiceContext() { - // default locale - setLocale(Locale.FRANCE); - } - - @Override - public Random getRandom() { - if (random == null) { - random = new Random(540727613); - } - return random; - } - - @Override - public Date getNow() { - Preconditions.checkState(date != null, "you must provide a date before running service test"); - if (log.isTraceEnabled()) { - log.trace("injecting fake date in service: " + date); - } - return date; - } - - public void setDate(Date date) { - this.date = date; - } - -// @Override -// public Locale getLocale() { -// return Locale.FRANCE; -// } - -} Added: trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoServiceContext.java =================================================================== --- trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoServiceContext.java (rev 0) +++ trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoServiceContext.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -0,0 +1,17 @@ +package fr.ifremer.wao.services; + +import fr.ifremer.wao.WaoTopiaPersistenceContext; + +import java.util.Locale; + +public class FakeWaoServiceContext extends DefaultWaoServiceContext { + + public FakeWaoServiceContext(WaoApplicationContext waoApplicationContext, WaoTopiaPersistenceContext persistenceContext, Locale locale) { + super(waoApplicationContext, persistenceContext, locale); + } + + public void setLocale(Locale locale) { + this.locale = locale; + } + +} Modified: trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerContactsServiceTest.java =================================================================== --- trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerContactsServiceTest.java 2014-05-27 15:31:39 UTC (rev 1985) +++ trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerContactsServiceTest.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -79,7 +79,7 @@ fixtures.navires(); // today must be after the observation end date - serviceContext.setDate(DateUtil.createDate(1, 2, 2011)); + applicationContext.setDate(DateUtil.createDate(1, 2, 2011)); InputStream input = null; try { Modified: trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanServiceTest.java =================================================================== --- trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanServiceTest.java 2014-05-27 15:31:39 UTC (rev 1985) +++ trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanServiceTest.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -88,7 +88,7 @@ fixtures.samplingPlan(); - serviceContext.setDate(DateUtil.createDate(15, 5, 2010)); + applicationContext.setDate(DateUtil.createDate(15, 5, 2010)); SampleRowsFilter filter = service.newSampleRowsFilter(fixtures.admin()); ObsMerSamplingPlan samplingPlan = service.getSamplingPlan(fixtures.admin(), filter); @@ -129,7 +129,7 @@ fixtures.samplingPlan(); - serviceContext.setDate(DateUtil.createDate(15, 5, 2010)); + applicationContext.setDate(DateUtil.createDate(15, 5, 2010)); SampleRowsFilter filter = service.newSampleRowsFilter(fixtures.admin()); @@ -164,7 +164,7 @@ FakeWaoServiceContext serviceContext = newServiceContext(); // today must be after the observation end date (for contacts import) - serviceContext.setDate(DateUtil.createDate(1, 2, 2011)); + applicationContext.setDate(DateUtil.createDate(1, 2, 2011)); fixtures = new ObsMerFixtures(serviceContext); fixtures.contacts(); Modified: trunk/wao-web/pom.xml =================================================================== --- trunk/wao-web/pom.xml 2014-05-27 15:31:39 UTC (rev 1985) +++ trunk/wao-web/pom.xml 2014-06-03 15:11:41 UTC (rev 1986) @@ -163,6 +163,11 @@ <artifactId>jsoup</artifactId> </dependency> + <dependency> + <groupId>net.sf.ehcache</groupId> + <artifactId>ehcache-core</artifactId> + </dependency> + </dependencies> <build> Copied: trunk/wao-web/src/main/java/fr/ifremer/wao/web/DefaultWaoApplicationContext.java (from rev 1985, trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoApplicationContext.java) =================================================================== --- trunk/wao-web/src/main/java/fr/ifremer/wao/web/DefaultWaoApplicationContext.java (rev 0) +++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/DefaultWaoApplicationContext.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -0,0 +1,200 @@ +package fr.ifremer.wao.web; + +/* + * #%L + * Wao :: Web + * %% + * Copyright (C) 2009 - 2014 Ifremer + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.opensymphony.xwork2.util.LocalizedTextUtil; +import fr.ifremer.wao.WaoApplicationConfig; +import fr.ifremer.wao.WaoTopiaApplicationContext; +import fr.ifremer.wao.WaoTopiaPersistenceContext; +import fr.ifremer.wao.services.DefaultWaoServiceContext; +import fr.ifremer.wao.services.WaoApplicationContext; +import fr.ifremer.wao.services.WaoServiceContext; +import fr.ifremer.wao.services.WaoWebApplicationContext; +import fr.ifremer.wao.services.service.InitWaoService; +import fr.ifremer.wao.services.service.ObsMerSamplingPlan; +import fr.ifremer.wao.services.service.SamplingPlanCacheKey; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.LogManager; +import org.apache.log4j.PropertyConfigurator; +import org.nuiton.i18n.I18n; +import org.nuiton.i18n.init.DefaultI18nInitializer; +import org.nuiton.i18n.init.I18nInitializer; + +import java.io.File; +import java.util.Date; +import java.util.Locale; +import java.util.Random; +import java.util.UUID; + +public class DefaultWaoApplicationContext implements WaoApplicationContext { + + private static Log log = LogFactory.getLog(DefaultWaoApplicationContext.class); + + public static final String APPLICATION_CONTEXT_PARAMETER = "WaoApplicationContext"; + + protected WaoTopiaApplicationContext topiaApplicationContext; + + protected WaoApplicationConfig applicationConfig; + + protected WaoWebApplicationContext webApplicationContext; + + protected Random random; + + protected Cache<SamplingPlanCacheKey, ObsMerSamplingPlan> samplingPlansCache; + + @Override + public WaoApplicationConfig getApplicationConfig() { + if (applicationConfig == null) { + applicationConfig = new WaoApplicationConfig(); + } + return applicationConfig; + } + + public WaoTopiaApplicationContext getTopiaApplicationContext() { + if (topiaApplicationContext == null) { + topiaApplicationContext = new WaoTopiaApplicationContext(getApplicationConfig().getTopiaProperties()); + } + return topiaApplicationContext; + } + + @Override + public String newUuid() { + return UUID.randomUUID().toString(); + } + + @Override + public WaoWebApplicationContext getWebApplicationContext() { + if (webApplicationContext == null) { + webApplicationContext = new DefaultWaoWebApplicationContext(getApplicationConfig().getInstanceUrl()); + } + return webApplicationContext; + } + + @Override + public Date getNow() { + Date now = new Date(); + return now; + } + + @Override + public Random getRandom() { + if (random == null) { + random = new Random(); + } + return random; + } + + @Override + public Cache<SamplingPlanCacheKey, ObsMerSamplingPlan> getSamplingPlansCache() { + if (samplingPlansCache == null) { + samplingPlansCache = CacheBuilder.newBuilder().build(); + } + return samplingPlansCache; + } + + @Override + public WaoTopiaPersistenceContext newPersistenceContext() { + + WaoTopiaPersistenceContext persistenceContext = getTopiaApplicationContext().newPersistenceContext(); + + return persistenceContext; + + } + + @Override + public WaoServiceContext newServiceContext(WaoTopiaPersistenceContext persistenceContext, Locale locale) { + + DefaultWaoServiceContext newServiceContext = + new DefaultWaoServiceContext(this, persistenceContext, locale); + + return newServiceContext; + + } + + @Override + public void close() { + + if (topiaApplicationContext != null) { + + topiaApplicationContext.closeContext(); + + } + + } + + public void init() { + + if (getApplicationConfig().isLogConfigurationProvided()) { + + File log4jConfigurationFile = getApplicationConfig().getLogConfigurationFile(); + + String log4jConfigurationFileAbsolutePath = log4jConfigurationFile.getAbsolutePath(); + + if (log4jConfigurationFile.exists()) { + + if (log.isInfoEnabled()) { + log.info("will use logging configuration " + log4jConfigurationFileAbsolutePath); + } + + // reset logger configuration + LogManager.resetConfiguration(); + + // use generate log config file + PropertyConfigurator.configure(log4jConfigurationFileAbsolutePath); + + log = LogFactory.getLog(DefaultWaoApplicationContext.class); + + } else { + if (log.isWarnEnabled()) { + log.warn("there is no file " + log4jConfigurationFileAbsolutePath + ". Default logging configuration will be used."); + } + } + + } else { + log.info("will use default logging configuration"); + } + + I18nInitializer initializer = new DefaultI18nInitializer("wao"); + // to show none translated sentences + initializer.setMissingKeyReturnNull(true); + + I18n.init(initializer, Locale.FRANCE); + + LocalizedTextUtil.addDefaultResourceBundle("i18n.wao-web"); + + WaoTopiaPersistenceContext persistenceContext = newPersistenceContext(); + + WaoServiceContext serviceContext = newServiceContext(persistenceContext, Locale.FRANCE); + + InitWaoService initWaoService = + serviceContext.newService(InitWaoService.class); + + initWaoService.init(); + + persistenceContext.closeContext(); + + } + +} Deleted: trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoApplicationContext.java =================================================================== --- trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoApplicationContext.java 2014-05-27 15:31:39 UTC (rev 1985) +++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoApplicationContext.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -1,160 +0,0 @@ -package fr.ifremer.wao.web; - -/* - * #%L - * Wao :: Web - * %% - * Copyright (C) 2009 - 2014 Ifremer - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * #L% - */ - -import com.opensymphony.xwork2.util.LocalizedTextUtil; -import fr.ifremer.wao.WaoApplicationConfig; -import fr.ifremer.wao.WaoTopiaApplicationContext; -import fr.ifremer.wao.WaoTopiaPersistenceContext; -import fr.ifremer.wao.services.DefaultWaoServiceContext; -import fr.ifremer.wao.services.WaoServiceContext; -import fr.ifremer.wao.services.WaoWebApplicationContext; -import fr.ifremer.wao.services.service.InitWaoService; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.LogManager; -import org.apache.log4j.PropertyConfigurator; -import org.nuiton.i18n.I18n; -import org.nuiton.i18n.init.DefaultI18nInitializer; -import org.nuiton.i18n.init.I18nInitializer; - -import java.io.File; -import java.util.Locale; - -public class WaoApplicationContext { - - private static Log log = LogFactory.getLog(WaoApplicationContext.class); - - public static final String APPLICATION_CONTEXT_PARAMETER = "WaoApplicationContext"; - - protected static WaoTopiaApplicationContext topiaApplicationContext; - - protected static WaoApplicationConfig applicationConfig; - - public static WaoApplicationConfig getApplicationConfig() { - if (applicationConfig == null) { - applicationConfig = new WaoApplicationConfig(); - } - return applicationConfig; - } - - public static WaoTopiaApplicationContext getTopiaApplicationContext() { - if (topiaApplicationContext == null) { - topiaApplicationContext = new WaoTopiaApplicationContext(getApplicationConfig().getTopiaProperties()); - } - return topiaApplicationContext; - } - - public WaoTopiaPersistenceContext newPersistenceContext() { - - WaoTopiaPersistenceContext persistenceContext = getTopiaApplicationContext().newPersistenceContext(); - - return persistenceContext; - - } - - public WaoServiceContext newServiceContext(WaoTopiaPersistenceContext persistenceContext, Locale locale) { - - DefaultWaoServiceContext newServiceContext = - new DefaultWaoServiceContext(); - - WaoApplicationConfig applicationConfig = getApplicationConfig(); - - newServiceContext.setApplicationConfig(applicationConfig); - - newServiceContext.setPersistenceContext(persistenceContext); - - WaoWebApplicationContext webApplicationContext = - new DefaultWaoWebApplicationContext(applicationConfig.getInstanceUrl()); - - newServiceContext.setWebApplicationContext(webApplicationContext); - - newServiceContext.setLocale(locale); - - return newServiceContext; - - } - - public void close() { - - if (topiaApplicationContext != null) { - - topiaApplicationContext.closeContext(); - - } - - } - - public void init() { - - if (getApplicationConfig().isLogConfigurationProvided()) { - - File log4jConfigurationFile = getApplicationConfig().getLogConfigurationFile(); - - String log4jConfigurationFileAbsolutePath = log4jConfigurationFile.getAbsolutePath(); - - if (log4jConfigurationFile.exists()) { - - if (log.isInfoEnabled()) { - log.info("will use logging configuration " + log4jConfigurationFileAbsolutePath); - } - - // reset logger configuration - LogManager.resetConfiguration(); - - // use generate log config file - PropertyConfigurator.configure(log4jConfigurationFileAbsolutePath); - - log = LogFactory.getLog(WaoApplicationContext.class); - - } else { - if (log.isWarnEnabled()) { - log.warn("there is no file " + log4jConfigurationFileAbsolutePath + ". Default logging configuration will be used."); - } - } - - } else { - log.info("will use default logging configuration"); - } - - I18nInitializer initializer = new DefaultI18nInitializer("wao"); - // to show none translated sentences - initializer.setMissingKeyReturnNull(true); - - I18n.init(initializer, Locale.FRANCE); - - LocalizedTextUtil.addDefaultResourceBundle("i18n.wao-web"); - - WaoTopiaPersistenceContext persistenceContext = newPersistenceContext(); - - WaoServiceContext serviceContext = newServiceContext(persistenceContext, Locale.FRANCE); - - InitWaoService initWaoService = - serviceContext.newService(InitWaoService.class); - - initWaoService.init(); - - persistenceContext.closeContext(); - - } - -} Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoApplicationListener.java =================================================================== --- trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoApplicationListener.java 2014-05-27 15:31:39 UTC (rev 1985) +++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoApplicationListener.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -31,7 +31,7 @@ private static final Log log = LogFactory.getLog(WaoApplicationListener.class); - protected WaoApplicationContext applicationContext; + protected DefaultWaoApplicationContext applicationContext; @Override public void contextInitialized(ServletContextEvent sce) { @@ -40,12 +40,12 @@ log.info("init WAO"); } - applicationContext = new WaoApplicationContext(); + applicationContext = new DefaultWaoApplicationContext(); applicationContext.init(); sce.getServletContext().setAttribute( - WaoApplicationContext.APPLICATION_CONTEXT_PARAMETER, + DefaultWaoApplicationContext.APPLICATION_CONTEXT_PARAMETER, applicationContext); } Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoInterceptor.java =================================================================== --- trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoInterceptor.java 2014-05-27 15:31:39 UTC (rev 1985) +++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoInterceptor.java 2014-06-03 15:11:41 UTC (rev 1986) @@ -33,6 +33,7 @@ import fr.ifremer.wao.entity.UserRole; import fr.ifremer.wao.entity.WaoUser; import fr.ifremer.wao.services.AuthenticatedWaoUser; +import fr.ifremer.wao.services.WaoApplicationContext; import fr.ifremer.wao.services.WaoService; import fr.ifremer.wao.services.WaoServiceContext; import fr.ifremer.wao.web.action.authentication.LoginAction; @@ -198,11 +199,11 @@ protected WaoApplicationContext getWaoApplicationContext(ActionInvocation invocation) { - WaoApplicationContext applicationContext = - (WaoApplicationContext) invocation + DefaultWaoApplicationContext applicationContext = + (DefaultWaoApplicationContext) invocation .getInvocationContext() .getApplication() - .get(WaoApplicationContext.APPLICATION_CONTEXT_PARAMETER); + .get(DefaultWaoApplicationContext.APPLICATION_CONTEXT_PARAMETER); Preconditions.checkNotNull(applicationContext, "application context must be initialized before calling an action");