mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Content URLs are now generated with an extra HOUR folder to handle high volume input in one day better
Added cleanup job for content stores - content is moved into (alf_data)/contentstore.deleted and mirrors the live content store - We'll make a call about disabling the trigger for the job, but currently it will fire at 4am git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2422 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -8,6 +8,44 @@
|
|||||||
<value>${dir.contentstore}</value>
|
<value>${dir.contentstore}</value>
|
||||||
</constructor-arg>
|
</constructor-arg>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<!-- deleted content will get pushed into this store, where it can be cleaned up at will -->
|
||||||
|
<bean id="deletedContentStore" class="org.alfresco.repo.content.filestore.FileContentStore">
|
||||||
|
<constructor-arg>
|
||||||
|
<value>${dir.contentstore.deleted}</value>
|
||||||
|
</constructor-arg>
|
||||||
|
</bean>
|
||||||
|
<!-- bean to move deleted content into the the backup store -->
|
||||||
|
<bean id="deletedContentBackupListener" class="org.alfresco.repo.content.cleanup.DeletedContentBackupCleanerListener" >
|
||||||
|
<property name="store">
|
||||||
|
<ref bean="deletedContentStore" />
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
<!-- Performs the content cleanup -->
|
||||||
|
<bean id="contentStoreCleaner" class="org.alfresco.repo.content.cleanup.ContentStoreCleaner" >
|
||||||
|
<property name="dictionaryService">
|
||||||
|
<ref bean="dictionaryService" />
|
||||||
|
</property>
|
||||||
|
<property name="nodeDaoService" >
|
||||||
|
<ref bean="nodeDaoService" />
|
||||||
|
</property>
|
||||||
|
<property name="transactionService" >
|
||||||
|
<ref bean="transactionComponent" />
|
||||||
|
</property>
|
||||||
|
<property name="protectDays" >
|
||||||
|
<value>14</value>
|
||||||
|
</property>
|
||||||
|
<property name="stores" >
|
||||||
|
<list>
|
||||||
|
<ref bean="fileContentStore" />
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<property name="listeners" >
|
||||||
|
<list>
|
||||||
|
<ref bean="deletedContentBackupListener" />
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<bean id="contentService" class="org.alfresco.repo.content.RoutingContentService" init-method="init">
|
<bean id="contentService" class="org.alfresco.repo.content.RoutingContentService" init-method="init">
|
||||||
<property name="transactionService">
|
<property name="transactionService">
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
dir.root=./alf_data
|
dir.root=./alf_data
|
||||||
|
|
||||||
dir.contentstore=${dir.root}/contentstore
|
dir.contentstore=${dir.root}/contentstore
|
||||||
|
dir.contentstore.deleted=${dir.root}/contentstore.deleted
|
||||||
|
|
||||||
# The location for lucene index files
|
# The location for lucene index files
|
||||||
|
|
||||||
|
@@ -52,32 +52,30 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="fileContentStoreCleanerTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
|
<bean id="contentStoreCleanerTrigger" class="org.alfresco.util.TriggerBean">
|
||||||
<property name="jobDetail">
|
<property name="jobDetail">
|
||||||
<bean id="fileContentStoreCleanerJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
|
<bean id="fileContentStoreCleanerJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
|
||||||
<property name="jobClass">
|
<property name="jobClass">
|
||||||
<value>org.alfresco.repo.content.ContentStoreCleanupJob</value>
|
<value>org.alfresco.repo.content.cleanup.ContentStoreCleanupJob</value>
|
||||||
</property>
|
</property>
|
||||||
<property name="jobDataAsMap">
|
<property name="jobDataAsMap">
|
||||||
<map>
|
<map>
|
||||||
<entry key="contentStore">
|
<entry key="contentStoreCleaner">
|
||||||
<ref bean="fileContentStore" />
|
<ref bean="contentStoreCleaner" />
|
||||||
</entry>
|
|
||||||
<entry key="searcher">
|
|
||||||
<ref bean="searchService" />
|
|
||||||
</entry>
|
|
||||||
<entry key="protectHours">
|
|
||||||
<value>24</value>
|
|
||||||
</entry>
|
</entry>
|
||||||
</map>
|
</map>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
</property>
|
</property>
|
||||||
<property name="startDelay">
|
<!-- trigger at 4am -->
|
||||||
<value>600000</value><!-- start after 10 minutes -->
|
<property name="hour">
|
||||||
|
<value>04</value>
|
||||||
|
</property>
|
||||||
|
<property name="minute">
|
||||||
|
<value>00</value>
|
||||||
</property>
|
</property>
|
||||||
<property name="repeatInterval">
|
<property name="repeatInterval">
|
||||||
<value>3600000</value><!-- repeat every hour -->
|
<value>86400000</value> <!-- repeat daily -->
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
@@ -153,9 +151,7 @@
|
|||||||
<property name="triggers">
|
<property name="triggers">
|
||||||
<list>
|
<list>
|
||||||
<ref bean="tempFileCleanerTrigger" />
|
<ref bean="tempFileCleanerTrigger" />
|
||||||
<!--
|
<ref local="contentStoreCleanerTrigger"/>
|
||||||
<ref local="fileContentStoreCleanerTrigger"/>
|
|
||||||
-->
|
|
||||||
<ref bean="ftsIndexerTrigger" />
|
<ref bean="ftsIndexerTrigger" />
|
||||||
<ref bean="indexRecoveryTrigger" />
|
<ref bean="indexRecoveryTrigger" />
|
||||||
<ref bean="indexBackupTrigger" />
|
<ref bean="indexBackupTrigger" />
|
||||||
|
@@ -59,12 +59,14 @@ public abstract class AbstractContentStore implements ContentStore
|
|||||||
int year = calendar.get(Calendar.YEAR);
|
int year = calendar.get(Calendar.YEAR);
|
||||||
int month = calendar.get(Calendar.MONTH) + 1; // 0-based
|
int month = calendar.get(Calendar.MONTH) + 1; // 0-based
|
||||||
int day = calendar.get(Calendar.DAY_OF_MONTH);
|
int day = calendar.get(Calendar.DAY_OF_MONTH);
|
||||||
|
int hour = calendar.get(Calendar.HOUR_OF_DAY);
|
||||||
// create the URL
|
// create the URL
|
||||||
StringBuilder sb = new StringBuilder(20);
|
StringBuilder sb = new StringBuilder(20);
|
||||||
sb.append(STORE_PROTOCOL)
|
sb.append(STORE_PROTOCOL)
|
||||||
.append(year).append('/')
|
.append(year).append('/')
|
||||||
.append(month).append('/')
|
.append(month).append('/')
|
||||||
.append(day).append('/')
|
.append(day).append('/')
|
||||||
|
.append(hour).append('/')
|
||||||
.append(GUID.generate()).append(".bin");
|
.append(GUID.generate()).append(".bin");
|
||||||
String newContentUrl = sb.toString();
|
String newContentUrl = sb.toString();
|
||||||
// done
|
// done
|
||||||
|
@@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2005 Alfresco, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Mozilla Public License version 1.1
|
|
||||||
* with a permitted attribution clause. You may obtain a
|
|
||||||
* copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.alfresco.org/legal/license.txt
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
|
||||||
* either express or implied. See the License for the specific
|
|
||||||
* language governing permissions and limitations under the
|
|
||||||
* License.
|
|
||||||
*/
|
|
||||||
package org.alfresco.repo.content;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
|
||||||
import org.alfresco.service.cmr.repository.ContentReader;
|
|
||||||
import org.alfresco.service.cmr.search.SearchService;
|
|
||||||
import org.quartz.Job;
|
|
||||||
import org.quartz.JobDataMap;
|
|
||||||
import org.quartz.JobExecutionContext;
|
|
||||||
import org.quartz.JobExecutionException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all content form the store that is not referenced by any content node.
|
|
||||||
* <p>
|
|
||||||
* The following parameters are required:
|
|
||||||
* <ul>
|
|
||||||
* <li><b>contentStore</b>: The content store bean to clean up</li>
|
|
||||||
* <li><b>searcher</b>: The index searcher that searches for content in the store</li>
|
|
||||||
* <li><b>protectHours</b>: The number of hours to protect content that isn't referenced</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @author Derek Hulley
|
|
||||||
*/
|
|
||||||
public class ContentStoreCleanupJob implements Job
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Gets all content URLs from the store, checks if it is in use by any node
|
|
||||||
* and deletes those that aren't.
|
|
||||||
*/
|
|
||||||
public void execute(JobExecutionContext context) throws JobExecutionException
|
|
||||||
{
|
|
||||||
JobDataMap jobData = context.getJobDetail().getJobDataMap();
|
|
||||||
// extract the content store to use
|
|
||||||
Object contentStoreObj = jobData.get("contentStore");
|
|
||||||
if (contentStoreObj == null || !(contentStoreObj instanceof ContentStore))
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException(
|
|
||||||
"ContentStoreCleanupJob data must contain valid 'contentStore' reference");
|
|
||||||
}
|
|
||||||
ContentStore contentStore = (ContentStore) contentStoreObj;
|
|
||||||
// extract the search to use
|
|
||||||
Object searcherObj = jobData.get("searcher");
|
|
||||||
if (searcherObj == null || !(searcherObj instanceof SearchService))
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException(
|
|
||||||
"ContentStoreCleanupJob data must contain valid 'searcher' reference");
|
|
||||||
}
|
|
||||||
SearchService searcher = (SearchService) searcherObj;
|
|
||||||
// get the number of hourse to protect content
|
|
||||||
Object protectHoursObj = jobData.get("protectHours");
|
|
||||||
if (protectHoursObj == null || !(protectHoursObj instanceof String))
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException(
|
|
||||||
"ContentStoreCleanupJob data must contain valid 'protectHours' value");
|
|
||||||
}
|
|
||||||
long protectHours = 24L;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
protectHours = Long.parseLong((String) protectHoursObj);
|
|
||||||
}
|
|
||||||
catch (NumberFormatException e)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException(
|
|
||||||
"ContentStoreCleanupJob data 'protectHours' value is not a valid integer");
|
|
||||||
}
|
|
||||||
|
|
||||||
long protectMillis = protectHours * 3600L * 1000L; // 3600s in an hour; 1000ms in a second
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
long lastModifiedSafeTimeMs = (now - protectMillis); // able to remove anything modified before this
|
|
||||||
|
|
||||||
// get all URLs in the store
|
|
||||||
Set<String> contentUrls = contentStore.getUrls();
|
|
||||||
for (String contentUrl : contentUrls)
|
|
||||||
{
|
|
||||||
// TODO here we need to get hold of all the orphaned content in this store
|
|
||||||
|
|
||||||
// not found - it is not in the repo, but check that it is old enough to delete
|
|
||||||
ContentReader reader = contentStore.getReader(contentUrl);
|
|
||||||
if (reader == null || !reader.exists())
|
|
||||||
{
|
|
||||||
// gone missing in the meantime
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
long lastModified = reader.getLastModified();
|
|
||||||
if (lastModified >= lastModifiedSafeTimeMs)
|
|
||||||
{
|
|
||||||
// not old enough
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// it is not in the repo and is old enough
|
|
||||||
boolean result = contentStore.delete(contentUrl);
|
|
||||||
System.out.println(contentUrl + ": " + Boolean.toString(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO for now throw this exception to ensure that this job does not get run until
|
|
||||||
// the orphaned content can be correctly retrieved
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,114 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2005 Alfresco, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Mozilla Public License version 1.1
|
|
||||||
* with a permitted attribution clause. You may obtain a
|
|
||||||
* copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.alfresco.org/legal/license.txt
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
|
||||||
* either express or implied. See the License for the specific
|
|
||||||
* language governing permissions and limitations under the
|
|
||||||
* License.
|
|
||||||
*/
|
|
||||||
package org.alfresco.repo.content;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import junit.framework.TestSuite;
|
|
||||||
|
|
||||||
import org.alfresco.service.cmr.repository.ContentReader;
|
|
||||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
|
||||||
import org.alfresco.util.BaseSpringTest;
|
|
||||||
import org.quartz.JobDataMap;
|
|
||||||
import org.quartz.JobExecutionContext;
|
|
||||||
import org.quartz.JobExecutionException;
|
|
||||||
import org.quartz.Scheduler;
|
|
||||||
import org.quartz.SchedulerFactory;
|
|
||||||
import org.quartz.impl.StdSchedulerFactory;
|
|
||||||
import org.quartz.impl.calendar.BaseCalendar;
|
|
||||||
import org.quartz.spi.TriggerFiredBundle;
|
|
||||||
import org.springframework.scheduling.quartz.SimpleTriggerBean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Content store cleanup job unit test
|
|
||||||
*
|
|
||||||
* @author Roy Wetherall
|
|
||||||
*/
|
|
||||||
public class ContentStoreCleanupJobTest extends BaseSpringTest
|
|
||||||
{
|
|
||||||
private SimpleTriggerBean simpleTriggerBean;
|
|
||||||
private JobExecutionContext jobExecutionContext;
|
|
||||||
private ContentStoreCleanupJob job;
|
|
||||||
|
|
||||||
private ContentStore contentStore;
|
|
||||||
private String url;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This can be removed once the class being tested actually has a remote
|
|
||||||
* chance of working.
|
|
||||||
*/
|
|
||||||
public static TestSuite suite()
|
|
||||||
{
|
|
||||||
return new TestSuite();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On setup in transaction
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void onSetUpInTransaction() throws Exception
|
|
||||||
{
|
|
||||||
this.contentStore = (ContentStore)this.applicationContext.getBean("fileContentStore");
|
|
||||||
this.simpleTriggerBean = (SimpleTriggerBean)this.applicationContext.getBean("fileContentStoreCleanerTrigger");
|
|
||||||
|
|
||||||
SchedulerFactory factory = new StdSchedulerFactory();
|
|
||||||
Scheduler scheduler = factory.getScheduler();
|
|
||||||
|
|
||||||
// Set the protect hours to 0 for the purpose of this test
|
|
||||||
JobDataMap jobDataMap = this.simpleTriggerBean.getJobDetail().getJobDataMap();
|
|
||||||
jobDataMap.put("protectHours", "0");
|
|
||||||
this.simpleTriggerBean.getJobDetail().setJobDataMap(jobDataMap);
|
|
||||||
|
|
||||||
this.job = new ContentStoreCleanupJob();
|
|
||||||
TriggerFiredBundle triggerFiredBundle = new TriggerFiredBundle(
|
|
||||||
this.simpleTriggerBean.getJobDetail(),
|
|
||||||
this.simpleTriggerBean,
|
|
||||||
new BaseCalendar(),
|
|
||||||
false,
|
|
||||||
new Date(),
|
|
||||||
new Date(),
|
|
||||||
new Date(),
|
|
||||||
new Date());
|
|
||||||
|
|
||||||
this.jobExecutionContext = new JobExecutionContext(scheduler, triggerFiredBundle, job);
|
|
||||||
|
|
||||||
ContentWriter contentWriter = this.contentStore.getWriter(null, null);
|
|
||||||
contentWriter.putContent("This is some content that I am going to delete.");
|
|
||||||
this.url = contentWriter.getContentUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test execute method
|
|
||||||
*/
|
|
||||||
public void testExecute()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ContentReader before = this.contentStore.getReader(this.url);
|
|
||||||
assertTrue(before.exists());
|
|
||||||
|
|
||||||
this.job.execute(this.jobExecutionContext);
|
|
||||||
|
|
||||||
ContentReader after = this.contentStore.getReader(this.url);
|
|
||||||
assertFalse(after.exists());
|
|
||||||
}
|
|
||||||
catch (JobExecutionException exception)
|
|
||||||
{
|
|
||||||
fail("Exception raised!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,218 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005 Alfresco, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Mozilla Public License version 1.1
|
||||||
|
* with a permitted attribution clause. You may obtain a
|
||||||
|
* copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.alfresco.org/legal/license.txt
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
* either express or implied. See the License for the specific
|
||||||
|
* language governing permissions and limitations under the
|
||||||
|
* License.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.content.cleanup;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.alfresco.repo.content.ContentStore;
|
||||||
|
import org.alfresco.repo.node.db.NodeDaoService;
|
||||||
|
import org.alfresco.repo.transaction.TransactionUtil;
|
||||||
|
import org.alfresco.repo.transaction.TransactionUtil.TransactionWork;
|
||||||
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component is responsible for finding orphaned content in a given
|
||||||
|
* content store or stores. Deletion handlers can be provided to ensure
|
||||||
|
* that the content is moved to another location prior to being removed
|
||||||
|
* from the store(s) being cleaned.
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
*/
|
||||||
|
public class ContentStoreCleaner
|
||||||
|
{
|
||||||
|
private static Log logger = LogFactory.getLog(ContentStoreCleaner.class);
|
||||||
|
|
||||||
|
private DictionaryService dictionaryService;
|
||||||
|
private NodeDaoService nodeDaoService;
|
||||||
|
private TransactionService transactionService;
|
||||||
|
private List<ContentStore> stores;
|
||||||
|
private List<ContentStoreCleanerListener> listeners;
|
||||||
|
private int protectDays;
|
||||||
|
|
||||||
|
public ContentStoreCleaner()
|
||||||
|
{
|
||||||
|
this.stores = new ArrayList<ContentStore>(0);
|
||||||
|
this.listeners = new ArrayList<ContentStoreCleanerListener>(0);
|
||||||
|
this.protectDays = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param dictionaryService used to determine which properties are content properties
|
||||||
|
*/
|
||||||
|
public void setDictionaryService(DictionaryService dictionaryService)
|
||||||
|
{
|
||||||
|
this.dictionaryService = dictionaryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nodeDaoService used to get the property values
|
||||||
|
*/
|
||||||
|
public void setNodeDaoService(NodeDaoService nodeDaoService)
|
||||||
|
{
|
||||||
|
this.nodeDaoService = nodeDaoService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param transactionService the component to ensure proper transactional wrapping
|
||||||
|
*/
|
||||||
|
public void setTransactionService(TransactionService transactionService)
|
||||||
|
{
|
||||||
|
this.transactionService = transactionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param stores the content stores to clean
|
||||||
|
*/
|
||||||
|
public void setStores(List<ContentStore> stores)
|
||||||
|
{
|
||||||
|
this.stores = stores;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param listeners the listeners that can react to deletions
|
||||||
|
*/
|
||||||
|
public void setListeners(List<ContentStoreCleanerListener> listeners)
|
||||||
|
{
|
||||||
|
this.listeners = listeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the minimum number of days old that orphaned content must be
|
||||||
|
* before deletion is possible. The default is 7 days.
|
||||||
|
*
|
||||||
|
* @param protectDays minimum age (in days) of deleted content
|
||||||
|
*/
|
||||||
|
public void setProtectDays(int protectDays)
|
||||||
|
{
|
||||||
|
this.protectDays = protectDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform basic checks to ensure that the necessary dependencies were injected.
|
||||||
|
*/
|
||||||
|
private void checkProperties()
|
||||||
|
{
|
||||||
|
if (dictionaryService == null)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Property 'dictionaryService' not set");
|
||||||
|
}
|
||||||
|
if (nodeDaoService == null)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Property 'nodeDaoService' not set");
|
||||||
|
}
|
||||||
|
if (transactionService == null)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Property 'transactionService' not set");
|
||||||
|
}
|
||||||
|
if (stores == null || stores.size() == 0)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Property 'stores' not set");
|
||||||
|
}
|
||||||
|
if (listeners == null)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Property 'listeners' not set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> getValidUrls()
|
||||||
|
{
|
||||||
|
// wrap to make the request in a transaction
|
||||||
|
TransactionWork<List<String>> getUrlsWork = new TransactionWork<List<String>>()
|
||||||
|
{
|
||||||
|
public List<String> doWork() throws Exception
|
||||||
|
{
|
||||||
|
return nodeDaoService.getContentDataStrings();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// execute in READ-ONLY txn
|
||||||
|
List<String> contentDataStrings = TransactionUtil.executeInUserTransaction(
|
||||||
|
transactionService,
|
||||||
|
getUrlsWork,
|
||||||
|
true);
|
||||||
|
|
||||||
|
// get all valid URLs
|
||||||
|
Set<String> validUrls = new HashSet<String>(contentDataStrings.size());
|
||||||
|
// convert the strings to objects and extract the URL
|
||||||
|
for (String contentDataString : contentDataStrings)
|
||||||
|
{
|
||||||
|
ContentData contentData = ContentData.createContentProperty(contentDataString);
|
||||||
|
if (contentData.getContentUrl() != null)
|
||||||
|
{
|
||||||
|
// a URL was present
|
||||||
|
validUrls.add(contentData.getContentUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// done
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Found " + validUrls.size() + " valid URLs in metadata");
|
||||||
|
}
|
||||||
|
return validUrls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute()
|
||||||
|
{
|
||||||
|
checkProperties();
|
||||||
|
Set<String> validUrls = getValidUrls();
|
||||||
|
// now clean each store in turn
|
||||||
|
for (ContentStore store : stores)
|
||||||
|
{
|
||||||
|
clean(validUrls, store);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clean(Set<String> validUrls, ContentStore store)
|
||||||
|
{
|
||||||
|
Date checkAllBeforeDate = new Date(System.currentTimeMillis() - (long) protectDays * 3600L * 1000L * 24L);
|
||||||
|
// get the store's URLs
|
||||||
|
Set<String> storeUrls = store.getUrls(null, checkAllBeforeDate);
|
||||||
|
// remove all URLs that occur in the validUrls
|
||||||
|
storeUrls.removeAll(validUrls);
|
||||||
|
// now clean the store
|
||||||
|
for (String url : storeUrls)
|
||||||
|
{
|
||||||
|
ContentReader sourceReader = store.getReader(url);
|
||||||
|
// announce this to the listeners
|
||||||
|
for (ContentStoreCleanerListener listener : listeners)
|
||||||
|
{
|
||||||
|
// get a fresh reader
|
||||||
|
ContentReader listenerReader = sourceReader.getReader();
|
||||||
|
// call it
|
||||||
|
listener.beforeDelete(listenerReader);
|
||||||
|
}
|
||||||
|
// delete it
|
||||||
|
store.delete(url);
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Removed URL from store: \n" +
|
||||||
|
" Store: " + store + "\n" +
|
||||||
|
" URL: " + url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005 Alfresco, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Mozilla Public License version 1.1
|
||||||
|
* with a permitted attribution clause. You may obtain a
|
||||||
|
* copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.alfresco.org/legal/license.txt
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
* either express or implied. See the License for the specific
|
||||||
|
* language governing permissions and limitations under the
|
||||||
|
* License.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.content.cleanup;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A listener that can be plugged into a
|
||||||
|
* {@link org.alfresco.repo.content.cleanup.ContentStoreCleaner cleaner} to
|
||||||
|
* move soon-to-be-deleted content to a new location.
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
*/
|
||||||
|
public interface ContentStoreCleanerListener
|
||||||
|
{
|
||||||
|
public void beforeDelete(ContentReader reader) throws ContentIOException;
|
||||||
|
}
|
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005 Alfresco, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Mozilla Public License version 1.1
|
||||||
|
* with a permitted attribution clause. You may obtain a
|
||||||
|
* copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.alfresco.org/legal/license.txt
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
* either express or implied. See the License for the specific
|
||||||
|
* language governing permissions and limitations under the
|
||||||
|
* License.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.content.cleanup;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.repo.content.ContentStore;
|
||||||
|
import org.alfresco.repo.content.filestore.FileContentStore;
|
||||||
|
import org.alfresco.repo.node.db.NodeDaoService;
|
||||||
|
import org.alfresco.service.ServiceRegistry;
|
||||||
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
|
import org.alfresco.util.ApplicationContextHelper;
|
||||||
|
import org.alfresco.util.TempFileProvider;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see org.alfresco.repo.content.cleanup.ContentStoreCleaner
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
*/
|
||||||
|
public class ContentStoreCleanerTest extends TestCase
|
||||||
|
{
|
||||||
|
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
|
||||||
|
|
||||||
|
private ContentStoreCleaner cleaner;
|
||||||
|
private ContentStore store;
|
||||||
|
private ContentStoreCleanerListener listener;
|
||||||
|
private List<String> deletedUrls;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception
|
||||||
|
{
|
||||||
|
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry");
|
||||||
|
TransactionService transactionService = serviceRegistry.getTransactionService();
|
||||||
|
DictionaryService dictionaryService = serviceRegistry.getDictionaryService();
|
||||||
|
NodeDaoService nodeDaoService = (NodeDaoService) ctx.getBean("nodeDaoService");
|
||||||
|
|
||||||
|
// we need a store
|
||||||
|
store = new FileContentStore(TempFileProvider.getTempDir().getAbsolutePath());
|
||||||
|
// and a listener
|
||||||
|
listener = new DummyCleanerListener();
|
||||||
|
// initialise record of deleted URLs
|
||||||
|
deletedUrls = new ArrayList<String>(5);
|
||||||
|
|
||||||
|
// construct the test cleaner
|
||||||
|
cleaner = new ContentStoreCleaner();
|
||||||
|
cleaner.setTransactionService(transactionService);
|
||||||
|
cleaner.setDictionaryService(dictionaryService);
|
||||||
|
cleaner.setNodeDaoService(nodeDaoService);
|
||||||
|
cleaner.setStores(Collections.singletonList(store));
|
||||||
|
cleaner.setListeners(Collections.singletonList(listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testImmediateRemoval() throws Exception
|
||||||
|
{
|
||||||
|
cleaner.setProtectDays(0);
|
||||||
|
// add some content to the store
|
||||||
|
ContentWriter writer = store.getWriter(null, null);
|
||||||
|
writer.putContent("ABC");
|
||||||
|
String contentUrl = writer.getContentUrl();
|
||||||
|
|
||||||
|
// fire the cleaner
|
||||||
|
cleaner.execute();
|
||||||
|
|
||||||
|
// the content should have disappeared as it is not in the database
|
||||||
|
assertFalse("Unprotected content was not deleted", store.exists(contentUrl));
|
||||||
|
assertTrue("Content listener was not called with deletion", deletedUrls.contains(contentUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testProtectedRemoval() throws Exception
|
||||||
|
{
|
||||||
|
cleaner.setProtectDays(1);
|
||||||
|
// add some content to the store
|
||||||
|
ContentWriter writer = store.getWriter(null, null);
|
||||||
|
writer.putContent("ABC");
|
||||||
|
String contentUrl = writer.getContentUrl();
|
||||||
|
|
||||||
|
// fire the cleaner
|
||||||
|
cleaner.execute();
|
||||||
|
|
||||||
|
// the content should have disappeared as it is not in the database
|
||||||
|
assertTrue("Protected content was deleted", store.exists(contentUrl));
|
||||||
|
assertFalse("Content listener was called with deletion of protected URL", deletedUrls.contains(contentUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DummyCleanerListener implements ContentStoreCleanerListener
|
||||||
|
{
|
||||||
|
public void beforeDelete(ContentReader reader) throws ContentIOException
|
||||||
|
{
|
||||||
|
deletedUrls.add(reader.getContentUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005 Alfresco, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Mozilla Public License version 1.1
|
||||||
|
* with a permitted attribution clause. You may obtain a
|
||||||
|
* copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.alfresco.org/legal/license.txt
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
* either express or implied. See the License for the specific
|
||||||
|
* language governing permissions and limitations under the
|
||||||
|
* License.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.content.cleanup;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.quartz.Job;
|
||||||
|
import org.quartz.JobDataMap;
|
||||||
|
import org.quartz.JobExecutionContext;
|
||||||
|
import org.quartz.JobExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers the deletion of unused content using a
|
||||||
|
* {@link org.alfresco.repo.content.cleanup.ContentStoreCleaner}.
|
||||||
|
* <p>
|
||||||
|
* The following parameters are required:
|
||||||
|
* <ul>
|
||||||
|
* <li><b>contentStoreCleaner</b>: The content store cleaner bean</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
*/
|
||||||
|
public class ContentStoreCleanupJob implements Job
|
||||||
|
{
|
||||||
|
public ContentStoreCleanupJob()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the cleaner to do its work
|
||||||
|
*/
|
||||||
|
public void execute(JobExecutionContext context) throws JobExecutionException
|
||||||
|
{
|
||||||
|
JobDataMap jobData = context.getJobDetail().getJobDataMap();
|
||||||
|
// extract the content cleaner to use
|
||||||
|
Object contentStoreCleanerObj = jobData.get("contentStoreCleaner");
|
||||||
|
if (contentStoreCleanerObj == null || !(contentStoreCleanerObj instanceof ContentStoreCleaner))
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException(
|
||||||
|
"ContentStoreCleanupJob data must contain valid 'contentStoreCleaner' reference");
|
||||||
|
}
|
||||||
|
ContentStoreCleaner contentStoreCleaner = (ContentStoreCleaner) contentStoreCleanerObj;
|
||||||
|
contentStoreCleaner.execute();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005 Alfresco, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Mozilla Public License version 1.1
|
||||||
|
* with a permitted attribution clause. You may obtain a
|
||||||
|
* copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.alfresco.org/legal/license.txt
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
* either express or implied. See the License for the specific
|
||||||
|
* language governing permissions and limitations under the
|
||||||
|
* License.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.content.cleanup;
|
||||||
|
|
||||||
|
import org.alfresco.repo.content.ContentStore;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for content that is about to be deleted and moves it into the store
|
||||||
|
* configured as the backup store.
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
*/
|
||||||
|
public class DeletedContentBackupCleanerListener implements ContentStoreCleanerListener
|
||||||
|
{
|
||||||
|
private static Log logger = LogFactory.getLog(DeletedContentBackupCleanerListener.class);
|
||||||
|
|
||||||
|
private ContentStore store;
|
||||||
|
|
||||||
|
public DeletedContentBackupCleanerListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the store to copy soon-to-be-deleted content into
|
||||||
|
*
|
||||||
|
* @param store the deleted content backup store
|
||||||
|
*/
|
||||||
|
public void setStore(ContentStore store)
|
||||||
|
{
|
||||||
|
this.store = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beforeDelete(ContentReader reader) throws ContentIOException
|
||||||
|
{
|
||||||
|
// write the content into the target store
|
||||||
|
ContentWriter writer = store.getWriter(null, reader.getContentUrl());
|
||||||
|
// copy across
|
||||||
|
writer.putContent(reader);
|
||||||
|
// done
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Moved content before deletion: \n" +
|
||||||
|
" URL: " + reader.getContentUrl() + "\n" +
|
||||||
|
" Store: " + store);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -330,6 +330,17 @@
|
|||||||
status.changeTxnId = :changeTxnId
|
status.changeTxnId = :changeTxnId
|
||||||
</query>
|
</query>
|
||||||
|
|
||||||
|
<query name="node.GetContentDataStrings">
|
||||||
|
select distinct
|
||||||
|
props.stringValue
|
||||||
|
from
|
||||||
|
org.alfresco.repo.domain.hibernate.NodeImpl as node
|
||||||
|
join
|
||||||
|
node.properties props
|
||||||
|
where
|
||||||
|
props.stringValue like 'contentUrl%'
|
||||||
|
</query>
|
||||||
|
|
||||||
<query name="node.patch.GetNodesWithPersistedSerializableProperties">
|
<query name="node.patch.GetNodesWithPersistedSerializableProperties">
|
||||||
select distinct
|
select distinct
|
||||||
node
|
node
|
||||||
|
@@ -24,12 +24,14 @@ import java.util.Map;
|
|||||||
import javax.transaction.UserTransaction;
|
import javax.transaction.UserTransaction;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.content.MimetypeMap;
|
||||||
import org.alfresco.repo.domain.NodeStatus;
|
import org.alfresco.repo.domain.NodeStatus;
|
||||||
import org.alfresco.repo.node.BaseNodeServiceTest;
|
import org.alfresco.repo.node.BaseNodeServiceTest;
|
||||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||||
import org.alfresco.repo.transaction.TransactionUtil;
|
import org.alfresco.repo.transaction.TransactionUtil;
|
||||||
import org.alfresco.repo.transaction.TransactionUtil.TransactionWork;
|
import org.alfresco.repo.transaction.TransactionUtil.TransactionWork;
|
||||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
@@ -257,4 +259,22 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the string_value retrieval against a property type is working
|
||||||
|
*/
|
||||||
|
public void testGetContentDataStringValues() throws Exception
|
||||||
|
{
|
||||||
|
ContentData contentData = new ContentData("abc", MimetypeMap.MIMETYPE_TEXT_PLAIN, 0L, null);
|
||||||
|
// put this in as a random property
|
||||||
|
nodeService.setProperty(
|
||||||
|
rootNodeRef,
|
||||||
|
QName.createQName(NAMESPACE, "random"),
|
||||||
|
contentData);
|
||||||
|
// get a list of all content values
|
||||||
|
List<String> contentDataStrings = nodeDaoService.getContentDataStrings();
|
||||||
|
assertNotNull(contentDataStrings);
|
||||||
|
assertTrue("ContentData not represented as a String in results",
|
||||||
|
contentDataStrings.contains(contentData.toString()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -176,4 +176,12 @@ public interface NodeDaoService
|
|||||||
* returns <code>null</code>.
|
* returns <code>null</code>.
|
||||||
*/
|
*/
|
||||||
public NodeStatus getNodeStatus(String protocol, String identifier, String id);
|
public NodeStatus getNodeStatus(String protocol, String identifier, String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all content data strings. These are all string values that begin
|
||||||
|
* with <b>contentUrl=</b>.
|
||||||
|
*
|
||||||
|
* @return Returns the string values for content data
|
||||||
|
*/
|
||||||
|
public List<String> getContentDataStrings();
|
||||||
}
|
}
|
||||||
|
@@ -54,11 +54,11 @@ import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
|
|||||||
*/
|
*/
|
||||||
public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements NodeDaoService
|
public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements NodeDaoService
|
||||||
{
|
{
|
||||||
public static final String QUERY_GET_ALL_STORES = "store.GetAllStores";
|
private static final String QUERY_GET_ALL_STORES = "store.GetAllStores";
|
||||||
public static final String QUERY_GET_CHILD_ASSOC = "node.GetChildAssoc";
|
private static final String QUERY_GET_NODE_ASSOC = "node.GetNodeAssoc";
|
||||||
public static final String QUERY_GET_NODE_ASSOC = "node.GetNodeAssoc";
|
private static final String QUERY_GET_NODE_ASSOC_TARGETS = "node.GetNodeAssocTargets";
|
||||||
public static final String QUERY_GET_NODE_ASSOC_TARGETS = "node.GetNodeAssocTargets";
|
private static final String QUERY_GET_NODE_ASSOC_SOURCES = "node.GetNodeAssocSources";
|
||||||
public static final String QUERY_GET_NODE_ASSOC_SOURCES = "node.GetNodeAssocSources";
|
private static final String QUERY_GET_CONTENT_DATA_STRINGS = "node.GetContentDataStrings";
|
||||||
|
|
||||||
/** a uuid identifying this unique instance */
|
/** a uuid identifying this unique instance */
|
||||||
private String uuid;
|
private String uuid;
|
||||||
@@ -504,4 +504,39 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
// remove instance
|
// remove instance
|
||||||
getHibernateTemplate().delete(assoc);
|
getHibernateTemplate().delete(assoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public List<String> getContentDataStrings()
|
||||||
|
{
|
||||||
|
HibernateCallback callback = new HibernateCallback()
|
||||||
|
{
|
||||||
|
public Object doInHibernate(Session session)
|
||||||
|
{
|
||||||
|
Query query = session.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CONTENT_DATA_STRINGS);
|
||||||
|
return query.list();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
List<String> queryResults = (List) getHibernateTemplate().execute(callback);
|
||||||
|
return queryResults;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user