Removing malformed Sites in Alfresco

Every site is created in Alfresco like any other node: as a children of an special node from type st:sites and having themselves a type called st:site with the following associated aspects:

sys:undeletable
cm:auditable
cm:tagscope
sys:referenceable
cm:titled
sys:localized

Sometimes, by user administrator repository arrangements, by migration tasks or by any other causes, this Folder Site becomes corrupt. If an st:site node is corrupt, some operations start to run wrong (list sites, searching sites…) in Alfresco REST API and in Alfresco Share web app.

It could be solved easily by removing this corrupt node from Alfresco. However, this node has sys:undeletable aspect, which can not be removed from Alfresco Share web interface.

Douglas C. R. Paes suggested me at Alfresco Chat to use that technique explained by Bindu Wavell in his Advanced Repository Administration with Javascript session at Alfresco Summit 2014 in London:

var context = Packages.org.springframework.web.context.ContextLoader
          .getCurrentWebApplicationContext();
var model = Packages.org.alfresco.model.ContentModel;
var policyBehaviourFilter = context.getBean('policyBehaviourFilter');
policyBehaviourFilter.disableBehaviour(document.nodeRef, model.ASPECT_UNDELETABLE);
try {
     document.remove();
} catch (scriptErr) {
logger.warn('There was a problem deleting the node: ' + scriptErr); }
policyBehaviourFilter.enableBehaviour(document.nodeRef, model.ASPECT_UNDELETABLE);

Unfortunately, this operation can only be performed by using Alfresco JavaScript Console, which was not available or installable in my case.

Having discarded any external removal operation (CMIS API has the same restrictions), I decided to write a patch by using Java API.

// Extending AbstractLifecycleBean allows to execute operations onBootstrap
public class DeleteMalformedSite extends AbstractLifecycleBean {

	private static Log logger = LogFactory.getLog(DeleteMalformedSite.class);

	// Delayed starting to guarantee that all services are up and ready
	private static final int DELAYED_START_SECONDS = 30;

	// Inject this services by Spring bean in bootstrap-context.xml
	private NodeService nodeService;
	private SearchService searchService;
	private BehaviourFilter behaviourFilter;
	private TransactionService transactionService;
	private SiteService siteService;
	private AuthenticationService authenticationService;

	@Override
	protected void onBootstrap(ApplicationEvent event) {
		deleteMalformedSiteFolders();
	}

	private void deleteMalformedSiteFolders() {

		ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
		scheduledExecutorService.schedule(new AsynchronousTask(), DELAYED_START_SECONDS, TimeUnit.SECONDS);
		scheduledExecutorService.shutdown();

	}

	private class AsynchronousTask implements Runnable {

		@Override
		public void run() {

			AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<String>() {

				@Override
				public String doWork() throws Exception {

					// Transactional enviroment is required
					RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
					txnHelper.setForceWritable(true);
					txnHelper.doInTransaction(new RetryingTransactionCallback<Void>() {
						
						public Void execute() throws Throwable {
							SearchParameters sp = new SearchParameters();
							sp.setLanguage(SearchService.LANGUAGE_CMIS_ALFRESCO);
							sp.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
							sp.setQuery("SELECT * FROM st:site");

							ResultSet results = null;
							try {

								results = searchService.query(sp);
								logger.info("Checking malformed sites, found " + results.length() + " sites");

								for (ResultSetRow row : results) {

									boolean siteMalformed = false;

									try {
										SiteInfo siteInfo = siteService.getSite(row.getNodeRef());
										if (siteInfo != null) {
										    siteService.getMembersRole(siteInfo.getShortName(), authenticationService.getCurrentUserName());
										} else {
											siteMalformed = true;
										}
									} catch (Throwable t) {
										logger.warn("Site " + row.getNodeRef() + " is malformed!", t);
										siteMalformed = true;
									}

									if (siteMalformed) {

										NodeRef currentNodeRef = null;
										NodeRef siteParent = null;

										try {

											currentNodeRef = row.getNodeRef();
											behaviourFilter.disableBehaviour(currentNodeRef, ContentModel.ASPECT_UNDELETABLE);

											logger.info("About to delete "
													+ nodeService.getProperty(currentNodeRef, ContentModel.PROP_NAME)
													+ " folder site");

											siteParent = nodeService.getPrimaryParent(currentNodeRef).getParentRef();
											behaviourFilter.disableBehaviour(siteParent, ContentModel.ASPECT_AUDITABLE);

											logger.info("Site parent folder is "
													+ nodeService.getProperty(siteParent, ContentModel.PROP_NAME)
													+ " folder");

											nodeService.deleteNode(currentNodeRef);

											logger.info("Site folder deleted");

										} catch (Throwable t) {
											logger.error("Site not deleted", t);
										} finally {
											behaviourFilter.enableBehaviour(currentNodeRef, ContentModel.ASPECT_UNDELETABLE);
											behaviourFilter.enableBehaviour(siteParent, ContentModel.ASPECT_AUDITABLE);
										}
									}
								}

								logger.info("Malformed sites checking completed");

							} finally {
								if (results != null) {
									results.close();
								}
							}

							return null;

						}

					}, false, true);

					return null;
				}

			});

		}
	}

	@Override
	protected void onShutdown(ApplicationEvent event) {}

	// Getters and setters
	// ...

}

Once deployed this AMP, malformed site folders are removing at Alfresco boot.

No one said it would be easy!

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s