简体   繁体   中英

How to Implement Factory Design Pattern for CsvProcessing based on Key

I have written a controller which is a default for MototuploadService(for Motor Upload), but I need to make one Factory Design so that based on parentPkId , need to call HealUploadService, TempUploadService, PersonalUploadService etc which will have separate file processing stages.

controller is below.

@RequestMapping(value = "/csvUpload", method = RequestMethod.POST)
    public List<String> csvUpload(@RequestParam String parentPkId, @RequestParam List<MultipartFile> files)
            throws IOException, InterruptedException, ExecutionException, TimeoutException {
        log.info("Entered method csvUpload() of DaoController.class");
        List<String> response = new ArrayList<String>();
        ExecutorService executor = Executors.newFixedThreadPool(10);
        CompletionService<String> compService = new ExecutorCompletionService<String>(executor);
        List< Future<String> > futureList = new ArrayList<Future<String>>();
        for (MultipartFile f : files) {
            compService.submit(new ProcessMutlipartFile(f ,parentPkId,uploadService));
            futureList.add(compService.take());
        }       
        for (Future<String> f : futureList) {
            long timeout = 0;
            System.out.println(f.get(timeout, TimeUnit.SECONDS));
            response.add(f.get());
        }
        executor.shutdown();
        return response;
    }

Here is ProcessMutlipartFile class which extends the callable interface, with CompletionService's compService.submit() invoke this class, which in turn executes call() method, which will process a file.

public class ProcessMutlipartFile implements Callable<String>
{
   private MultipartFile file; 
   private String temp;
   private MotorUploadService motUploadService;
   public ProcessMutlipartFile(MultipartFile file,String temp, MotorUploadService motUploadService )
   {
       this.file=file;
       this.temp=temp;
       this.motUploadService=motUploadService;
   }

   public String call() throws Exception 
   {

    return   motUploadService.csvUpload(temp, file);
    }

}

Below is MotorUploadService class, where I'm processing uploaded CSV file, line by line and then calling validateCsvData() method to validate Data, which returns ErrorObject having line number and Errors associated with it. if csvErrorRecords is null, then error-free and proceed with saving to Db. else save errorList to Db and return Upload Failure.

@Component
public class MotorUploadService {

@Value("${external.resource.folder}")
     String resourceFolder;

    public String csvUpload(String parentPkId, MultipartFile file) {

    String OUT_PATH = resourceFolder;

    try {
            DateFormat df = new SimpleDateFormat("yyyyMMddhhmmss"); 
            String filename = file.getOriginalFilename().split(".")[0] + df.format(new Date()) + file.getOriginalFilename().split(".")[1];
            Path  path = Paths.get(OUT_PATH,fileName)
            Files.copy(file.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);
        }
        catch(IOException e){
            e.printStackTrace();
            return "Failed to Upload File...try Again";
        }
        List<TxnMpMotSlaveRaw> txnMpMotSlvRawlist = new ArrayList<TxnMpMotSlaveRaw>();

        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(file.getInputStream()));
            String line = "";
            int header = 0;
            int lineNum = 1;
            TxnMpSlaveErrorNew txnMpSlaveErrorNew = new TxnMpSlaveErrorNew();
            List<CSVErrorRecords> errList = new ArrayList<CSVErrorRecords>();
            while ((line = br.readLine()) != null) {
                // TO SKIP HEADER
                if (header == 0) {
                    header++;
                    continue;
                }
                lineNum++;
                header++;
                // Use Comma As Separator
                String[] csvDataSet = line.split(",");

                CSVErrorRecords csvErrorRecords = validateCsvData(lineNum, csvDataSet);
                System.out.println("Errors from csvErrorRecords is " + csvErrorRecords);

                if (csvErrorRecords.equals(null) || csvErrorRecords.getRecordNo() == 0) {
                    //Function to Save to Db

                } else {
                    // add to errList
                    continue;
                }
            }
            if (txnMpSlaveErrorNew.getErrRecord().size() == 0) {
                //save all
                return "Successfully Uploaded " + file.getOriginalFilename();   
            } 
            else {
                // save the error in db;
                return "Failure as it contains Faulty Information" + file.getOriginalFilename();
            }
        } catch (IOException ex) {
            ex.printStackTrace();
            return "Failure Uploaded " + file.getOriginalFilename();
        }

    }

    private TxnMpMotSlaveRaw saveCsvData(String[] csvDataSet, String parentPkId) {
        /*
            Mapping csvDataSet to PoJo
            returning Mapped Pojo;
        */
    }

    private CSVErrorRecords validateCsvData(int lineNum, String[] csvDataSet) {
        /*
        Logic for Validation goes here
        */
    }

}

How to make it as a factory design pattern from controller , so that if

 parentPkId='Motor' call MotorUploadService,
    parentPkId='Heal' call HealUploadService 

I'm not so aware of the Factory Design pattern, please help me out. Thanks in advance.

If I understood the question, in essence you would create an interface, and then return a specific implementation based upon the desired type.

So

public interface UploadService {
  void csvUpload(String temp, MultipartFile file) throws IOException;
}

The particular implementations

public class MotorUploadService implements UploadService
{
  public void csvUpload(String temp, MultipartFile file) {
    ...
  }
}

public class HealUploadService implements UploadService
{
  public void csvUpload(String temp, MultipartFile file) {
    ...
  }
}

Then a factory

public class UploadServiceFactory {
  public UploadService getService(String type) {
    if ("Motor".equals(type)) {
      return new MotorUploadService();
    }
    else if ("Heal".equals(type)) {
      return new HealUploadService();
    }
  }
}

The factory might cache the particular implementations. One can also use an abstract class rather than an interface if appropriate.

I think you currently have a class UploadService but that is really the MotorUploadService if I followed your code, so I would rename it to be specific.

Then in the controller, presumably having used injection for the UploadServiceFactory

...
for (MultipartFile f : files) {
  UploadService uploadSrvc = uploadServiceFactory.getService(parentPkId);
  compService.submit(new ProcessMutlipartFile(f ,parentPkId,uploadService));
  futureList.add(compService.take());
} 

So with some additional reading in your classes:

public class ProcessMutlipartFile implements Callable<String>
{
   private MultipartFile file; 
   private String temp;
   private UploadService uploadService;

   // change to take the interface UploadService
   public ProcessMutlipartFile(MultipartFile file,String temp, UploadService uploadService )
   {
       this.file=file;
       this.temp=temp;
       this.uploadService=uploadService;
   }

   public String call() throws Exception 
   {
     return   uploadService.csvUpload(temp, file);
   }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM