Groovy: Close those transactions, or else...

See Nexus 3 Groovy Script development environment setup for tips on how to develop groovy scripts for Nexus 3.

Warning: Do try this at home, do not try this in production.

Warning: Do NOT run the example code below before reading this entire article to completion. Hint: The code is intentionally broken, and it will damage your Nexus server, scare your pets, and make you sad.

To prepare the Nexus repository, download a junit component into your local Nexus proxy (maven-central) of the Central repository, using the command:

wget http://localhost:8081/repository/maven-central/junit/junit/4.12/junit-4.12.jar

After junit-4.12.jar is downloaded, you can delete it. We only fetched it in order to populate Nexus maven-central with that jar.

Next, run this groovy script in Nexus to see if that specific file is found in your repository, and then deleted from your repository:

import org.sonatype.nexus.repository.Repository
import org.sonatype.nexus.repository.storage.Query
import org.sonatype.nexus.repository.storage.StorageFacet
import org.sonatype.nexus.script.plugin.internal.provisioning.RepositoryApiImpl
import org.sonatype.nexus.repository.storage.StorageTx

class MyTransactionClass
{
  private final log
  private final RepositoryApiImpl repo

  MyTransactionClass(log, RepositoryApiImpl repo) {
    this.log = log
    this.repo = repo
  }

  void doStuff() {
    final Repository repositoryToEdit = repo.repositoryManager.get('maven-central')
    final StorageFacet storageFacet = repositoryToEdit.facet(StorageFacet)
    final StorageTx tx = storageFacet.txSupplier().get()
    try {
      tx.begin()
      tx.findComponents(Query.builder().where('group').eq("junit").and('version ').eq("4.12").build(), [repositoryToEdit]).each { component ->
        log.info("Deleting component ${component.group()} ${component.name()} ${component.version()}");
        tx.deleteComponent(component)
      }
      tx.commit()
    }
    catch (Exception e) {
      log.warn("Yikes: {}", e.toString())
      tx.rollback()
    }
    finally {
      // @todo Fix me! Danger Will Robinson!  
      //tx.close()
    }
  }
}

new MyTransactionClass(log, repository).doStuff()

Everything is fine, right? The component was found and then deleted. Job's done. After all, you saw a message in the nexus.log like:

*SYSTEM org.sonatype.nexus.internal.script.ScriptTask - Deleting component junit junit 4.12

Well, not so fast. There is actually a nasty bug in this script that could eventualy bring down your Nexus server. Notice the "finally" block has the 'tx.close' command commented out. It is very easy to forget to wrap transactional code in a "try - finally" block. To avoid leaks, be sure the transaction.close() method is called in a "finally" block. This way, no matter what path the execution takes, and no matter what exceptions occur in the "try" block, the transaction will be closed.

A leak like the one above may not produce an immediately visible problem on the server, but it will eventually lead to failure of code that requires access to the database. So you won't know when, or where, but you can be sure, eventually, it will break your server and make you sad.

Have more questions? Submit a request

0 Comments

Article is closed for comments.
Powered by Zendesk