/*
* Copyright (c) 2008-2013 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.db.server.upgrade;
import java.util.*;
import com.emc.storageos.db.server.upgrade.DbSimpleMigrationTestBase;
import javassist.*;
import javassist.util.*;
import javassist.bytecode.*;
import javassist.bytecode.annotation.*;
import org.junit.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The DB migration test base class
*/
public abstract class DbMigrationTestBase extends DbSimpleMigrationTestBase {
private static final Logger _log = LoggerFactory.getLogger(DbMigrationTestBase.class);
protected static volatile HotSwapper hs = null;
/**
* @return the DB version upgraded from
*/
protected abstract String getSourceVersion();
/**
* @return the DB version upgraded to
*/
protected abstract String getTargetVersion();
/**
* Implement this method to create test data to be migrated
*
* @throws Exception
*/
protected abstract void prepareData() throws Exception;
/**
* Implement this method to verify that your test data was properly migrated
*
* @throws Exception
*/
protected abstract void verifyResults() throws Exception;
/**
* Sub-classes can implement this method if they wish to make modifications
* to the DB schema after "before migration" database setup has been completed
* and prepareData() has executed, but prior to running the migration.
*
* The default implementation does nothing.
*
* @throws Exception
*/
protected void changeSchema() throws Exception {
}
@Test
@Override
public void runTest() throws Exception {
setupDB();
prepareData();
stopAll();
changeSchema();
runMigration();
verifyResults();
}
protected void setupDB() throws Exception {
if (hs == null) {
// Suppress Sonar violation of Lazy initialization of static fields should be synchronized
// Junit test will be called in single thread by default, it's safe to ignore this violation
hs = new HotSwapper(8000); // NOSONAR ("squid:S2444")
}
super.setupDB();
}
// Note: the className and annotationName should be full package names
protected static void addAnnotation(String className, String methodName,
String annotationName, Map<String, Object> values) throws Exception {
// pool creation
ClassPool pool = ClassPool.getDefault();
// extracting the class
CtClass cc = pool.getCtClass(className);
cc.defrost();
// looking for the method to apply the annotation on
CtMethod methodDescriptor = cc.getDeclaredMethod(methodName);
// create the annotation
ClassFile ccFile = cc.getClassFile();
ccFile.setVersionToJava5();
ConstPool constpool = ccFile.getConstPool();
MethodInfo minfo = methodDescriptor.getMethodInfo();
AnnotationsAttribute attr = (AnnotationsAttribute) minfo.getAttribute(AnnotationsAttribute.visibleTag);
Annotation annot = new Annotation(annotationName, constpool);
Set<Map.Entry<String, Object>> entries = values.entrySet();
for (Map.Entry<String, Object> entry : entries) {
String attrName = entry.getKey();
Object attrValue = entry.getValue();
if (attrValue instanceof String) {
annot.addMemberValue(attrName, new StringMemberValue((String) attrValue, ccFile.getConstPool()));
} else {
throw new RuntimeException(String.format("Unsupported attribute type %s of %s", attrName, attrValue));
}
}
attr.addAnnotation(annot);
// add the annotation to the method descriptor
minfo.addAttribute(attr);
_log.info("add {} to method {}", attr, methodDescriptor);
// transform the ctClass to java class
// so we can get the new added annotations through Class object.
byte[] byteCodes = cc.toBytecode();
hs.reload(className, byteCodes);
}
protected static void removeAnnotation(String className, String methodName, String annotationName) throws Exception {
// pool creation
ClassPool pool = ClassPool.getDefault();
// extracting the class
CtClass cc = pool.getCtClass(className);
cc.defrost();
// looking for the method to apply the annotation on
CtMethod methodDescriptor = cc.getDeclaredMethod(methodName);
// create the annotation
ClassFile ccFile = cc.getClassFile();
ccFile.setVersionToJava5();
ConstPool constpool = ccFile.getConstPool();
MethodInfo minfo = methodDescriptor.getMethodInfo();
AnnotationsAttribute attr = (AnnotationsAttribute) minfo.getAttribute(AnnotationsAttribute.visibleTag);
Annotation[] annotations = attr.getAnnotations();
List<Annotation> list = new ArrayList();
for (Annotation annotation : annotations) {
if (!annotation.getTypeName().equals(annotationName)) {
list.add(annotation);
}
}
Annotation[] newAnnotations = list.toArray(new Annotation[0]);
attr.setAnnotations(newAnnotations);
minfo.addAttribute(attr);
// transform the ctClass to java class
// so we can get the new added annotations through Class object.
byte[] byteCodes = cc.toBytecode();
hs.reload(className, byteCodes);
}
}