ubic.gemma.core.security.audit.AuditAdviceTest.java Source code

Java tutorial

Introduction

Here is the source code for ubic.gemma.core.security.audit.AuditAdviceTest.java

Source

/*
 * The Gemma project
 *
 * Copyright (c) 2006 University of British Columbia
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 ubic.gemma.core.security.audit;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.Collection;
import java.util.HashSet;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.lang3.RandomStringUtils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.encoding.PasswordEncoder;

import ubic.gemma.core.util.test.BaseSpringContextTest;
import ubic.gemma.model.common.Auditable;
import ubic.gemma.model.common.auditAndSecurity.AuditEvent;
import ubic.gemma.model.common.auditAndSecurity.AuditTrail;
import ubic.gemma.model.expression.bioAssay.BioAssay;
import ubic.gemma.model.expression.experiment.ExperimentalFactor;
import ubic.gemma.model.expression.experiment.ExpressionExperiment;
import ubic.gemma.persistence.service.common.auditAndSecurity.AuditEventService;
import ubic.gemma.persistence.service.expression.experiment.ExpressionExperimentService;

/**
 * Test of adding audit events when objects are created, updated or deleted.
 * Note: this test used to use genes, but we removed auditability from genes.
 *
 * @author pavlidis
 */
public class AuditAdviceTest extends BaseSpringContextTest {

    @Autowired
    private AuditEventService auditEventService;

    @Autowired
    private ExpressionExperimentService expressionExperimentService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private SessionFactory sessionFactory;

    @Test
    public void testAuditCreateAndDeleteExpressionExperiment() {
        ExpressionExperiment ee = this.getTestPersistentCompleteExpressionExperiment(true);

        Collection<Long> trailIds = new HashSet<>();
        Collection<Long> eventIds = new HashSet<>();

        this.checkEEAuditTrails(ee, trailIds, eventIds);

        assertEquals(1, ee.getAuditTrail().getEvents().size());

        ee = expressionExperimentService.load(ee.getId()); // so not thawed, which tests lazy issue in the advice.

        ee.setShortName(this.randomName());

        expressionExperimentService.update(ee);

        ee = expressionExperimentService.thawLite(ee);

        // make sure we added an update event on the ee
        assertEquals(2, auditEventService.getEvents(ee).size());

        expressionExperimentService.remove(ee);

        this.checkDeletedTrails(trailIds, eventIds);

    }

    @Test
    public void testCascadingCreateOnUpdate() {
        ExpressionExperiment ee = this.getTestPersistentCompleteExpressionExperiment(false);

        ee = this.expressionExperimentService.load(ee.getId());
        ee = expressionExperimentService.thawLite(ee);

        // should have create only
        assertEquals(1, ee.getAuditTrail().getEvents().size());

        BioAssay ba = BioAssay.Factory.newInstance();
        String name = RandomStringUtils.randomAlphabetic(20);
        ba.setName(name);
        ba.setArrayDesignUsed(ee.getBioAssays().iterator().next().getArrayDesignUsed());
        ee.getBioAssays().add(ba);

        this.expressionExperimentService.update(ee);
        assertNotNull(ee.getAuditTrail());

        // should have create and 1 updates
        assertEquals(2, ee.getAuditTrail().getEvents().size());

        Session session = sessionFactory.openSession();
        session.update(ee);

        session.close();

        this.expressionExperimentService.update(ee);
        this.expressionExperimentService.update(ee);
        this.expressionExperimentService.update(ee);

        assertEquals(5, ee.getAuditTrail().getEvents().size());

    }

    @Test
    public void testCascadingCreateWithAssociatedAuditable() {
        ExpressionExperiment ee = this.getTestPersistentCompleteExpressionExperiment(false);

        ee = this.expressionExperimentService.load(ee.getId());
        ee = expressionExperimentService.thawLite(ee);

        assertEquals(16, ee.getBioAssays().size());

        assertNotNull(ee.getBioAssays().iterator().next().getId());

        assertEquals(1, ee.getAuditTrail().getEvents().size());

    }

    @Test
    public void testSimpleAuditFindOrCreate() {
        ExpressionExperiment ee = ExpressionExperiment.Factory.newInstance();
        ee.setDescription("From test");
        ee.setName(RandomStringUtils.randomAlphabetic(20));
        ee.setTaxon(taxonService.load(1L));
        ee = expressionExperimentService.findOrCreate(ee);

        assertNotNull(ee.getAuditTrail());
        assertEquals(1, ee.getAuditTrail().getEvents().size());
        assertNotNull(ee.getCurationDetails());
        assertNotNull(ee.getCurationDetails().getId());
        assertNotNull(ee.getCurationDetails().getLastUpdated());
        assertNotNull(ee.getAuditTrail().getCreationEvent().getId());
    }

    /*
     * Torture test. Passes fine with a single thread.
     */
    @SuppressWarnings("Duplicates") // Not in this project
    @Test
    public void testAuditFindOrCreateConcurrentTorture() throws Exception {
        int numThreads = 14; // too high and we run out of connections, which is not what we're testing.
        final int numExperimentsPerThread = 5;
        final int numUpdates = 10;
        final Random random = new Random();
        final AtomicInteger c = new AtomicInteger(0);
        final AtomicBoolean failed = new AtomicBoolean(false);
        Collection<Thread> threads = new HashSet<>();
        for (int i = 0; i < numThreads; i++) {

            Thread.sleep(random.nextInt(100));

            Thread k = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for (int j = 0; j < numExperimentsPerThread; j++) {
                            log.debug("Starting experiment " + j);
                            ExpressionExperiment ee = ExpressionExperiment.Factory.newInstance();
                            ee.setDescription("From test");
                            ee.setShortName(RandomStringUtils.randomAlphabetic(20));
                            ee.setName(RandomStringUtils.randomAlphabetic(20));
                            ee.setTaxon(taxonService.load(1L));
                            ee = expressionExperimentService.findOrCreate(ee);

                            assertNotNull(ee.getAuditTrail());
                            assertEquals(1, ee.getAuditTrail().getEvents().size());
                            assertNotNull(ee.getCurationDetails());
                            assertNotNull(ee.getCurationDetails().getId());
                            assertNotNull(ee.getCurationDetails().getLastUpdated());
                            assertNotNull(ee.getAuditTrail().getCreationEvent().getId());

                            for (int q = 0; q < numUpdates; q++) {
                                Thread.sleep(random.nextInt(5));
                                log.debug("Update: experiment " + j);
                                expressionExperimentService.update(ee);
                                c.incrementAndGet();
                            }
                            log.debug("Done with experiment " + j);
                        }
                    } catch (Exception e) {
                        failed.set(true);
                        log.error("!!!!!!!!!!!!!!!!!!!!!! FAILED: " + e.getMessage());
                        log.debug(e, e);
                        throw new RuntimeException(e);
                    }
                    log.debug("Thread done.");
                }
            });
            threads.add(k);

            k.start();
        }

        int waits = 0;
        int maxWaits = 20;
        int expectedEventCount = numThreads * numExperimentsPerThread * numUpdates;
        while (c.get() < expectedEventCount && !failed.get()) {
            Thread.sleep(1000);
            log.info("Waiting ...");
            if (++waits > maxWaits) {
                for (Thread t : threads) {
                    if (t.isAlive())
                        t.interrupt();
                }
                fail("Multithreaded failure: timed out.");
            }
        }

        log.debug(" &&&&& DONE &&&&&");

        for (Thread thread : threads) {
            if (thread.isAlive())
                thread.interrupt();
        }

        if (failed.get() || c.get() != expectedEventCount) {
            fail("Multithreaded loading failure: check logs for failure to recover from deadlock?");
        } else {
            log.info("TORTURE TEST PASSED!");
        }

    }

    private void checkAuditTrail(Auditable c, Collection<Long> trailIds, Collection<Long> eventIds) {

        AuditTrail auditTrail = c.getAuditTrail();
        assertNotNull("No audit trail for " + c, auditTrail);

        trailIds.add(auditTrail.getId());

        assertTrue("Trail but no events for " + c, auditTrail.getEvents().size() > 0);

        for (AuditEvent ae : auditTrail.getEvents()) {
            eventIds.add(ae.getId());
        }

    }

    private boolean checkDeletedAuditTrail(Long atid) {
        return this.simpleJdbcTemplate.queryForObject("SELECT COUNT(*) FROM AUDIT_TRAIL WHERE ID = ?",
                Integer.class, atid) == 0;
    }

    private boolean checkDeletedEvent(Long i) {
        return this.simpleJdbcTemplate.queryForObject("SELECT COUNT(*) FROM AUDIT_EVENT WHERE ID = ?",
                Integer.class, i) == 0;
    }

    private void checkDeletedTrails(Collection<Long> trailIds, Collection<Long> eventIds) {

        for (Long id : trailIds) {
            assertTrue(this.checkDeletedAuditTrail(id));
        }

        for (Long id : eventIds) {
            assertTrue(this.checkDeletedEvent(id));
        }

    }

    private void checkEEAuditTrails(ExpressionExperiment ee, Collection<Long> trailIds, Collection<Long> eventIds) {
        this.checkAuditTrail(ee, trailIds, eventIds);

        Collection<ExperimentalFactor> experimentalFactors = ee.getExperimentalDesign().getExperimentalFactors();
        assertTrue(experimentalFactors.size() > 0);

    }

}