两服务器使用文件夹共享文件的方式交换数据——XML

业务场景:

  1. 两个系统交换数据,因为某些特殊原因,不能使用接口调用的方式
  2. 使用共享文件夹来处理问题,通过读写文件交换数据
  3. A服务器将XML数据文件放在A目录下
  4. B服务器读取A目录下的文件,同时将文件转移到B目录下,然后,将自己传递的XML数据文件放在C目录下。
  5. A服务器,将C目录的文件读取,同时将文件转移到D目录下,将自己的XML数据文件放在A目录下。
  6. 这样一轮完成一次数据交换。

业务需求说明:

  1. 使用XML传递数据
  2. 通过共享目录来交换数据
  3. 通过扫描文件夹来读取数据

技术点:

  1. xml字符串数据与pojo对象的互相转换
  2. 文件的读写操作,包括文件锁的问题
  3. 文件夹的监听操作,当文件创建时操作

下面通过三个技术点来解决问题,这里只模仿一个服务器发操作流程,另一个一样

一、xml字符串数据与pojo对象的互相转换

首先我们需要一个POJO对象

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
// XML文件中的根标识
@XmlRootElement(name = "User")
// 控制JAXB 绑定类中属性和字段的排序
@XmlType(propOrder = {
        "userId",
        "userName",
        "password",
        "birthday",
        "money",
        "userList",
})
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    // 读取的文件目录
    public static final String UrlA = "D:\\AA\\User.xml";
    // 转移的文件目录
    public static final String UrlB = "D:\\BB";

    // 用户Id
    private int userId;
    // 用户名
    private String userName;
    // 用户密码
    private String password;
    // 用户生日
    private Date birthday;
    // 用户钱包
    private double money;

    private List<User> userList;

    public List<User> getUserList() {
        return userList;
    }

    public void setUserList(List<User> userList) {
        this.userList = userList;
    }

    public User() {
        super();
    }

    public User(int userId, String userName, String password, Date birthday,
                double money) {
        super();
        this.userId = userId;
        this.userName = userName;
        this.password = password;
        this.birthday = birthday;
        this.money = money;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                ", birthday=" + birthday +
                ", money=" + money +
                ", userList=" + userList +
                '}';
    }
}

下面是一个转换工具

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import static java.lang.Thread.sleep;

public class XMLUtil {

    //设置锁的休眠时间
    private static final long INTERVAL = TimeUnit.MILLISECONDS.toMillis(500);

    /**
     * 将对象直接转换成String类型的 XML输出
     *
     * @param obj
     * @return
     */
    public static String convertToXml(Object obj) {
        // 创建输出流
        StringWriter sw = new StringWriter();
        try {
            // 利用jdk中自带的转换类实现
            JAXBContext context = JAXBContext.newInstance(obj.getClass());

            Marshaller marshaller = context.createMarshaller();
            // 格式化xml输出的格式
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
                    Boolean.TRUE);
            // 将对象转换成输出流形式的xml
            marshaller.marshal(obj, sw);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return sw.toString();
    }


    @SuppressWarnings("unchecked")
    /**
     * 将String类型的xml转换成对象
     */
    public static Object convertXmlStrToObject(Class clazz, String xmlStr) {
        Object xmlObject = null;
        try {
            JAXBContext context = JAXBContext.newInstance(clazz);
            // 进行将Xml转成对象的核心接口
            Unmarshaller unmarshaller = context.createUnmarshaller();
            StringReader sr = new StringReader(xmlStr);
            xmlObject = unmarshaller.unmarshal(sr);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return xmlObject;
    }
}

我们写个测试类测试一下

@Test
public void test03() {
    // 创建需要转换的对象
    User user = new User(1, "Steven", "@sun123", new Date(), 1000.0);
    User user1 = new User(2, "Steven", "@sun123", new Date(), 2000.0);
    User user2 = new User(3, "Steven", "@sun123", new Date(), 3000.0);
    User user3 = new User(4, "Steven", "@sun123", new Date(), 4000.0);
    List<User> userList = new ArrayList<>();
    userList.add(user1);
    userList.add(user2);
    userList.add(user3);
    user.setUserList(userList);
    System.out.println("---将对象转换成string类型的xml Start---");
    String str = XMLUtil.convertToXml(user);
    System.out.println(str);
    System.out.println("---将对象转换成string类型的xml End---");
    System.out.println("---将string类型的xml转换成对象 Start---");
    User user4 = (User) XMLUtil.convertXmlStrToObject(User.class, str);
    System.out.println(user4.toString());
    System.out.println("---将string类型的xml转换成对象 End---");
}

测试结果

---将对象转换成string类型的xml Start---
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<User>
    <userId>1</userId>
    <userName>Steven</userName>
    <password>@sun123</password>
    <birthday>2020-01-19T20:56:53.046+08:00</birthday>
    <money>1000.0</money>
    <userList>
        <userId>2</userId>
        <userName>Steven</userName>
        <password>@sun123</password>
        <birthday>2020-01-19T20:56:53.046+08:00</birthday>
        <money>2000.0</money>
    </userList>
    <userList>
        <userId>3</userId>
        <userName>Steven</userName>
        <password>@sun123</password>
        <birthday>2020-01-19T20:56:53.046+08:00</birthday>
        <money>3000.0</money>
    </userList>
    <userList>
        <userId>4</userId>
        <userName>Steven</userName>
        <password>@sun123</password>
        <birthday>2020-01-19T20:56:53.046+08:00</birthday>
        <money>4000.0</money>
    </userList>
</User>

---将对象转换成string类型的xml End---
---将string类型的xml转换成对象 Start---
User{userId=1, userName='Steven', password='@sun123', birthday=Sun Jan 19 20:56:53 GMT+08:00 2020, money=1000.0, userList=[User{userId=2, userName='Steven', password='@sun123', birthday=Sun Jan 19 20:56:53 GMT+08:00 2020, money=2000.0, userList=null}, User{userId=3, userName='Steven', password='@sun123', birthday=Sun Jan 19 20:56:53 GMT+08:00 2020, money=3000.0, userList=null}, User{userId=4, userName='Steven', password='@sun123', birthday=Sun Jan 19 20:56:53 GMT+08:00 2020, money=4000.0, userList=null}]}
---将string类型的xml转换成对象 End---

这样就完成了数据的转换工作

二、文件的读写操作,包括文件锁的问题

下面解决文件的读写操作,当我们读取和写文件时先锁文件,在操作文件,如果锁不了,说明有其他对象在操作文件,于是线程进入等待状态,等待时间为500毫秒每轮。读取完数据后将文件转移到B文件夹。

下面在改造我们的xml工具类,添加读写函数

/**
     * 将对象根据路径转换成xml文件
     *
     * @param obj
     * @param path
     * @return
     */
public static void convertToXmlFile(Object obj, String path) {
    Calendar calstart = Calendar.getInstance();
    File file = new File(path);
    try {
        if (!file.exists()) {
            file.createNewFile();
        }

        //对该文件加锁
        RandomAccessFile out = new RandomAccessFile(file, "rw");
        FileChannel fcout = out.getChannel();
        FileLock flout = null;
        while (true) {
            try {
                flout = fcout.tryLock();
                break;
            } catch (Exception e) {
                System.out.println("有其他线程正在操作该文件,当前线程休眠500毫秒");
                sleep(INTERVAL);
            }

        }
        StringBuffer sb = new StringBuffer();
        sb.append(convertToXml(obj));
        out.write(sb.toString().getBytes("utf-8"));


        flout.release();
        fcout.close();
        out.close();
        out = null;
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}


@SuppressWarnings("unchecked")
/**
     * 将file类型的xml转换成对象
     */
public static Object convertXmlFileToObject(Class clazz, String xmlPath, String newPath) {
    StringBuffer sb = new StringBuffer();
    try {
        File file = new File(xmlPath);

        //给该文件加锁
        RandomAccessFile fis = new RandomAccessFile(file, "rw");
        FileChannel fcin = fis.getChannel();
        FileLock flin = null;
        while (true) {
            try {
                flin = fcin.tryLock();
                break;
            } catch (Exception e) {
                System.out.println("有其他线程正在操作该文件,当前线程休眠500毫秒");
                sleep(INTERVAL);
            }
        }
        byte[] buf = new byte[1024];

        while ((fis.read(buf)) != -1) {
            sb.append(new String(buf, "utf-8"));
            buf = new byte[1024];
        }
        flin.release();
        fcin.close();
        fis.close();
        fis = null;

        if (file.getName().indexOf(".") >= 0) {
            String filename = file.getName().substring(0, file.getName().lastIndexOf("."));
            file.renameTo(new File(newPath + "\\" + filename + (new SimpleDateFormat("yyyyMMddHHmmss")).format(new Date()) + ".xml"));
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return convertXmlStrToObject(clazz, sb.toString().trim());
}

下面测试函数

@Test
public void test04() {
    // 创建需要转换的对象
    User user = new User(1, "Steven", "@sun123", new Date(), 1000.0);
    User user1 = new User(2, "Steven", "@sun123", new Date(), 2000.0);
    User user2 = new User(3, "Steven", "@sun123", new Date(), 3000.0);
    User user3 = new User(4, "Steven", "@sun123", new Date(), 4000.0);
    List<User> userList = new ArrayList<>();
    userList.add(user1);
    userList.add(user2);
    userList.add(user3);
    user.setUserList(userList);
    System.out.println("---将对象转换成string类型的xml文件 Start---");
    XMLUtil.convertToXmlFile(user, User.UrlA);
    System.out.println("---将对象转换成string类型的xml文件 End---");
    System.out.println("---将string类型的xml文件转换成对象 Start---");
    System.out.println(XMLUtil.convertXmlFileToObject(User.class, User.UrlA, User.UrlB).toString());
    System.out.println("---将string类型的xml文件转换成对象 End---");
}

测试结果:

---将对象转换成string类型的xml文件 Start---
---将对象转换成string类型的xml文件 End---
---将string类型的xml文件转换成对象 Start---
User{userId=1, userName='Steven', password='@sun123', birthday=Sun Jan 19 21:16:29 GMT+08:00 2020, money=1000.0, userList=[User{userId=2, userName='Steven', password='@sun123', birthday=Sun Jan 19 21:16:29 GMT+08:00 2020, money=2000.0, userList=null}, User{userId=3, userName='Steven', password='@sun123', birthday=Sun Jan 19 21:16:29 GMT+08:00 2020, money=3000.0, userList=null}, User{userId=4, userName='Steven', password='@sun123', birthday=Sun Jan 19 21:16:29 GMT+08:00 2020, money=4000.0, userList=null}]}
---将string类型的xml文件转换成对象 End---

这样我们就完成了锁的逻辑了

三、文件夹的监听操作,当文件创建时操作

我这里使用的是SpringBoot做测试,有commons.io ,如果不是,可以导入这个工具:

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

这里使用观察者模式,需要一个观察者

import com.hello.service.ListenerService;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationObserver;

import java.io.File;

public class FileListener extends FileAlterationListenerAdaptor {

    // 声明业务服务
    private ListenerService listenerService;

    // 采用构造函数注入服务
    public FileListener(ListenerService listenerService) {
        this.listenerService = listenerService;
    }

    // 文件创建执行
    @Override
    public void onFileCreate(File file) {
        listenerService.doSomething();
    }

    // 文件创建修改
    @Override
    public void onFileChange(File file) {
        // 触发业务
        listenerService.doSomething();
    }

    // 文件创建删除
    @Override
    public void onFileDelete(File file) {
        listenerService.doNothing();
    }

    // 目录创建
    @Override
    public void onDirectoryCreate(File directory) {
    }

    // 目录修改
    @Override
    public void onDirectoryChange(File directory) {
    }

    // 目录删除
    @Override
    public void onDirectoryDelete(File directory) {
    }


    // 轮询开始
    @Override
    public void onStart(FileAlterationObserver observer) {
        System.out.println("轮询开始");
    }

    // 轮询结束
    @Override
    public void onStop(FileAlterationObserver observer) {
        System.out.println("轮询结束");
    }
}

再去创建一个工厂,监控对象是A目录下的xml文件

import com.hello.service.ListenerService;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.HiddenFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.concurrent.TimeUnit;

@Component
public class FileListenerFactory {

    // 设置监听路径
    private final String monitorDir = "D:\\AA";

    // 设置轮询间隔
    private final long interval = TimeUnit.SECONDS.toMillis(5);

    // 自动注入业务服务
    @Autowired
    private ListenerService listenerService;

    public FileAlterationMonitor getMonitor() {
        // 创建过滤器
        IOFileFilter directories = FileFilterUtils.and(
                FileFilterUtils.directoryFileFilter(),
                HiddenFileFilter.VISIBLE);
        IOFileFilter files = FileFilterUtils.and(
                FileFilterUtils.fileFileFilter(),
                FileFilterUtils.suffixFileFilter(".xml"));
        IOFileFilter filter = FileFilterUtils.or(directories, files);

        // 装配过滤器
        // FileAlterationObserver observer = new FileAlterationObserver(new File(monitorDir));
        FileAlterationObserver observer = new FileAlterationObserver(new File(monitorDir), filter);

        // 向监听者添加监听器,并注入业务服务
        observer.addListener(new FileListener(listenerService));

        // 返回监听者
        return new FileAlterationMonitor(interval, observer);
    }
}

这里通过CommandLineRunner接口完成当服务启动后开始监听文件夹

import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class FileListenerRunner implements CommandLineRunner {

    @Autowired
    private FileListenerFactory fileListenerFactory;

    @Override
    public void run(String... args) throws Exception {
        // 创建监听者
        FileAlterationMonitor fileAlterationMonitor = fileListenerFactory.getMonitor();
        try {
            // do not stop this thread
            fileAlterationMonitor.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这里我们需要一个业务功能的整合

import com.hello.bean.User;
import com.hello.service.ListenerService;
import com.hello.util.XMLUtil;
import com.hello.util.YamlFromConstant;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
public class ListenerServiceImpl implements ListenerService {
    @Override
    public void doSomething() {
        System.out.println("检测文件夹检测操作数据");
        System.out.println("---将String类型的xml转换成对象 Start---");
        User userTest = (User) XMLUtil.convertXmlFileToObject(User.class, User.UrlA, User.UrlB);
        System.out.println(userTest);
        System.out.println("---将String类型的xml转换成对象 End---");
    }

    @Override
    public void doNothing() {
        System.out.println("检测文件夹什么也不做");
    }
}

我们启动服务器日志如下

轮询开始
轮询结束
轮询开始
轮询结束
轮询开始
轮询结束

我们发现日志一直在刷这个日志

下面我们用测试类向A文夹写文件:

@Test
public void test01() {
    // 创建需要转换的对象
    User user = new User(1, "Steven", "@sun123", new Date(), 1000.0);
    User user1 = new User(2, "Steven", "@sun123", new Date(), 2000.0);
    User user2 = new User(3, "Steven", "@sun123", new Date(), 3000.0);
    User user3 = new User(4, "Steven", "@sun123", new Date(), 4000.0);
    List<User> userList = new ArrayList<>();
    userList.add(user1);
    userList.add(user2);
    userList.add(user3);
    user.setUserList(userList);
    System.out.println("---将对象转换成string类型的xml文件 Start---");
    // 将对象转换成string类型的xml
    XMLUtil.convertToXmlFile(user, User.UrlA);
    System.out.println("---将对象转换成string类型的xml文件 End---");
}

日志如下:

轮询结束
轮询开始
检测文件夹检测操作数据
---将String类型的xml文件转换成对象 Start---
User{userId=1, userName='Steven', password='@sun123', birthday=Sun Jan 19 21:34:27 GMT+08:00 2020, money=1000.0, userList=[User{userId=2, userName='Steven', password='@sun123', birthday=Sun Jan 19 21:34:27 GMT+08:00 2020, money=2000.0, userList=null}, User{userId=3, userName='Steven', password='@sun123', birthday=Sun Jan 19 21:34:27 GMT+08:00 2020, money=3000.0, userList=null}, User{userId=4, userName='Steven', password='@sun123', birthday=Sun Jan 19 21:34:27 GMT+08:00 2020, money=4000.0, userList=null}]}
---将String类型的xml文件转换成对象 End---
轮询结束
轮询开始
检测文件夹什么也不做
轮询结束
轮询开始

我们查看文件夹


 location > D:\AA > dir
 location > D:\AA > cd ../BB
 location > D:\BB > dir

    目录: D:\BB


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        2020/1/19     21:34            900 User20200119213432.xml


 location > D:\BB >

我们发现A目录下没有文件,B目录下存有我们的文件,说明转移成功了,这样就完成了我们上述的需求了。

这里只是提供一个业务问题的解决方案,如果有更好的方案可以在评论区留言,谢谢!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章