您的位置:首页 > 编程语言 > Java开发

非阻塞的Remote Debug(二)

2015-07-25 17:07 543 查看
摘要: 在(一)中介绍了实现的原理及代码,作者也指出了在实际调试过程中能够和Eclipse关联起来会更方便,不过作者貌似没有给出Eclipse插件的源代码(可能是我没找到~),最近抽空自己写了个插件,方便自己用的,只在本地的Windows版的eclipse 4.3.2上测试过时工作的,还是这么懒啊~

生产服务器上一般不会有开发环境,按照(一)的方式来远程调试可以有几种方式:

每次手动复制一个编译好的Class文件到服务器上制定的目录

把你的开发机器上某一个目录共享出来,保证生产服务器能访问到,把编译好的Class文件放到该目录下,修改debug.jsp里读取Class文件的默认位置

使用http请求把开发环境下的Class文件读取为Byte[]再转换成String,然后post到服务器,服务器再把String解析回Byte[],再加载为Class对象

第一种复制几次之后就会烦了,第二种会好点,第三种是最好的。个人习惯用Eclipse做开发,为了更方便就写了个eclipse的插件,把执行的结果直接返回到Eclipse的console输出,第一次做Eclipse的插件开发,难免坑多,有错漏的地方请指正~

使用Eclipse Plugin的开发向导选择Hellow, World Command,生成一个最基本的plugin project,在此基础上一步步的修改,最终达到自己需要的效果。

第一步,找到要Post到服务器的Class文件的路径,这个路径是当前的Java文件编译后的路径,我没有找到能直接查到这个路径的API,用的是一个比较复杂的方式解析出来的,是用三个路径拼接起来的,

通过JDT的JavaUI获取当前的Java Project对象,得到Project的绝对路径

通过获取当前的Java源文件在工程里的相对路径

获取ClassPathEntry[ ],找到该Java文件所在的ClassPathEntry,得到其对应的sourcePath和outputLocation

至此可以得到当前的Java文件的完整路径,然后在该路径里用3中获取的outputLocation替换sourcePath,再把文件后缀从java换成class即得到了对应的Class文件的绝对路径,核心代码如下:

IEditorInput currentEditor = HandlerUtil.getActiveEditorInput(event);
// get current JavaElement
IJavaElement javaElement = JavaUI.getEditorInputJavaElement(currentEditor);
// get relative java src path
String outputPath = javaElement.getPath().makeRelative().toFile().getPath();
// get the project
IJavaProject javaProject = javaElement.getJavaProject();
String projectPath = javaProject.getProject().getLocation().makeAbsolute().toFile().getParent();
String javaFilePath = projectPath + File.separator + outputPath;
Activator.getStream().println("project relative src path is: "+javaFilePath);
try {
IPath projectOutput = javaProject.getOutputLocation();
IClasspathEntry[] classpathEntries = javaProject.getRawClasspath();
for (IClasspathEntry iClasspathEntry : classpathEntries) {
if (IClasspathEntry.CPE_SOURCE == iClasspathEntry.getEntryKind()) {
String source = iClasspathEntry.getPath().makeRelative().toFile().getPath();
IPath iPath = iClasspathEntry.getOutputLocation();
if (iPath == null) {
iPath = projectOutput;
}
String output = iPath.makeRelative().toFile().getPath();
Activator.getStream().println("source file is: " + source);
if (javaFilePath.contains(source)) {
classPath = javaFilePath.replace(source, output).replace(".java", ".class");
break;
}
}
}
}
catch (JavaModelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


第二步,把该文件转换为String类型,Post到服务器

try {
Activator.getStream().println("debug class path is:"+classPath);
InputStream is = new FileInputStream(classPath);
byte[] b = new byte[is.available()];
is.read(b);
is.close();
String classValue = new String(b, "iso-8859-1");
String result = HttpUtils.postRequest(debugURL, classValue);
Activator.getStream().println(result);
}
catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

String classValue = new String(b, "iso-8859-1");

这里指定编码非常重要,因为如果不指定编码而默认编码不是iso-8859-1的时候,远程服务器就不能把它再解析回去,字节数组里的魔数CAFEBABE变成了3F3F3F3F,这里我是用的httpclient,有点大材小用,而且还会对Eclipse的Http插件有依赖。

package com.gideon.remotedebug.handlers;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
public class HttpUtils {
private static DefaultHttpClient httpclient = new DefaultHttpClient();
public static String postRequest(String url, String classValue) throws UnsupportedEncodingException, IllegalStateException, IOException {
HttpPost httppost = new HttpPost(url);
// post 参数 传递
List<BasicNameValuePair> nvps = new ArrayList<BasicNameValuePair>();
nvps.add(new BasicNameValuePair("classValue", classValue)); // 参数
httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); // 设置参数给Post
// 执行
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
if (entity != null) {
// System.out.println("Response content length: " +
// entity.getContentLength());
// 显示结果
BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
String line = null;
StringBuilder stringBuilder = new StringBuilder();
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
reader.close();
return stringBuilder.toString();
}
else {
return "return entity is null";
}
}
}

服务器对应的JSP文件:

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="com.gideon.remotedebug.*"%>
<%
String classString = request.getParameter("classValue");
byte[] classByte = classString.getBytes("iso-8859-1");
String result = JavaClassExecuter.execute(classByte);
out.print(result);
out.flush();
%>


第三步,把服务器返回的结果输出到Eclipse的Console里,在插件的Activator中定义一个static的MessageConsoleStream,添加到ConsoleManager中,并在start()中初始化。

private static MessageConsoleStream stream;

public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
MessageConsole remoteDebugConsole=new MessageConsole("RemoteDebug Console", null);
ConsolePlugin.getDefault().getConsoleManager().addConsoles(new IConsole[]{remoteDebugConsole});
stream=remoteDebugConsole.newMessageStream();
}

public static MessageConsoleStream getStream() {
return stream;
}

在输出Message的时候用Activator.getStream().println()

第四步,如果第一步的自动解析出了问题,需要能够手动指定一个路径能够加载正确的Class路径,远程服务器的URL肯定需要从配置文件里读取的,这里用的是Eclipse的Preference。过程不复杂,直接贴代码。

package remotedebug.preferences;
/**
* Constant definitions for plug-in preferences
*/
public class PreferenceConstants {
public static final String P_PATH = "pathPreference";
public static final String P_DEBUG_PATH = "pathDebugPreference";
public static final String P_BOOLEAN = "booleanPreference";
}

package remotedebug.preferences;
import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
import org.eclipse.jface.preference.IPreferenceStore;
import remotedebug.Activator;
/**
* Class used to initialize default preference values.
*/
public class PreferenceInitializer extends AbstractPreferenceInitializer {
/*
* (non-Javadoc)
*
* @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#initializeDefaultPreferences()
*/
public void initializeDefaultPreferences() {
IPreferenceStore store = Activator.getDefault().getPreferenceStore();
store.setDefault(PreferenceConstants.P_BOOLEAN, false);
store.setDefault(PreferenceConstants.P_DEBUG_PATH, "http://localhost:8082/WebDemo/debug.jsp");
}
}

package remotedebug.preferences;
import org.eclipse.jface.preference.*;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.IWorkbench;
import remotedebug.Activator;
/**
* This class represents a preference page that is contributed to the
* Preferences dialog. By subclassing <samp>FieldEditorPreferencePage</samp>, we
* can use the field support built into JFace that allows us to create a page
* that is small and knows how to save, restore and apply itself.
* <p>
* This page is used to modify preferences only. They are stored in the
* preference store that belongs to the main plug-in class. That way,
* preferences can be accessed directly via the preference store.
*/
public class SamplePreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
public SamplePreferencePage() {
super(GRID);
setPreferenceStore(Activator.getDefault().getPreferenceStore());
setDescription("A demonstration of a preference page implementation");
}
/**
* Creates the field editors. Field editors are abstractions of the common
* GUI blocks needed to manipulate various types of preferences. Each field
* editor knows how to save and restore itself.
*/
public void createFieldEditors() {
addField(new BooleanFieldEditor(PreferenceConstants.P_BOOLEAN, "&Use the customize path of the class", getFieldEditorParent()));
addField(new FileFieldEditor(PreferenceConstants.P_PATH, "Choose the class to remote debug", getFieldEditorParent()));
addField(new StringFieldEditor(PreferenceConstants.P_DEBUG_PATH, "Set the remote debug URL", getFieldEditorParent()));
}
/*
* (non-Javadoc)
* @see
* org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
*/
public void init(IWorkbench workbench) {
}
}


第五步,导出成Eclipse插件



把导出的jar包丢到eclipse目录下的dropins目录下,再重启即可

完整的源代码打包下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息