package org.hyperic.hq.vm; import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.PostConstruct; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.FlushMode; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hyperic.hq.appdef.shared.CPropKeyNotFoundException; import org.hyperic.hq.appdef.shared.PlatformManager; import org.hyperic.hq.authz.shared.AuthzSubjectManager; import org.hyperic.hq.authz.shared.PermissionException; import org.hyperic.hq.common.ApplicationException; import org.hyperic.hq.common.SystemException; import org.hyperic.hq.common.shared.HQConstants; import org.hyperic.hq.common.shared.ServerConfigManager; import org.hyperic.hq.hibernate.SessionManager; import org.hyperic.hq.hibernate.SessionManager.SessionRunner; import org.hyperic.util.ConfigPropertyException; import org.hyperic.util.StringUtil; import org.jasypt.hibernate.encryptor.HibernatePBEStringEncryptor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.orm.hibernate3.SessionFactoryUtils; import org.springframework.orm.hibernate3.SessionHolder; import org.springframework.orm.hibernate3.support.OpenSessionInViewFilter; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronizationManager; import com.vmware.vim25.Event; import com.vmware.vim25.EventFilterSpec; import com.vmware.vim25.EventFilterSpecByTime; import com.vmware.vim25.GuestInfo; import com.vmware.vim25.GuestNicInfo; import com.vmware.vim25.InvalidProperty; import com.vmware.vim25.ManagedObjectReference; import com.vmware.vim25.RuntimeFault; import com.vmware.vim25.VmEvent; import com.vmware.vim25.mo.Folder; import com.vmware.vim25.mo.InventoryNavigator; import com.vmware.vim25.mo.ManagedEntity; import com.vmware.vim25.mo.ServerConnection; import com.vmware.vim25.mo.ServiceInstance; import com.vmware.vim25.mo.VirtualMachine; @SuppressWarnings("restriction") @Service public class VCManagerImpl implements VCManager, ApplicationContextAware { private static final String POWER_ON_VM_TASK = "PowerOnVM_Task"; private static final String RESET_VM_TASK = "ResetVM_Task"; private static final String SUSPEND_VM_TASK = "SuspendVM_Task"; private static final String POWER_OFF_VM_TASK = "PowerOffVM_Task"; private static final String[] DISABLED_METHOD_FOR_INACTIVE_VM = {POWER_ON_VM_TASK, RESET_VM_TASK, SUSPEND_VM_TASK, POWER_OFF_VM_TASK}; private static final String VC_SYNCHRONIZER = "VCSynchronizer"; protected final Log log = LogFactory.getLog(VCManagerImpl.class.getName()); protected final VCDAO vcDao; protected final VCConfigDAO vcConfigDao; private final AuthzSubjectManager authzSubjectManager; private final Set<VCConfig> vcConfigs = new HashSet<VCConfig>(); private ScheduledThreadPoolExecutor executor ; private ApplicationContext appContext; private final int SYNC_INTERVAL_MINUTES; private final PlatformManager platformManager; private final ServerConfigManager serverConfigManager; @Autowired public VCManagerImpl(VCDAO vcDao, HibernatePBEStringEncryptor encryptor, AuthzSubjectManager authzSubjectManager, PlatformManager platformManager, VCConfigDAO vcConnectionDao, ServerConfigManager serverConfigManager, @Value("#{VCProperties['vc.sync.interval.minutes']}") int syncInterval){ this.vcDao = vcDao; this.platformManager = platformManager; this.SYNC_INTERVAL_MINUTES = syncInterval; this.authzSubjectManager = authzSubjectManager; this.vcConfigDao = vcConnectionDao; this.serverConfigManager = serverConfigManager; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.appContext = applicationContext; } @PostConstruct void initialize() { //get the vCenter credentials from the database try { SessionManager.runInSession(new SessionRunner() { public void run() throws Exception { convertOldVCConfigsFromDB(); loadVCConfigsFromDB(); } public String getName() { return "VCManagerImpl"; } }); }catch(Exception e) { log.error(e,e); } } @Transactional(readOnly = true) private void loadVCConfigsFromDB() { List<VCConfig> vcDBConfigs = getVCConfigsFromDB(); if (null != executor) { executor.shutdown(); } executor = new ScheduledThreadPoolExecutor(1, new ThreadFactory() { private final AtomicLong i = new AtomicLong(0); public Thread newThread(Runnable r) { final Thread rtn = new Thread(r, VC_SYNCHRONIZER + i.getAndIncrement()); rtn.setDaemon(true); return rtn; } }); vcConfigs.clear(); //create a scheduled 'sync task' for each vCenter for (VCConfig conf : vcDBConfigs) { vcConfigs.add(conf); executor.scheduleWithFixedDelay(new VCSynchronizer(conf), 0, SYNC_INTERVAL_MINUTES, TimeUnit.MINUTES); } } /** * @param conf - the credentials of the vCenter */ @Transactional(readOnly = false) private void doVCEventsScan(VCConfig conf) { Event[] events = null; ServiceInstance si = null; EventFilterSpec filterSpec; Set<ManagedEntity> vms= new HashSet<ManagedEntity>(); Folder rootFolder; InventoryNavigator navigator = null; List<VmMapping> vmsMapping = null; boolean doFullSync = false; try { si = new ServiceInstance(new URL(conf.getUrl()), conf.getUser(), conf.getPassword(), true); rootFolder = si.getRootFolder(); navigator = new InventoryNavigator(rootFolder); //if the last sync was not successful - run a full sync if (!conf.lastSyncSucceeded()) { log.info("Collection full inventory for '" + conf.getUrl() + "'"); if (null == doVCFullScan(si)){ conf.setLastSyncSucceeded(false); } else { conf.setLastSyncSucceeded(true); } return; } filterSpec = createVCEventsFilter(); //query last events events = si.getEventManager().queryEvents(filterSpec); if (null == events) { return; } Set<String> addedEntities = new HashSet<String>(); for (Event event : events) { //we only care about virtual machine's events if (event instanceof VmEvent){ ManagedEntity vm = navigator.searchManagedEntity("VirtualMachine", event.vm.name); //if the vm is null we need to run a full scan because it probably means that //this vm was deleted if (null == vm) { doFullSync = true; break; } if(!addedEntities.contains(vm.getName())) { vms.add(vm); addedEntities.add(vm.getName()); } } } String vcUUID = si.getServiceContent().getAbout().getInstanceUuid(); if (doFullSync) { doVCFullScan(si); return; } if (!vms.isEmpty()) { //do the mapping only for virtual machines that we got events on vmsMapping = mapVMToMacAddresses(vms, vcUUID); } } catch (InvalidProperty e) { log.error(e, e); conf.setLastSyncSucceeded(false); return; } catch (RuntimeFault e) { log.error(e, e); conf.setLastSyncSucceeded(false); return; } catch (RemoteException e) { log.error(e, e); conf.setLastSyncSucceeded(false); return; } catch (MalformedURLException e) { log.error(e, e); conf.setLastSyncSucceeded(false); return; } finally{ if (si!=null) { ServerConnection sc = si.getServerConnection(); if (sc!=null) { sc.logout(); } } } //mark this sync as successful conf.setLastSyncSucceeded(true); //persist the mapping persistMapping(vmsMapping); } /** * @return a vCenter 'time based' events filter - we want to get all the events that happened in the last * 2 * sync_interval minutes just to make sure we don't miss anything, for example - if we pull the vCenter * events every 2 minutes, each time we will ask for all the events that happened in the last 4 minutes */ private EventFilterSpec createVCEventsFilter() { EventFilterSpec filterSpec; EventFilterSpecByTime timeSpec; filterSpec = new EventFilterSpec(); timeSpec = new EventFilterSpecByTime(); Calendar begin = (Calendar) Calendar.getInstance().clone(); begin.setTimeInMillis(System.currentTimeMillis() - (SYNC_INTERVAL_MINUTES * 2 * 60 * 1000)); timeSpec.setBeginTime(begin); timeSpec.setEndTime(Calendar.getInstance()); filterSpec.setTime(timeSpec); return filterSpec; } public boolean vcConfigExistsByUrl(String url) { for (VCConfig connection : vcConfigs) { if (connection.getUrl().equalsIgnoreCase(url)) { return true; } } return false; } /* (non-Javadoc) * @see org.hyperic.hq.vm.VCManager#getActiveVCConfings() */ public Set<VCConfig> getActiveVCConfings(){ return vcConfigs; } /* (non-Javadoc) * @see org.hyperic.hq.vm.VCManager#getActiveVCConfingsAsString() */ public String getActiveVCConfingsAsString() { StringBuffer sb = new StringBuffer(); sb.append("vCenter configurations - ").append("\n"); for (VCConfig config:vcConfigs) { sb.append(config.toString()).append("\n"); } return sb.toString(); } /** * @return a Set of VC credentials from the database of all the vCenters 'registered' for VM => macs mapping */ private List<VCConfig> getVCConfigsFromDB() { return vcConfigDao.findAll(); } @Transactional(readOnly = false) private void convertOldVCConfigsFromDB() { //after upgrade from versions < 5.8 //we need to convert the vCenter config entries //to the new format Properties conf; Set<String> keysToDelete = new HashSet<String>(); try { conf = serverConfigManager.getConfig(); } catch (ConfigPropertyException ex) { throw new SystemException(ex); } for (int i=0; ; i++) { String vCenterURL = conf.getProperty(HQConstants.vCenterURL + "_" + i); String vCenterUser = conf.getProperty(HQConstants.vCenterUser+ "_" + i); String vCenterPassword = conf.getProperty(HQConstants.vCenterPassword+ "_" + i); if ((null == vCenterURL) || (null == vCenterUser) || (null == vCenterPassword)) { break; } keysToDelete.add(HQConstants.vCenterURL + "_" + i); keysToDelete.add(HQConstants.vCenterUser + "_" + i); keysToDelete.add(HQConstants.vCenterPassword + "_" + i); VCConfig vcConfig = new VCConfig(vCenterURL, vCenterUser, vCenterPassword); if (i == 0) { vcConfig.setSetByUI(true); } setVcUuid(vcConfig); vcConfigDao.save(vcConfig); vcConfigDao.getSession().flush(); vcConfigDao.getSession().clear(); } if (!keysToDelete.isEmpty()) { try { serverConfigManager.deleteConfig(authzSubjectManager.getOverlordPojo(), keysToDelete); }catch(Exception ex) { throw new SystemException(ex); } } } /** * @param me - the managed entity (the virtual machine) * @param vcUUID - the vCenter UUID * @return - an instance of VmMapping that represents the given virtual machine * or null in case we cannot extract all the mandatory information from the managed entity */ private VmMapping getVMFromManagedEntity(ManagedEntity me, String vcUUID) { GuestNicInfo[] nics = null; VirtualMachine vm = (VirtualMachine)me; //Check if this VM is an inactive or a fault tolerant second machine if (Arrays.asList(vm.getDisabledMethod()). containsAll(Arrays.asList(DISABLED_METHOD_FOR_INACTIVE_VM))){ if (log.isDebugEnabled()) { log.debug("Found an inactive or a fault tolerant second machine '" + vm.getName() + "'"); } return null; } String vmName = vm.getName(); GuestInfo guest = vm.getGuest(); final boolean debug = log.isDebugEnabled(); if (guest==null) { if (debug) log.debug("no guest for vm " + vmName); return null; } ManagedObjectReference moref = vm.getMOR(); if (moref==null) { if (debug) log.debug("no moref is defined for vm " + vmName); return null; } nics = guest.getNet(); if (debug && ((nics == null) || (nics.length==0))) { log.debug("no nics defined on vm " + vmName); } VmMapping vmMapping = new VmMapping(moref.getVal(),vcUUID); vmMapping.setName(vm.getName()); vmMapping.setGuestNicInfo(nics); return vmMapping; } /** * @param vms - a collection of virtual machines * @param vcUUID - the UUID of the vCenter * @return a list of VmMapping objects */ private List<VmMapping> mapVMToMacAddresses(Collection<ManagedEntity> vms, String vcUUID) { final boolean debug = log.isDebugEnabled(); List<VmMapping> mapping = new ArrayList<VmMapping>(); // this is done in order to prevent gathering of VMs which share identical mac addresses Map<String,VmMapping> overallMacsSet = new HashMap<String,VmMapping>(); for (ManagedEntity me : vms) { Set<String> macs = new HashSet<String>(); VmMapping vmMapping = getVMFromManagedEntity(me, vcUUID); if (null == vmMapping || null == vmMapping.getGuestNicInfo()) { // Print specific message for debugging if ((null == vmMapping) && (log.isDebugEnabled())){ log.debug("vmMapping is null"); }else if ((vmMapping != null) && (null == vmMapping.getGuestNicInfo()) && (log.isDebugEnabled())){ log.debug("Nic is null for vmMapping [" + vmMapping + "]" ); } continue; } boolean foundDupMacOnCurrVM = false; for (int i=0; i<vmMapping.getGuestNicInfo().length ; i++) { if (vmMapping.getGuestNicInfo()[i]==null) { if (debug) { log.debug("nic no. " + i + " is null on " + vmMapping); } continue; } String mac = vmMapping.getGuestNicInfo()[i].getMacAddress(); if ((mac==null) || "00:00:00:00:00:00".equals(mac)) { if (debug) { log.debug("no mac address / mac address is 00:00:00:00:00:00 on nic" + vmMapping.getGuestNicInfo()[i] + " of vm " + vmMapping); } continue; } mac = mac.toUpperCase(); macs.add(mac); VmMapping dupMacVM = overallMacsSet.get(mac); if (dupMacVM!=null) { if (log.isDebugEnabled()){ log.debug("Found dupMacVM [" + dupMacVM + "], mac [" + mac + "], vmMapping [" + vmMapping + "]\n" + "Number of defined nics for [" + vmMapping.getMoId() + "] [" + vmMapping.getGuestNicInfo().length + "]"); } // remove the other VM with the duplicate mac from the response object, as this is illegal mapping.remove(dupMacVM); foundDupMacOnCurrVM = true; try { //check if we already have saved virtual machines with this mac address //and if there are such machines - delete them VmMapping existingVM = vcDao.findVMByMac(mac); if (null != existingVM) { persistMapping(null, Arrays.asList(existingVM)); } }catch(DupMacException e) { log.warn("Duplicate Mac exists!", e); } continue; }else { overallMacsSet.put(mac,vmMapping); } } if (!foundDupMacOnCurrVM) { String macsString = ""; for(String mac : macs) { macsString += mac; macsString += ";"; } vmMapping.setMacs(macsString); mapping.add(vmMapping); } } return mapping; } /** * @param toSave - the list of VmMapping objects we want to save in the database */ private void persistMapping(List<VmMapping> toSave) { persistMapping(toSave, null); } /** * @param toSave - the list of VmMapping objects we want to save in the database * @param toRemove - the list of VmMapping objects we want to remove from the database */ private void persistMapping(List<VmMapping> toSave, List<VmMapping> toRemove) { if((null != toRemove) && (!toRemove.isEmpty())) { this.vcDao.remove(toRemove); List<String> macAddresses = new ArrayList<String>(); for (VmMapping mapping : toRemove) { macAddresses.addAll(Arrays.asList(mapping.getMacs().split(";"))); } try { platformManager.removePlatformVmMapping(authzSubjectManager.getOverlordPojo(), macAddresses); } catch (PermissionException e) { log.error(e,e); } } if((null != toSave) && (!toSave.isEmpty())) { this.vcDao.save(toSave); try { platformManager.mapUUIDToPlatforms(authzSubjectManager.getOverlordPojo(), toSave); } catch (CPropKeyNotFoundException e) { log.error(e, e); } catch (PermissionException e) { log.error(e, e); } } } /** * @param si - the 'connection instance' to the vCenter * @return a list of VmMapping objects */ @Transactional(readOnly = false) private List<VmMapping> doVCFullScan(ServiceInstance si) throws InvalidProperty, RuntimeFault, RemoteException { List<VmMapping> activeVms = null; String vcUUID = si.getServiceContent().getAbout().getInstanceUuid(); Folder rootFolder = si.getRootFolder(); ManagedEntity[] me = new InventoryNavigator(rootFolder).searchManagedEntities("VirtualMachine"); if((me==null) || (me.length==0)){ if (log.isDebugEnabled()) { log.debug("no virtual machines were discovered on " + vcUUID); } return null; } activeVms = mapVMToMacAddresses(Arrays.asList(me), vcUUID); if (log.isDebugEnabled()) { log.debug("Active VMs\n" + activeVms); } List<VmMapping> deletedVms = new ArrayList<VmMapping>(); for (VmMapping mapping : vcDao.findByVcUUID(si.getServiceContent().getAbout().getInstanceUuid())) { if (!activeVms.contains(mapping)) { deletedVms.add(mapping); if (log.isDebugEnabled()) { log.debug("Add to deletedVms [" + mapping + "]"); } } } //Now check if there are virtual machines with the same mac address in other vCenters for (VmMapping existingMapping : vcDao.getVMsFromOtherVcenters(si.getServiceContent().getAbout().getInstanceUuid())) { Iterator<VmMapping> iter = activeVms.iterator(); while (iter.hasNext()) { VmMapping newMapping = iter.next(); for (String mac : existingMapping.getMacs().split(";")) { //Found duplicate mac address, remove both of the machines if (newMapping.macs.contains(mac)) { if (log.isDebugEnabled()){ log.debug("Found duplicate mac. remove mac [" + newMapping.macs + "], vmMapping [" + newMapping + "]"); } iter.remove(); deletedVms.add(existingMapping); } } } } if (log.isDebugEnabled()){ log.debug("Deleted VMs [" + deletedVms + "]\n" + "Active VMs [" + activeVms + "]"); } vcDao.getSession().clear(); persistMapping(activeVms, deletedVms); return activeVms; } /* (non-Javadoc) * @see org.hyperic.hq.vm.VCManager#getVMID(java.util.List) */ public VMID getVMID(final List<String> macs) { for(String mac:macs) { try { //TODO~ change to findByID if more efficient, and turn mac the id of this class VMID vmid = this.vcDao.findByMac(mac); if (vmid!=null) { return vmid; } } catch (DupMacException e) { log.error(e); } } return null; } /* (non-Javadoc) * @see org.hyperic.hq.vm.VCManager#deleteVCConfig(int) */ @Transactional(readOnly = false) public void deleteVCConfig(int id) { deleteVCMapping(vcConfigDao.get(id)); vcConfigDao.remove(vcConfigDao.get(id)); vcConfigDao.getSession().flush(); vcConfigDao.getSession().clear(); loadVCConfigsFromDB(); } /* (non-Javadoc) * @see org.hyperic.hq.vm.VCManager#deleteVCConfig(java.lang.String) */ @Transactional(readOnly = false) public void deleteVCConfig(String id) { deleteVCConfig(Integer.valueOf(id)); } /* (non-Javadoc) * @see org.hyperic.hq.vm.VCManager#vcConfigExists(int) */ public boolean vcConfigExists(int id) { if (null == vcConfigDao.get(id)) { return false; } return true; } /* (non-Javadoc) * @see org.hyperic.hq.vm.VCManager#vcConfigExists(java.lang.String) */ public boolean vcConfigExists(String id) { try { return vcConfigExists(Integer.valueOf(id)); }catch(Exception e){ return false; } } /* (non-Javadoc) * @see org.hyperic.hq.vm.VCManager#updateVCConfig(java.lang.String, java.lang.String, java.lang.String, java.lang.String) */ @Transactional(readOnly = false) public void updateVCConfig(String id, String url, String user, String password) throws ApplicationException { if (null == id) { throw new ApplicationException("Please provide the ID of the vCenter you like to update"); } VCConfig vcConfig = vcConfigDao.get(Integer.valueOf(id)); if (null == vcConfig) { throw new ApplicationException("There is no VC connection with id '" + id + "'"); } vcConfig.setUrl(url); vcConfig.setUser(user); vcConfig.setPassword(password); updateVCConfig(vcConfig); } /* (non-Javadoc) * @see org.hyperic.hq.vm.VCManager#updateVCConfig(org.hyperic.hq.vm.VCConfig) */ @Transactional(readOnly = false) public void updateVCConfig(VCConfig vc) throws ApplicationException { VCConfig vcConfig = null; for(VCConfig con : vcConfigs) { if(con.equals(vc)) { vcConfig = con; break; } } if (null == vcConfig) { throw new ApplicationException("There is no VC connection with id '" + vc.getId() + "'"); } if (vcConfig.getUrl() != null && !vcConfig.getUrl().equalsIgnoreCase(vc.getUrl())) { deleteVCMapping(vcConfig); } vcConfig.setUrl(vc.getUrl()); vcConfig.setPassword(vc.getPassword()); vcConfig.setUser(vc.getUser()); vcConfig.setLastSyncSucceeded(false); setVcUuid(vcConfig); vcConfigDao.getSession().clear(); vcConfigDao.save(vcConfig); } public VCConfig getVCConfig(int id) { for (VCConfig conf : vcConfigs) { if (conf.getId() == id) { return conf; } } return null; } private void setVcUuid(VCConfig config){ ServiceInstance si = null; String vcUuid = null; try { si = new ServiceInstance(new URL(config.getUrl()), config.getUser(), config.getPassword(), true); vcUuid = si.getServiceContent().getAbout().getInstanceUuid(); } catch (RemoteException e) { log.warn(e,e); } catch (MalformedURLException e) { log.warn(e,e); } finally { if (si != null) { ServerConnection sc = si.getServerConnection(); if (sc != null) { sc.logout(); } } } if (null != vcUuid) { config.setVcUuid(vcUuid); } } /** * @param config - the vCenter config for which we want to * remove all related VM mapping entries in the DB */ private void deleteVCMapping(VCConfig config) { if (null != config.getVcUuid()) { List<VmMapping> deletedVms = new ArrayList<VmMapping>(); for (VmMapping mapping : vcDao.findByVcUUID(config.getVcUuid())) { deletedVms.add(mapping); } vcDao.getSession().clear(); persistMapping(null, deletedVms); } } /* (non-Javadoc) * @see org.hyperic.hq.vm.VCManager#addVCConfig(java.lang.String, java.lang.String, java.lang.String, boolean) */ @Transactional(readOnly = false) public VCConfig addVCConfig(String url, String user, String password, boolean setByUi) throws ApplicationException { if ((StringUtil.isNullOrEmpty(url) || StringUtil.isNullOrEmpty(user) || StringUtil.isNullOrEmpty(password))) { throw new ApplicationException(" missing one or more of these fields - vCenter URL, username and password"); } if (vcConfigExistsByUrl(url)) { throw new ApplicationException("There is already an existing vCenter configuration for '" + url + "'"); } VCConfig vcConfig = new VCConfig(url, user, password); vcConfig.setSetByUI(setByUi); vcConfigs.add(vcConfig); // create a scheduled 'sync task' for the vCenter executor.scheduleWithFixedDelay(new VCSynchronizer(vcConfig), 0, SYNC_INTERVAL_MINUTES, TimeUnit.MINUTES); setVcUuid(vcConfig); vcConfigDao.save(vcConfig); return vcConfig; } /* (non-Javadoc) * @see org.hyperic.hq.vm.VCManager#addVCConfig(java.lang.String, java.lang.String, java.lang.String) */ @Transactional(readOnly = false) public VCConfig addVCConfig(String url, String user, String password) throws ApplicationException { return addVCConfig(url, user, password, false); } /* (non-Javadoc) * @see org.hyperic.hq.vm.VCManager#getVCConfigSetByUI() */ public VCConfig getVCConfigSetByUI() { return vcConfigDao.getVCConnectionSetByUI(); } /** * A worker class responsible for keeping the vCenter mapping up to date, * each worker is responsible for one vCenter. */ private class VCSynchronizer implements Runnable{ private final VCConfig config; private AtomicInteger scanCount = new AtomicInteger(0); public VCSynchronizer(VCConfig credantials) { this.config = credantials; } public void run() { Session session = null; SessionFactory sessionFactory = null; try{ //Binds a Hibernate Session to the thread for the entire //processing of the request. This logic is copied from the OpenSessionInViewFilter filter sessionFactory = appContext.getBean(OpenSessionInViewFilter.DEFAULT_SESSION_FACTORY_BEAN_NAME, SessionFactory.class); session = SessionFactoryUtils.getSession(sessionFactory, true); session.setFlushMode(FlushMode.MANUAL); if (!TransactionSynchronizationManager.hasResource(sessionFactory)) { TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session)); } //Every 10 scans - do a full VC scan if (scanCount.incrementAndGet() >= 9) { scanCount.set(0); config.setLastSyncSucceeded(false); } doVCEventsScan(config); } catch (Error e) { log.fatal(e,e); throw e; } catch (Throwable t) { log.error(t,t); }finally{ TransactionSynchronizationManager.unbindResource(sessionFactory); //Close the given Session SessionFactoryUtils.closeSession(session); } } } }