现在的位置: 首页 > 综合 > 正文

利用JNI监控CVS仓库变动,自动更新到相应目录——简化WEB小组开发(一、监控篇)

2018年05月21日 ⁄ 综合 ⁄ 共 11272字 ⁄ 字号 评论关闭
 做这个小项目的目的主要也是为了项目小组成员能更好地一致工作。现在一般做项目都是通过CVS将人家的代码更新下来,在自己的机子上把应用给跑起来,但如果我们做的是web开发,那么可以试着将大家各自的“服务器”统一到一台服务器上,比如,CVS提交上192.168.0.1上后,打开网页192.168.0.1能自动体现出来。现在的问题是,如果简单在服务器上把IIS或者TOMCAT之类的目录指到CVS仓库上是没用的,因为存在CVS仓库上的文件都是经过CVS的方式储存的,拿出来不能直接跑起来的。于是我就有了做这个自动化工作的想法。

CVS提交——》服务器监控到CVS仓库变化——》下载更新到IIS或者TOMCAT指定的目录——》小组成员刷新网页
    |                                                                                                                                                                                   |
    |________________________小组成员所需做的_______________________________________|

这里有2个重点所以,一、监控文件变化      二、CVS操作

对于CVS操作,JAVA用javacvs开源项目可以非常轻松搞定,但是对于文件的变化监控,考虑到效率java非常难做,因为java只能主动去提取文件系统的变化,而不能让文件系统的变化来通知java,所以这个while循环只能依赖线程的sleep。   所幸的是,windows API给我们提供了一套非常好的回调机制用来监控文件系统

在MSDN上有很详细的描述,这里我把MSDN上的例子拿出来

#include <windows.h>
#include 
<stdlib.h>
#include 
<stdio.h>
#include 
<tchar.h>

void RefreshDirectory(LPTSTR);
void RefreshTree(LPTSTR);

void WatchDirectory(LPTSTR lpDir)
{
   DWORD dwWaitStatus; 
   HANDLE dwChangeHandles[
2]; 
   TCHAR lpDrive[
4];
   TCHAR lpFile[_MAX_FNAME];
   TCHAR lpExt[_MAX_EXT];

   _tsplitpath(lpDir, lpDrive, NULL, lpFile, lpExt);

   lpDrive[
2= (TCHAR)'/';
   lpDrive[
3= (TCHAR)'';
 
// Watch the directory for file creation and deletion. 
 
   dwChangeHandles[
0= FindFirstChangeNotification( 
      lpDir,                         
// directory to watch 
      FALSE,                         // do not watch subtree 
      FILE_NOTIFY_CHANGE_FILE_NAME); // watch file name changes 
 
   
if (dwChangeHandles[0== INVALID_HANDLE_VALUE) 
      ExitProcess(GetLastError()); 
 
// Watch the subtree for directory creation and deletion. 
 
   dwChangeHandles[
1= FindFirstChangeNotification( 
      lpDrive,                       
// directory to watch 
      TRUE,                          // watch the subtree 
      FILE_NOTIFY_CHANGE_DIR_NAME);  // watch dir. name changes 
 
   
if (dwChangeHandles[1== INVALID_HANDLE_VALUE) 
      ExitProcess(GetLastError()); 
 
// Change notification is set. Now wait on both notification 
// handles and refresh accordingly. 
 
   
while (TRUE) 
   

   
// Wait for notification.
 
      dwWaitStatus 
= WaitForMultipleObjects(2, dwChangeHandles, 
         FALSE, INFINITE); 
 
      
switch (dwWaitStatus) 
      

         
case WAIT_OBJECT_0: 
 
         
// A file was created or deleted in the directory.
         
// Refresh this directory and restart the notification.
 
            RefreshDirectory(lpDir); 
            
if ( FindNextChangeNotification( 
                    dwChangeHandles[
0]) == FALSE ) 
                ExitProcess(GetLastError()); 
            
break
 
         
case WAIT_OBJECT_0 + 1
 
         
// A directory was created or deleted in the subtree.
         
// Refresh the tree and restart the notification.
 
            RefreshTree(lpDrive); 
            
if (FindNextChangeNotification( 
                    dwChangeHandles[
1]) == FALSE) 
                ExitProcess(GetLastError()); 
            
break
 
        
default
            ExitProcess(GetLastError()); 
      }

   }

}


void RefreshDirectory(LPTSTR lpDir)
{
   _tprintf(TEXT(
"Refresh the directory (%s). "), lpDir);
}


void RefreshTree(LPTSTR lpDrive)
{
   _tprintf(TEXT(
"Refresh the directory tree (%s). "), lpDrive);
}

嗯……看来完全符合我们的要求。好了,现在开始从JAVA端开始写

JNI的文章网上都有,一般都是编写native接口,生产.h头文件,再写.cpp实现,编译成DLL……

let‘s go……

package wxy.tool.filewatcher;

public final class FileWatcherNative {

    
static {
        System.loadLibrary(
"FileWatcher");
    }


    
/**
     * HANDLE FindFirstChangeNotification( LPCTSTR lpPathName, BOOL
     * bWatchSubtree, DWORD dwNotifyFilter );
     * 用来得到句柄
     
*/

    
static native long FindFirstChangeNotification(String lpPathName,
            
boolean bWatchSubtree, int dwNotifyFilter) throws Exception;

    
/**
     * BOOL FindNextChangeNotification( HANDLE hChangeHandle );
     * 开始监控句柄
     
*/

    
static native void FindNextChangeNotification(long hChangeHandle)
            
throws Exception;

    
/**
     * BOOL FindCloseChangeNotification( HANDLE hChangeHandle );
     * 关闭监控
     
*/

    
static native void FindCloseChangeNotification(long hChangeHandle)
            
throws Exception;

    
/**
     * DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
     * 
     * 等待这个句柄的反应
     * 
     * 注意,这里我们要让它无穷等待,所以dwMilliseconds要设置为无穷,但是java里的无穷
     * 与c里的无穷是不一样的,java里,int类型的正无穷代表2的31次方-1,负无穷代表
     * -2的31次方,有正负之分,而dwMilliseconds数据类型为unsigned long仅仅代表了正数
     * 概念,所以我们表示成0xFFFFFFFF就可以了,其实相当于-1,这里我们干脆把这个类型定义死
     
*/

    
static native int WaitForSingleObject(long hHandle,
            
int dwTimeoutMilliseconds);

    
static final int INFINITE = 0xFFFFFFFF;

    
/**
     * 下面三个参数都是代表WaitForSingleObject的返回值,第二个是正常返回的
     
*/

    
static final int WAIT_ABANDONED = 0x00000080;

    
static final int WAIT_OBJECT_0 = 0x00000000;

    
static final int WAIT_TIMEOUT = 0x00000102;
}

接着我们编译好,用javah生成.h文件

把JDK里的inlude 文件内的文件包含到工程里,引入刚才生成的头文件

头文件代码是自动生成的,如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
"jni.h"
/* Header for class wxy_tool_filewatcher_FileWatcherNative */

#ifndef _Included_wxy_tool_filewatcher_FileWatcherNative
#define _Included_wxy_tool_filewatcher_FileWatcherNative
#ifdef __cplusplus
extern "C" {
#endif
#undef wxy_tool_filewatcher_FileWatcherNative_INFINITE
#define wxy_tool_filewatcher_FileWatcherNative_INFINITE -1L
#undef wxy_tool_filewatcher_FileWatcherNative_WAIT_ABANDONED
#define wxy_tool_filewatcher_FileWatcherNative_WAIT_ABANDONED 128L
#undef wxy_tool_filewatcher_FileWatcherNative_WAIT_OBJECT_0
#define wxy_tool_filewatcher_FileWatcherNative_WAIT_OBJECT_0 0L
#undef wxy_tool_filewatcher_FileWatcherNative_WAIT_TIMEOUT
#define wxy_tool_filewatcher_FileWatcherNative_WAIT_TIMEOUT 258L
/*
 * Class:     wxy_tool_filewatcher_FileWatcherNative
 * Method:    FindFirstChangeNotification
 * Signature: (Ljava/lang/String;ZI)J
 
*/

JNIEXPORT jlong JNICALL Java_wxy_tool_filewatcher_FileWatcherNative_FindFirstChangeNotification
  (JNIEnv 
*, jclass, jstring, jboolean, jint);

/*
 * Class:     wxy_tool_filewatcher_FileWatcherNative
 * Method:    FindNextChangeNotification
 * Signature: (J)V
 
*/

JNIEXPORT 
void JNICALL Java_wxy_tool_filewatcher_FileWatcherNative_FindNextChangeNotification
  (JNIEnv 
*, jclass, jlong);

/*
 * Class:     wxy_tool_filewatcher_FileWatcherNative
 * Method:    FindCloseChangeNotification
 * Signature: (J)V
 
*/

JNIEXPORT 
void JNICALL Java_wxy_tool_filewatcher_FileWatcherNative_FindCloseChangeNotification
  (JNIEnv 
*, jclass, jlong);

/*
 * Class:     wxy_tool_filewatcher_FileWatcherNative
 * Method:    WaitForSingleObject
 * Signature: (JI)I
 
*/

JNIEXPORT jint JNICALL Java_wxy_tool_filewatcher_FileWatcherNative_WaitForSingleObject
  (JNIEnv 
*, jclass, jlong, jint);

#ifdef __cplusplus
}

#endif
#endif

下面是自己编写的 filewatcher.cpp

// filewatcher.cpp : 定义 DLL 应用程序的入口点。
//

#include 
"stdafx.h"
#include 
"wxy_tool_filewatcher_FileWatcherNative.h"

#ifdef _MANAGED
#pragma managed(push, off)
#endif

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    
return TRUE;
}


JNIEXPORT jlong JNICALL Java_wxy_tool_filewatcher_FileWatcherNative_FindFirstChangeNotification
(JNIEnv
* javaEnv, jclass, jstring javaPathName, jboolean javaWatchSubtree, jint javaNotifyFilter)
{
    
/*
    *    将java的数据类型转换过来,    GetStringChars ReleaseStringChars要注意配对出现
    
*/

  
const jchar* lpJavaPathName = javaEnv->GetStringChars(javaPathName, NULL);
  LPCTSTR lpPathName 
= (LPCTSTR) lpJavaPathName;
  BOOL bWatchSubtree 
= (BOOL)javaWatchSubtree;
  DWORD dwNotifyFilter 
= (DWORD)javaNotifyFilter;
  
  HANDLE handle 
= FindFirstChangeNotification(lpPathName, bWatchSubtree, dwNotifyFilter);
  
if (handle == INVALID_HANDLE_VALUE || handle == (HANDLE)ERROR_INVALID_FUNCTION)
  
{
    DWORD errorCode 
= GetLastError();
    jclass exceptionClass 
= javaEnv->FindClass("java/lang/Exception");
    javaEnv
->ThrowNew(exceptionClass, "DLL Throws a Exception in FindFirstChangeNotification");
  }


  javaEnv
->ReleaseStringChars(javaPathName, lpJavaPathName);
  
return (jlong)handle;
}


JNIEXPORT 
void JNICALL Java_wxy_tool_filewatcher_FileWatcherNative_FindNextChangeNotification
(JNIEnv 
*javaEnv, jclass, jlong handle)
{
  
if (! FindNextChangeNotification((HANDLE) handle))
  
{
    DWORD errorCode 
= GetLastError();
    jclass exceptionClass 
= javaEnv->FindClass("java/lang/Exception");
    javaEnv
->ThrowNew(exceptionClass, "DLL Throws a Exception in FindNextChangeNotification");
  }

}


JNIEXPORT 
void JNICALL Java_wxy_tool_filewatcher_FileWatcherNative_FindCloseChangeNotification
(JNIEnv 
*javaEnv, jclass, jlong handle)
{
  
if (! FindCloseChangeNotification((HANDLE) handle)) 
  
{
    DWORD errorCode 
= GetLastError();
    jclass exceptionClass 
= javaEnv->FindClass("java/lang/Exception");
    javaEnv
->ThrowNew(exceptionClass, "DLL Throws a Exception in FindCloseChangeNotification");
  }

}


JNIEXPORT jint JNICALL Java_wxy_tool_filewatcher_FileWatcherNative_WaitForSingleObject
(JNIEnv 
*javaEnv , jclass, jlong hWaitHandle, jint waitTimeoutMillis)
{
  
return WaitForSingleObject((HANDLE) hWaitHandle, (DWORD) waitTimeoutMillis);
}


#ifdef _MANAGED
#pragma managed(pop)
#endif

编译生成 filewatcher.dll

可以了,我们开始做单元测试吧:

package wxy.tool.filewatcher;

import static org.junit.Assert.*;

import org.junit.Test;

public class FileWatcherNativeTest {
    
    
private long handle;

    @Test
    
public void testFindFirstChangeNotification() throws Exception {
        handle 
= FileWatcherNative.FindFirstChangeNotification(
                
"d:/"true0x00000010);
    }

    
    @Test
    
public void testFindNextChangeNotification()throws Exception{
        handle 
= FileWatcherNative.FindFirstChangeNotification(
                
"d:/"true0x00000010);
        FileWatcherNative.FindNextChangeNotification(handle);
    }

    
    @Test
    
public void testFindCloseChangeNotification() throws Exception{
        handle 
= FileWatcherNative.FindFirstChangeNotification(
                
"d:/"true0x00000010);
        FileWatcherNative.FindCloseChangeNotification(handle);
    }

    
    @Test
    
public void testWaitForSingleObject() throws Exception{
        handle 
= FileWatcherNative.FindFirstChangeNotification(
                
"d:/"true0x00000010);
        FileWatcherNative.FindNextChangeNotification(handle);
        
int returnState = FileWatcherNative.WaitForSingleObject(
                handle, FileWatcherNative.INFINITE);
        assertEquals(FileWatcherNative.WAIT_OBJECT_0, returnState);
    }


}

可以发现,在第一个方法那里停住了,也就是说进入了监控状态,我们在D盘下写入一个文件后,可以发现测试自动全部完成。

监控的状态如下

  /** 当文件名变更 */
  
public static final int FILE_NOTIFY_CHANGE_FILE_NAME   = 0x00000001;
  
  
/** 当目录名字变更 */
  
public static final int FILE_NOTIFY_CHANGE_DIR_NAME    = 0x00000002;
  
  
/** 当文件属性变更 */
  
public static final int FILE_NOTIFY_CHANGE_ATTRIBUTES  = 0x00000004;
  
  
/** 当文件大小变更 */
  
public static final int FILE_NOTIFY_CHANGE_SIZE        = 0x00000008;
  
  
/** 当发现有写入数据行为 */
  
public static final int FILE_NOTIFY_CHANGE_LAST_WRITE  = 0x00000010;
  
  
/** 当有访问发生 */
  
public static final int FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x00000020;
  
  
/** 当有文件创建 */
  
public static final int FILE_NOTIFY_CHANGE_CREATION    = 0x00000040;
  
  
/** 当安全性发生变更 */
  
public static final int FILE_NOTIFY_CHANGE_SECURITY    = 0x00000100;

  
/** 全部监控!! */
  
public static final int FILE_NOTIFY_CHANGE_ALL    = 0x000000FF;

抱歉!评论已关闭.