import edu.ucsb.eucalyptus.cloud.entities.AOEMetaInfo; import edu.ucsb.eucalyptus.cloud.entities.AOEVolumeInfo; import edu.ucsb.eucalyptus.cloud.entities.ARecordInfo; import edu.ucsb.eucalyptus.cloud.entities.CNAMERecordInfo; import edu.ucsb.eucalyptus.cloud.entities.ClusterInfo; import edu.ucsb.eucalyptus.cloud.entities.DirectStorageInfo; import edu.ucsb.eucalyptus.cloud.entities.ISCSIMetaInfo; import edu.ucsb.eucalyptus.cloud.entities.ISCSIVolumeInfo; import edu.ucsb.eucalyptus.cloud.entities.ImageCacheInfo; import edu.ucsb.eucalyptus.cloud.entities.LVMVolumeInfo; import edu.ucsb.eucalyptus.cloud.entities.MetaDataInfo; import edu.ucsb.eucalyptus.cloud.entities.NSRecordInfo; import edu.ucsb.eucalyptus.cloud.entities.SOARecordInfo; import edu.ucsb.eucalyptus.cloud.entities.SnapshotInfo; import edu.ucsb.eucalyptus.cloud.entities.StorageInfo; import edu.ucsb.eucalyptus.cloud.entities.StorageStatsInfo; import edu.ucsb.eucalyptus.cloud.entities.SystemConfiguration; import edu.ucsb.eucalyptus.cloud.entities.TorrentInfo; import edu.ucsb.eucalyptus.cloud.entities.VolumeInfo; import edu.ucsb.eucalyptus.cloud.entities.WalrusInfo; import edu.ucsb.eucalyptus.cloud.entities.WalrusSnapshotInfo; import edu.ucsb.eucalyptus.cloud.entities.WalrusStatsInfo; import edu.ucsb.eucalyptus.cloud.entities.ZoneInfo; import edu.ucsb.eucalyptus.cloud.state.AbstractIsomorph; import edu.ucsb.eucalyptus.cloud.state.State; import groovy.sql.GroovyRowResult; import groovy.sql.Sql; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import javax.persistence.PersistenceContext; import javax.persistence.Table; import org.apache.log4j.Logger; import com.eucalyptus.address.Address; import com.eucalyptus.blockstorage.Snapshot; import com.eucalyptus.blockstorage.Volume; import com.eucalyptus.config.ClusterConfiguration; import com.eucalyptus.config.StorageControllerConfiguration; import com.eucalyptus.config.WalrusConfiguration; import com.eucalyptus.entities.AbstractPersistent; import com.eucalyptus.entities.Counters; import com.eucalyptus.entities.EntityWrapper; import com.eucalyptus.entities.SshKeyPair; import com.eucalyptus.images.ImageInfo; import com.eucalyptus.upgrade.AbstractUpgradeScript; import com.eucalyptus.upgrade.StandalonePersistence; import com.eucalyptus.upgrade.UpgradeScript; class Upgrade1_6_2to2_0_0 extends AbstractUpgradeScript { static final String FROM_VERSION = "1.6.2"; static final String TO_VERSION = "2.0.0"; private static Logger LOG = Logger.getLogger( Upgrade1_6_2to2_0_0.class ); private static List<Class> entities = new ArrayList<Class>(); private static Map<String, Class> entityMap = new HashMap<String, Class>(); public Upgrade1_6_2to2_0_0() { super(0); } @Override public Boolean accepts( String from, String to ) { if(FROM_VERSION.equals(from) && TO_VERSION.equals(to)) return true; return false; } @Override public void upgrade(File oldEucaHome, File newEucaHome) { buildEntityMap(); Set<String> entityKeys = entityMap.keySet(); for (String entityKey : entityKeys) { String contextName = getContextName(entityKey); Sql conn = getConnection(contextName); if (conn != null) { Map<String, Method> setterMap = buildSetterMap(conn, entityKey); if(setterMap != null) doUpgrade(contextName, conn, entityKey, setterMap); } } } private Sql getConnection(String contextName) { try { Sql conn = StandalonePersistence.getConnection(contextName); return conn; } catch (SQLException e) { LOG.error(e); return null; } } private String getContextName(String entityKey) { Class entity = entityMap.get(entityKey); if (entity != null) { if(entity.isAnnotationPresent(PersistenceContext.class)) { PersistenceContext annot = (PersistenceContext) entity.getAnnotation(PersistenceContext.class); return annot.name(); } } return null; } private void doUpgrade(String contextName, Sql conn, String entityKey, Map<String, Method> setterMap) { List<GroovyRowResult> rowResults; try { rowResults = conn.rows("SELECT * FROM " + entityKey); EntityWrapper db = new EntityWrapper(contextName); for (GroovyRowResult rowResult : rowResults) { Set<String> columns = rowResult.keySet(); Object dest; try { dest = ClassLoader.getSystemClassLoader().loadClass(entityMap.get(entityKey).getCanonicalName()).newInstance(); } catch (ClassNotFoundException e1) { LOG.error(e1); break; } catch (InstantiationException e) { LOG.error(e); break; } catch (IllegalAccessException e) { LOG.error(e); break; } for (String column : columns) { Method setter = setterMap.get(column); if(setter != null) { Object o = rowResult.get(column); if(o != null) { try { if(dest instanceof AbstractIsomorph && (setter.getName().equals("setState"))) { o = State.valueOf((String)o); } if(dest instanceof Volume && (setter.getName().equals("setRemoteDevice"))) { ((Volume)dest).setRemoteDevice(null); } else { setter.invoke(dest, o); } } catch (IllegalArgumentException e) { LOG.error(dest.getClass().getName() + " " + column + " " + e); } catch (IllegalAccessException e) { LOG.error(dest.getClass().getName() + " " + column + " " + e); } catch (InvocationTargetException e) { LOG.error(dest.getClass().getName() + " " + column + " " + e); } } } } LOG.debug("Upgraded: " + dest.getClass().getName()); db.add(dest); } db.commit(); } catch (SQLException e) { LOG.error(e); return; } } private Map<String, Method> buildSetterMap(Sql conn, String entityKey) { Map<String, Method> setterMap = new HashMap<String, Method>(); try { Object firstRow = conn.firstRow("SELECT * FROM " + entityKey); if(firstRow == null) { LOG.warn("Unable to find anything in table: " + entityKey); return null; } if(firstRow instanceof Map) { Set<String> columnNames = ((Map) firstRow).keySet(); Class definingClass = entityMap.get(entityKey); Field[] fields = definingClass.getDeclaredFields(); //special case. Do this better. addToSetterMap(setterMap, columnNames, definingClass, fields); Class superClass = entityMap.get(entityKey).getSuperclass(); while(superClass.isAnnotationPresent(MappedSuperclass.class)) { Field[] superFields = superClass.getDeclaredFields(); addToSetterMap(setterMap, columnNames, superClass, superFields); //nothing to see here (otherwise we loop forever). if(superClass.equals(AbstractPersistent.class)) break; superClass = superClass.getSuperclass(); } /*for (String column : columnNames) { if(!setterMap.containsKey(column)) { LOG.warn("No corresponding field for column: " + column + " found"); } }*/ } } catch (SQLException e) { LOG.error(e); } return setterMap; } private void addToSetterMap(Map<String, Method> setterMap, Set<String> columnNames, Class definingClass, Field[] fields) { for(String column : columnNames) { for(Field f : fields) { if(f.isAnnotationPresent(Column.class) && !f.isAnnotationPresent(Id.class)) { Column annotClass = (Column)f.getAnnotation(Column.class); if(((String)column).toLowerCase().equals(annotClass.name().toLowerCase())) { String baseMethodName = f.getName( ).substring( 0, 1 ).toUpperCase( ) + f.getName( ).substring( 1 ); try { Class[] classes = new Class[1]; classes[0] = f.getType(); Method setMethod = definingClass.getDeclaredMethod( "set" + baseMethodName, classes ); setterMap.put(column, setMethod); } catch (SecurityException e) { LOG.error(e); } catch (NoSuchMethodException e) { LOG.error(e); } break; } } } /*if(setterMap.containsKey(column)) { LOG.debug(column + " is set by: " + setterMap.get(column).getName()); }*/ } } private void buildEntityMap() { for (Class entity : entities) { if (entity.isAnnotationPresent(Table.class)) { Table annot = (Table)entity.getAnnotation(Table.class); entityMap.put(annot.name(), entity); } } //special cases entityMap.put("SNAPSHOT", Snapshot.class); entityMap.put("VOLUME", Volume.class); entityMap.put("STORAGE_INFO", DirectStorageInfo.class); } static { //this is added by hand because there are special cases/entities that other upgrade scripts will process. //In the future, this should be discovered. //entities.add(BucketInfo.class); //entities.add(ObjectInfo.class); //entities.add(GrantInfo.class); entities.add(MetaDataInfo.class); entities.add(ImageCacheInfo.class); entities.add(TorrentInfo.class); entities.add(WalrusSnapshotInfo.class); entities.add(WalrusInfo.class); entities.add(WalrusStatsInfo.class); entities.add(VolumeInfo.class); entities.add(SnapshotInfo.class); entities.add(AOEMetaInfo.class); entities.add(AOEVolumeInfo.class); entities.add(ISCSIMetaInfo.class); entities.add(ISCSIVolumeInfo.class); entities.add(LVMVolumeInfo.class); entities.add(StorageInfo.class); entities.add(StorageStatsInfo.class); entities.add(ARecordInfo.class); entities.add(CNAMERecordInfo.class); entities.add(SOARecordInfo.class); entities.add(NSRecordInfo.class); entities.add(ZoneInfo.class); entities.add(com.eucalyptus.config.System.class); entities.add(ClusterConfiguration.class); entities.add(StorageControllerConfiguration.class); entities.add(WalrusConfiguration.class); entities.add(SystemConfiguration.class); entities.add(ImageInfo.class); entities.add(Address.class); entities.add(ClusterInfo.class); entities.add(Counters.class); entities.add(SshKeyPair.class); } }