SMBJ 和 DFS 以及 "Nested Session"

SMBJ and DFS and "Nested Session"

我有一个项目,我在其中获得了一个 ID,然后使用该 ID 查找文件路径并处理它们...这些文件位于各种已安装的驱动器上,所以我使用的是 SMBJ java 图书馆访问它们。

我遇到的问题是一些(大多数)文件正在使用 DFS 挂载点...现在,这本身并不是问题,但显然 SMBJ 库似乎创建嵌套每个不同的 DFS 位置的会话。因此,即使我在完成读取后关闭实际的 FILE,DiskSession 对象仍保留着所有这些嵌套的会话......最终通过 DFS 配置设置,或通过这些库,我遇到了一些它只是吹的点并停止允许创建更多会话。

我正在处理数十万条记录,"crash" 似乎发生在大约 500 条记录(会话)正在处理的地方。在查看明确关闭这些嵌套会话的代码时,我没有看到任何明显的东西。事实上,我从 DiskShare 对象的外部根本看不到对它们的外部访问。

我是否缺少某种设置来最大化它所持有的会话?除了我围绕这个管理某种我自己的柜台,关闭和重新打开 sessions/connections 我不知道如何处理这个。

有人知道我在这里遗漏了什么吗?

代码如下:

public class Smb {

private static SMBClient client;
private static String[] DFSMounts = {"DFS1","dfs1"};
private static final Logger Log = LoggerFactory.getLogger(Smb.class);
private static HashMap<String,DiskShare> shares = new HashMap<>();
private static HashMap<String,Connection> connections = new HashMap<>();
private static HashMap<Connection,Session> sessions = new HashMap<>();

private synchronized static SMBClient getClient(){
    if (client == null){
        SmbConfig cfg = SmbConfig.builder().withDfsEnabled(true).build();
        client = new SMBClient(cfg);
    }
    return client;
}

private synchronized static Connection getConnection(String realDomainName) throws IOException{

    Log.info("DOMAIN NAME "+realDomainName);
    Connection connection = (connections.get(realDomainName) == null) ? client.connect(realDomainName) : connections.get(realDomainName);
    if(!connection.isConnected()) {
        connection.close();
        sessions.remove(connection);
        connection = client.connect(realDomainName);

    }
    // connection = client.connect(realDomainName);
    connections.put(realDomainName,connection);
    return connection;


}

private synchronized static Session getSession(Connection connection,SMBClient client){

    Session session = sessions.get(connection);
    if(session==null) {
        PropertiesCache props = PropertiesCache.getInstance();
        String sambaUsername = props.getProperty("smb.user");
        String sambaPass = props.getProperty("smb.password");
        String sambaDomain = props.getProperty("smb.domain");
        Log.info("CLIENT " + client);

        session = (sessions.get(connection) != null) ? sessions.get(connection) : connection.authenticate(new AuthenticationContext(sambaUsername, sambaPass.toCharArray(), sambaDomain));

        sessions.put(connection, session);
    }
    return session;
}

@SuppressWarnings("UnusedReturnValue")
public synchronized static DiskShare getShare(String domainName, String shareName) throws SmbException
{
    DiskShare share = shares.get(domainName+"/"+shareName);
    if((share!=null)&&(!share.isConnected())) share=null;
    if(share == null){
        try {


            PropertiesCache props = PropertiesCache.getInstance();
            String sambaUsername = props.getProperty("smb.user");
            String sambaPass = props.getProperty("smb.password");
            String sambaDomain = props.getProperty("smb.domain");
            String dfsIP = props.getProperty("smb.sambaIP");

            SMBClient client = getClient();

            String realDomainName = (Arrays.stream(DFSMounts).anyMatch(domainName::equals)) ? dfsIP: domainName;
            Connection connection = getConnection(realDomainName);
            Session session = getSession(connection,client);

            share = (DiskShare) session.connectShare(shareName);
            shares.put(domainName+"/"+shareName,share);
        }
        catch (Exception e){
            Log.info("EXCEPTION E "+e);
            Log.info("EX "+e.getMessage());

            throw new SmbException();
        }

    }
    return(share);

}

public static String fixFilename(String filename){
    String[] parts = filename.split("\\");
    ArrayList<String> partsList = new ArrayList<>(Arrays.asList(parts));
    partsList.remove(0);
    partsList.remove(0);
    partsList.remove(0);
    partsList.remove(0);
    return String.join("/",partsList);

}


public static File open(String filename) throws SmbException {
    String[] parts = filename.split("\\");
    String domainName = parts[2];
    String shareName = parts[3];
    DiskShare share = getShare(domainName,shareName);
    Set<SMB2ShareAccess> s = new HashSet<>();
    s.add(SMB2ShareAccess.ALL.iterator().next());
    filename = fixFilename(filename);
    return(share.openFile(filename, EnumSet.of(AccessMask.GENERIC_READ), null, s,  SMB2CreateDisposition.FILE_OPEN, null));
}

}

下面是 OPEN 的使用方式(显示它在使用后关闭文件):

String filename = documents.get(0).getUNCPath();
            try (File f = Smb.open(filename)){

               Process the file code...

                f.closeSilently();
            }

并且:

    while(i.hasNext()){
        String filename =  (String)i.next();
        Log.info("FILENAME "+filename);
        try(File f = Smb.open(filename)){

           Process the file stuff here


        }
    }

我已经为 SMBJ 创建了一个 PR,它改变了这一点。它将为同一主机重用嵌套会话。我自己已经成功地使用它来避免你遇到的完全相同的问题。 https://github.com/hierynomus/smbj/pull/489