nvazquez commented on a change in pull request #3553: [WIP] [DO NOT MERGE] 
CloudStack Backup & Recovery Framework
URL: https://github.com/apache/cloudstack/pull/3553#discussion_r357928238
 
 

 ##########
 File path: 
server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
 ##########
 @@ -0,0 +1,1051 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 org.apache.cloudstack.backup;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.inject.Inject;
+import javax.naming.ConfigurationException;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.command.admin.backup.DeleteBackupOfferingCmd;
+import org.apache.cloudstack.api.command.admin.backup.ImportBackupOfferingCmd;
+import 
org.apache.cloudstack.api.command.admin.backup.ListBackupProviderOfferingsCmd;
+import org.apache.cloudstack.api.command.admin.backup.ListBackupProvidersCmd;
+import 
org.apache.cloudstack.api.command.user.backup.AssignVirtualMachineToBackupOfferingCmd;
+import org.apache.cloudstack.api.command.user.backup.CreateBackupCmd;
+import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd;
+import org.apache.cloudstack.api.command.user.backup.DeleteBackupCmd;
+import org.apache.cloudstack.api.command.user.backup.DeleteBackupScheduleCmd;
+import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd;
+import org.apache.cloudstack.api.command.user.backup.ListBackupScheduleCmd;
+import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd;
+import 
org.apache.cloudstack.api.command.user.backup.RemoveVirtualMachineFromBackupOfferingCmd;
+import org.apache.cloudstack.api.command.user.backup.RestoreBackupCmd;
+import 
org.apache.cloudstack.api.command.user.backup.RestoreVolumeFromBackupAndAttachToVMCmd;
+import org.apache.cloudstack.api.command.user.backup.UpdateBackupScheduleCmd;
+import org.apache.cloudstack.backup.dao.BackupDao;
+import org.apache.cloudstack.backup.dao.BackupOfferingDao;
+import org.apache.cloudstack.backup.dao.BackupScheduleDao;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher;
+import org.apache.cloudstack.framework.jobs.AsyncJobManager;
+import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
+import org.apache.cloudstack.managed.context.ManagedContextRunnable;
+import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
+import org.apache.cloudstack.poll.BackgroundPollManager;
+import org.apache.cloudstack.poll.BackgroundPollTask;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+import org.apache.log4j.Logger;
+
+import com.cloud.api.ApiDispatcher;
+import com.cloud.api.ApiGsonHelper;
+import com.cloud.dc.DataCenter;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.event.ActionEvent;
+import com.cloud.event.ActionEventUtils;
+import com.cloud.event.EventTypes;
+import com.cloud.event.UsageEventUtils;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.PermissionDeniedException;
+import com.cloud.host.HostVO;
+import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.Hypervisor;
+import com.cloud.hypervisor.HypervisorGuru;
+import com.cloud.hypervisor.HypervisorGuruManager;
+import com.cloud.projects.Project;
+import com.cloud.storage.ScopeType;
+import com.cloud.storage.Volume;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.DiskOfferingDao;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.usage.dao.UsageBackupDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.AccountService;
+import com.cloud.user.User;
+import com.cloud.utils.DateUtil;
+import com.cloud.utils.Pair;
+import com.cloud.utils.Ternary;
+import com.cloud.utils.component.ComponentContext;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.db.DB;
+import com.cloud.utils.db.Filter;
+import com.cloud.utils.db.GlobalLock;
+import com.cloud.utils.db.JoinBuilder;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.db.TransactionStatus;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.dao.VMInstanceDao;
+import com.google.common.base.Strings;
+import com.google.gson.Gson;
+
+public class BackupManagerImpl extends ManagerBase implements BackupManager {
+    private static final Logger LOG = 
Logger.getLogger(BackupManagerImpl.class);
+
+    @Inject
+    private BackupDao backupDao;
+    @Inject
+    private BackupScheduleDao backupScheduleDao;
+    @Inject
+    private BackupOfferingDao backupOfferingDao;
+    @Inject
+    private VMInstanceDao vmInstanceDao;
+    @Inject
+    private AccountService accountService;
+    @Inject
+    private AccountManager accountManager;
+    @Inject
+    private UsageBackupDao usageBackupDao;
+    @Inject
+    private VolumeDao volumeDao;
+    @Inject
+    private DataCenterDao dataCenterDao;
+    @Inject
+    private BackgroundPollManager backgroundPollManager;
+    @Inject
+    private HostDao hostDao;
+    @Inject
+    private HypervisorGuruManager hypervisorGuruManager;
+    @Inject
+    private PrimaryDataStoreDao primaryDataStoreDao;
+    @Inject
+    private DiskOfferingDao diskOfferingDao;
+    @Inject
+    private ApiDispatcher apiDispatcher;
+    @Inject
+    private AsyncJobManager asyncJobManager;
+
+    private AsyncJobDispatcher asyncJobDispatcher;
+    private Timer backupTimer;
+    private Date currentTimestamp;
+
+    private static Map<String, BackupProvider> backupProvidersMap = new 
HashMap<>();
+    private List<BackupProvider> backupProviders;
+
+    public AsyncJobDispatcher getAsyncJobDispatcher() {
+        return asyncJobDispatcher;
+    }
+
+    public void setAsyncJobDispatcher(final AsyncJobDispatcher dispatcher) {
+        asyncJobDispatcher = dispatcher;
+    }
+
+    @Override
+    public List<BackupOffering> listBackupProviderOfferings(final Long zoneId) 
{
+        if (zoneId == null || zoneId < 1) {
+            throw new CloudRuntimeException("Invalid zone ID passed");
+        }
+        validateForZone(zoneId);
+        final Account account = CallContext.current().getCallingAccount();
+        if (!accountService.isRootAdmin(account.getId())) {
+            throw new PermissionDeniedException("Parameter external can only 
be specified by a Root Admin, permission denied");
+        }
+        final BackupProvider backupProvider = getBackupProvider(zoneId);
+        LOG.debug("Listing external backup offerings for the backup provider 
configured for zone ID " + zoneId);
+        return backupProvider.listBackupOfferings(zoneId);
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_BACKUP_IMPORT_OFFERING, 
eventDescription = "importing backup offering", async = true)
+    public BackupOffering importBackupOffering(final ImportBackupOfferingCmd 
cmd) {
+        validateForZone(cmd.getZoneId());
+        final BackupOffering existingOffering = 
backupOfferingDao.findByExternalId(cmd.getExternalId(), cmd.getZoneId());
+        if (existingOffering != null) {
+            throw new CloudRuntimeException("A backup offering with external 
ID " + cmd.getExternalId() + " already exists");
+        }
+        if (backupOfferingDao.findByName(cmd.getName(), cmd.getZoneId()) != 
null) {
+            throw new CloudRuntimeException("A backup offering with the same 
name already exists in this zone");
+        }
+
+        final BackupProvider provider = getBackupProvider(cmd.getZoneId());
+        if (!provider.isBackupOffering(cmd.getZoneId(), cmd.getExternalId())) {
+            throw new CloudRuntimeException("Backup offering '" + 
cmd.getExternalId() + "' does not exist on provider " + provider.getName() + " 
on zone " + cmd.getZoneId());
+        }
+
+        final BackupOfferingVO offering = new 
BackupOfferingVO(cmd.getZoneId(), cmd.getExternalId(), provider.getName(),
+                cmd.getName(), cmd.getDescription(), 
cmd.getUserDrivenBackups());
+
+        final BackupOfferingVO savedOffering = 
backupOfferingDao.persist(offering);
+        if (savedOffering == null) {
+            throw new CloudRuntimeException("Unable to create backup offering: 
" + cmd.getExternalId() + ", name: " + cmd.getName());
+        }
+        LOG.debug("Successfully created backup offering " + cmd.getName() + " 
mapped to backup provider offering " + cmd.getExternalId());
+        return savedOffering;
+    }
+
+    @Override
+    public Pair<List<BackupOffering>, Integer> listBackupOfferings(final 
ListBackupOfferingsCmd cmd) {
+        final Long offeringId = cmd.getOfferingId();
+        final Long zoneId = cmd.getZoneId();
+        final String keyword = cmd.getKeyword();
+
+        if (offeringId != null) {
+            BackupOfferingVO offering = backupOfferingDao.findById(offeringId);
+            if (offering == null) {
+                throw new CloudRuntimeException("Offering ID " + offeringId + 
" does not exist");
+            }
+            return new Pair<>(Collections.singletonList(offering), 1);
+        }
+
+        final Filter searchFilter = new Filter(BackupOfferingVO.class, "id", 
true, cmd.getStartIndex(), cmd.getPageSizeVal());
+        SearchBuilder<BackupOfferingVO> sb = 
backupOfferingDao.createSearchBuilder();
+        sb.and("zone_id", sb.entity().getZoneId(), SearchCriteria.Op.EQ);
+        sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE);
+
+        final SearchCriteria<BackupOfferingVO> sc = sb.create();
+
+        if (zoneId != null) {
+            sc.setParameters("zone_id", zoneId);
+        }
+
+        if (keyword != null) {
+            sc.setParameters("name", "%" + keyword + "%");
+        }
+        Pair<List<BackupOfferingVO>, Integer> result = 
backupOfferingDao.searchAndCount(sc, searchFilter);
+        return new Pair<>(new ArrayList<>(result.first()), result.second());
+    }
+
+    @Override
+    public boolean deleteBackupOffering(final Long offeringId) {
+        final BackupOfferingVO offering = 
backupOfferingDao.findById(offeringId);
+        if (offering == null) {
+            throw new CloudRuntimeException("Could not find a backup offering 
with id: " + offeringId);
+        }
+
+        if (vmInstanceDao.listByZoneWithBackups(offering.getZoneId(), 
offering.getId()).size() > 0) {
+            throw new CloudRuntimeException("Backup offering is assigned to 
VMs, remove the assignment(s) in order to remove the offering.");
+        }
+
+        validateForZone(offering.getZoneId());
+        return backupOfferingDao.remove(offering.getId());
+    }
+
+    private String createVolumeInfoFromVolumes(List<VolumeVO> vmVolumes) {
+        List<Backup.VolumeInfo> list = new ArrayList<>();
+        for (VolumeVO vol : vmVolumes) {
+            list.add(new Backup.VolumeInfo(vol.getUuid(), vol.getPath(), 
vol.getVolumeType(), vol.getSize()));
+        }
+        return new Gson().toJson(list.toArray(), Backup.VolumeInfo[].class);
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_BACKUP_OFFERING_ASSIGN, 
eventDescription = "assign VM to backup offering", async = true)
+    public boolean assignVMToBackupOffering(Long vmId, Long offeringId) {
+        final VMInstanceVO vm = vmInstanceDao.findById(vmId);
+        if (vm == null) {
+            throw new CloudRuntimeException("Did not find VM by provided ID");
+        }
+
+        validateForZone(vm.getDataCenterId());
+
+        accountManager.checkAccess(CallContext.current().getCallingAccount(), 
null, true, vm);
+
+        if (vm.getBackupOfferingId() != null) {
+            throw new CloudRuntimeException("VM already is assigned to a 
backup offering, please remove the VM from its previous offering");
+        }
+
+        final BackupOfferingVO offering = 
backupOfferingDao.findById(offeringId);
+        if (offering == null) {
+            throw new CloudRuntimeException("Provided backup offering does not 
exist");
+        }
+
+        final BackupProvider backupProvider = 
getBackupProvider(offering.getProvider());
+        if (backupProvider == null) {
+            throw new CloudRuntimeException("Failed to get the backup provider 
for the zone, please contact the administrator");
+        }
+
+        boolean result = false;
+        try {
+            vm.setBackupOfferingId(offering.getId());
+            
vm.setBackupVolumes(createVolumeInfoFromVolumes(volumeDao.findByInstance(vm.getId())));
+            result = backupProvider.assignVMToBackupOffering(vm, offering);
+        } catch (Exception e) {
+            LOG.error("Exception caught while assigning VM to backup offering 
by the backup provider", e);
+            throw e;
+        }
+
+        if (result && vmInstanceDao.update(vm.getId(), vm)) {
+            
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_BACKUP_OFFERING_ASSIGN, 
vm.getAccountId(), vm.getDataCenterId(), vm.getId(),
+                    "Backup-" + vm.getHostName() + "-" + vm.getUuid(), 
vm.getBackupOfferingId(), null, null,
+                    Backup.class.getSimpleName(), vm.getUuid());
+            return true;
+        }
+        throw new CloudRuntimeException("Failed to update VM backup in the 
database, please try again");
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_BACKUP_OFFERING_REMOVE, 
eventDescription = "remove VM from backup offering", async = true)
+    public boolean removeVMFromBackupOffering(final Long vmId, final boolean 
forced) {
+        final VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(vmId);
+        if (vm == null) {
+            throw new CloudRuntimeException("Did not find VM by provided ID");
+        }
+
+        validateForZone(vm.getDataCenterId());
+        accountManager.checkAccess(CallContext.current().getCallingAccount(), 
null, true, vm);
+
+        final BackupOfferingVO offering = 
backupOfferingDao.findById(vm.getBackupOfferingId());
+        if (offering == null) {
+            throw new CloudRuntimeException("No previously configured backup 
offering found for the VM");
+        }
+
+        final BackupProvider backupProvider = 
getBackupProvider(offering.getProvider());
+        if (backupProvider == null) {
+            throw new CloudRuntimeException("Failed to get the backup provider 
for the zone, please contact the administrator");
+        }
+
+        if (!forced && backupProvider.willDeleteBackupsOnOfferingRemoval()) {
+            throw new CloudRuntimeException("The backend provider will only 
allow removal of VM from the offering if forced:true is provided " +
+                    "that will also delete the backups.");
+        }
+
+        boolean result = false;
+        VMInstanceVO vmInstance = null;
+        try {
+            vmInstance = vmInstanceDao.acquireInLockTable(vm.getId());
+            vmInstance.setBackupOfferingId(null);
+            vmInstance.setBackupExternalId(null);
+            vmInstance.setBackupVolumes(null);
+            result = backupProvider.removeVMFromBackupOffering(vmInstance);
+            if ((result || forced) && vmInstanceDao.update(vmInstance.getId(), 
vmInstance)) {
+                
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_BACKUP_OFFERING_REMOVE, 
vm.getAccountId(), vm.getDataCenterId(), vm.getId(),
+                        "Backup-" + vm.getHostName() + "-" + vm.getUuid(), 
vm.getBackupOfferingId(), null, null,
+                        Backup.class.getSimpleName(), vm.getUuid());
+                final BackupSchedule backupSchedule = 
backupScheduleDao.findByVM(vmInstance.getId());
+                if (backupSchedule != null) {
+                    backupScheduleDao.remove(backupSchedule.getId());
+                }
+                result = true;
+            }
+        } catch (final Exception e) {
+            LOG.warn("Exception caught when trying to remove VM from the 
backup offering: ", e);
+        } finally {
+            if (vmInstance != null) {
+                vmInstanceDao.releaseFromLockTable(vmInstance.getId());
+            }
+        }
+        return result;
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_BACKUP_SCHEDULE_CONFIGURE, 
eventDescription = "configuring VM backup schedule")
+    public BackupSchedule configureBackupSchedule(CreateBackupScheduleCmd cmd) 
{
+        final Long vmId = cmd.getVmId();
+        final DateUtil.IntervalType intervalType = cmd.getIntervalType();
+        final String scheduleString = cmd.getSchedule();
+        final TimeZone timeZone = TimeZone.getTimeZone(cmd.getTimezone());
+
+        if (intervalType == null) {
+            throw new CloudRuntimeException("Invalid interval type provided");
+        }
+
+        final VMInstanceVO vm = vmInstanceDao.findById(vmId);
+        if (vm == null) {
+            throw new CloudRuntimeException("Did not find VM by provided ID");
+        }
+        validateForZone(vm.getDataCenterId());
+        accountManager.checkAccess(CallContext.current().getCallingAccount(), 
null, true, vm);
+
+        if (vm.getBackupOfferingId() == null) {
+            throw new CloudRuntimeException("Cannot configure backup schedule 
for the VM without having any backup offering");
+        }
+
+        final BackupOffering offering = 
backupOfferingDao.findById(vm.getBackupOfferingId());
+        if (offering == null || !offering.isUserDrivenBackupAllowed()) {
+            throw new CloudRuntimeException("The selected backup offering does 
not allow user-defined backup schedule");
+        }
+
+        final String timezoneId = timeZone.getID();
+        if (!timezoneId.equals(cmd.getTimezone())) {
+            LOG.warn("Using timezone: " + timezoneId + " for running this 
snapshot policy as an equivalent of " + cmd.getTimezone());
+        }
+
+        Date nextDateTime = null;
+        try {
+            nextDateTime = DateUtil.getNextRunTime(intervalType, 
cmd.getSchedule(), timezoneId, null);
+        } catch (Exception e) {
+            throw new InvalidParameterValueException("Invalid schedule: " + 
cmd.getSchedule() + " for interval type: " + cmd.getIntervalType());
+        }
+
+        final BackupScheduleVO schedule = backupScheduleDao.findByVM(vmId);
+        if (schedule == null) {
+            return backupScheduleDao.persist(new BackupScheduleVO(vmId, 
intervalType, scheduleString, timezoneId, nextDateTime));
+        }
+
+        schedule.setScheduleType((short) intervalType.ordinal());
+        schedule.setSchedule(scheduleString);
+        schedule.setTimezone(timezoneId);
+        schedule.setScheduledTimestamp(nextDateTime);
+        backupScheduleDao.update(schedule.getId(), schedule);
+        return backupScheduleDao.findByVM(vmId);
+    }
+
+    @Override
+    public BackupSchedule listBackupSchedule(final Long vmId) {
+        final VMInstanceVO vm = vmInstanceDao.findById(vmId);
+        if (vm == null) {
+            throw new CloudRuntimeException("Did not find VM by provided ID");
+        }
+        validateForZone(vm.getDataCenterId());
+        accountManager.checkAccess(CallContext.current().getCallingAccount(), 
null, true, vm);
+
+        return backupScheduleDao.findByVM(vmId);
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_BACKUP_SCHEDULE_DELETE, 
eventDescription = "deleting VM backup schedule")
+    public boolean deleteBackupSchedule(final Long vmId) {
+        final VMInstanceVO vm = vmInstanceDao.findById(vmId);
+        if (vm == null) {
+            throw new CloudRuntimeException("Did not find VM by provided ID");
+        }
+        validateForZone(vm.getDataCenterId());
+        accountManager.checkAccess(CallContext.current().getCallingAccount(), 
null, true, vm);
+
+        final BackupSchedule schedule = backupScheduleDao.findByVM(vmId);
+        if (schedule == null) {
+            throw new CloudRuntimeException("VM has no backup schedule 
defined, no need to delete anything.");
+        }
+        return backupScheduleDao.remove(schedule.getId());
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_BACKUP_CREATE, 
eventDescription = "creating VM backup", async = true)
+    public boolean createBackup(final Long vmId) {
+        final VMInstanceVO vm = vmInstanceDao.findById(vmId);
+        if (vm == null) {
+            throw new CloudRuntimeException("Did not find VM by provided ID");
+        }
+        validateForZone(vm.getDataCenterId());
+        accountManager.checkAccess(CallContext.current().getCallingAccount(), 
null, true, vm);
+
+        if (vm.getBackupOfferingId() == null) {
+            throw new CloudRuntimeException("VM has not backup offering 
configured, cannot create backup before assigning it to a backup offering");
+        }
+
+        final BackupOffering offering = 
backupOfferingDao.findById(vm.getBackupOfferingId());
+        if (offering == null) {
+            throw new CloudRuntimeException("VM backup offering not found");
+        }
+
+        if (!offering.isUserDrivenBackupAllowed()) {
+            throw new CloudRuntimeException("The assigned backup offering does 
not allow ad-hoc user backup");
+        }
+
+        final BackupProvider backupProvider = 
getBackupProvider(offering.getProvider());
+        if (backupProvider != null && backupProvider.takeBackup(vm)) {
+            return true;
+        }
+        throw new CloudRuntimeException("Failed to create VM backup");
+    }
+
+    @Override
+    public Pair<List<Backup>, Integer> listBackups(final ListBackupsCmd cmd) {
+        final Long id = cmd.getId();
+        final Long vmId = cmd.getVmId();
+        final Long zoneId = cmd.getZoneId();
+        final Account caller = CallContext.current().getCallingAccount();
+        final String keyword = cmd.getKeyword();
+        List<Long> permittedAccounts = new ArrayList<Long>();
+
+        if (vmId != null) {
+            VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(vmId);
+            if (vm != null) {
+                accountManager.checkAccess(caller, null, true, vm);
+            }
+        }
+
+        final Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> 
domainIdRecursiveListProject = new Ternary<Long, Boolean, 
Project.ListProjectResourcesCriteria>(cmd.getDomainId(),
+                cmd.isRecursive(), null);
+        accountManager.buildACLSearchParameters(caller, id, 
cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, 
domainIdRecursiveListProject, cmd.listAll(), false);
+        final Long domainId = domainIdRecursiveListProject.first();
+        final Boolean isRecursive = domainIdRecursiveListProject.second();
+        final Project.ListProjectResourcesCriteria 
listProjectResourcesCriteria = domainIdRecursiveListProject.third();
+
+        final Filter searchFilter = new Filter(BackupVO.class, "id", true, 
cmd.getStartIndex(), cmd.getPageSizeVal());
+        SearchBuilder<BackupVO> sb = backupDao.createSearchBuilder();
 
 Review comment:
   Same here below

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to