I want to modify an Eclipse workspace programatically from within a plugin (adding existing projects is my main request).
Also I want to modify CDT options (environment, indexer options) from within that plugin.
Does anyone know how to best do this or can point me to good documentations on that topic?
EDIT:
Actually I don't want to modify CDT project settings but some of the global CDT settings (actually I want to disable the indexer).
It depends the kind of modification you are after.
For instance, adding a project is best illustrated by this thread.
String theProjName = "Test";
String theLocation = "/some/test/project";
try {
IWorkspaceRoot theRoot = ResourcesPlugin.getWorkspace().getRoot();
IProject theProject = theRoot.getProject(theProjName);
IProjectDescription theDesc = theProject.getWorkspace().newProjectDescription(theProjName);
theDesc.setLocation(new Path(theLocation));
theProject.create(theDesc, new NullProgressMonitor());
if (theProject.exists()) {
theProject.open(new NullProgressMonitor());
}
} catch (CoreException err) {
err.printStackTrace();
}
You could also want to open an editor:
IWorkbenchWindow dw = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
try {
if (dw != null) {
IWorkbenchPage page = dw.getActivePage();
if (page != null) {
IDE.openEditor(page, file, true);
}
}
} catch (PartInitException e) {
}
More generally, eclipse.dev.org can be a good source for pointers on that topic.
Since 2004, CDT has options you can modify through the Preference Setting Store (ICSettingsStorage). May be that can help.
Regarding the Indexer, beware of the Discovery Preferences.
I am not sure there is an Indexer API, but you may look at the sources for further clues.
Related
I want to develop an editor for eclipse which works with java projects.
The plugin needs to know the resources in the classpath of the project of the open file. Since the question is quite ambigious i always find threads about the classpath of the bundle/plugin not the project edited with. Can someone tell me the right buzzword(runtime project) or share some code/links for that topic?
To be clear. This editor and its auto-completion/validation has to behave different whenever a classpath entry is added/removed same as the standard java-file editor.
With helpful hint of #howgler i figured out how to get the classpath of the IJavaProject and scan it via google reflections. Hope that help somebody in the future.
#Override
public void init(IEditorSite site, IEditorInput input) throws PartInitException {
INSTANCE = this;
super.init(site, input);
FileEditorInput fei = (FileEditorInput) input;
IFile file = fei.getFile();
IProject project = file.getProject();
try {
if (project.hasNature(JavaCore.NATURE_ID)) {
IJavaProject targetProject = JavaCore.create(project);
final IClasspathEntry[] resolvedClasspath = targetProject.getResolvedClasspath(true);
ArrayList<URL> urls = new ArrayList<>();
for (IClasspathEntry classpathEntry : resolvedClasspath) {
if (classpathEntry.getPath().toFile().isAbsolute()) {
urls.add(classpathEntry.getPath().toFile().toURI().toURL());
} else {
urls.add(new File(project.getWorkspace().getRoot().getLocation().toFile(),classpathEntry.getPath().toString()).toURI().toURL());
}
}
URLClassLoader urlCl = new URLClassLoader(urls.toArray(new URL[urls.size()]));
Reflections reflections = new Reflections(urlCl,new TypeAnnotationsScanner(),new SubTypesScanner(true));
Set<Class<?>> classes = reflections.getTypesAnnotatedWith(<???>.class);
System.out.println(classes);
}
} catch (CoreException | IOException e1) {
e1.printStackTrace();
}
}
So, this is the problem I am stuck at for a few weeks.
I am developing an Eclipse plugin which fills in a View with custom values depending on a particular StackFrame selection in the Debug View.
In particular, I want to listen to the stackframe selected and would like to get the underlying IStackFrame object.
However, I have tried more than a dozen things and all of them have failed. So I tried adding DebugContextListener to get the DebugContextEvent and finally the selection. However, the main issue is that ISelection doesn't return the underlying IStackFrame object. It instead returns an object of type AbstractDMVMNode.DMVMContext. I tried getting an adapter but that didn't work out too. I posted this question sometime back also:
Eclipse Plugin Dev- Extracting IStackFrame object from selection in Debug View
Since then, I have tried out many different approaches. I tried adding IDebugEventSetListener (but this failed as it cannot identify stack frame selection in the debug view).
I tried adding an object contribution action but this too was pointless as it ultimately returned me an ISelection which is useless as it only returns me an object of class AbstractDMVMNode.DMVMContext and not IStackframe.
Moreover, I checked out the implementation of the VariablesView source code itself in the org.eclipse.debug.ui plugin. It looks like a few versions back (VariablesView source code in version 3.2), the underlying logic was to use the ISelection and get the IStackFrame. All the other resources on the internet also advocate the same. However, now, this scheme no longer works as ISelection doesn't return you an IStackFrame. Also, the source code for the latest eclipse Debug plugin (which doesn't use this scheme) was not very intuitive for me.
Can anyone tell how I should proceed ? Is hacking the latest Eclipse source code for VariablesView my only option ? This doesn't look like a good design practice and I believe there should be a much more elegant way of doing this.
PS: I have tried all the techniques and all of them return an ISelection. So, if your approach too return the same thing, then it is most likely incorrect.
Edit (Code snippet for trying to adapt the ISelection):
// Following is the listener implemnetation
IDebugContextListener flistener = new IDebugContextListener() {
#Override
public void debugContextChanged(DebugContextEvent event) {
if ((event.getFlags() & DebugContextEvent.ACTIVATED) > 0) {
contextActivated(event.getContext());
}
};
};
// Few things I tried in the contextActivated Method
//Attempt 1 (Getting the Adapter):
private void contextActivated(ISelection context) {
if (context instanceof StructuredSelection) {
Object data = ((StructuredSelection) context).getFirstElement();
if( data instanceof IAdaptable){
System.out.println("check1");
IStackFrame model = (IStackFrame)((IAdaptable)data).getAdapter(IStackFrame.class);
if(model != null){
System.out.println("success" + model.getName());
}
}
}
}
// Attemp2 (Directly getting it from ISelection):
private void contextActivated(ISelection context) {
if (context instanceof StructuredSelection) {
System.out.println("a");
Object data = ((StructuredSelection) context).getFirstElement();
if (data instanceof IStackFrame) {
System.out.println("yes");
} else {
System.out.println("no" + data.getClass().getName());
}
}
// This always execute the else and it prints: org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode.DMVMContext
}
// Attemp3 (Trying to obtain it from the viewer (similiar to object action binding in some ways):
private void contextActivated(ISelection context) {
VariablesView variablesView = (VariablesView) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().findView(IDebugUIConstants.ID_VARIABLE_VIEW);
if (variablesView != null) {
Object input = ((TreeViewer) variablesView.getViewer()).getInput();
if(input != null) System.out.println(input.getClass().getName());
if (input instanceof IStackFrame) {
System.out.println("success");
} else if (input instanceof IThread) {
System.out.println("success");
try {
IStackFrame[] stackFrames = ((IThread) input).getStackFrames();
for (IStackFrame iStackFrame : stackFrames) {
printVariables(iStackFrame);
}
} catch (DebugException e) {
e.printStackTrace();
}
}
}
}
While I am building this view to work with both JDT & CDT, I am testing it out on the C project. So, this might be the reason why I always get the returned object type as AbstractDMVMNode.DMVMContext. Should my implementation be different to handle both these cases ? I believe I should be building a generic view. Also, if AbstractDMVMNode.DMVMContext is CDT specific, I should I go about implementing it for the CDT case?
I am implementing my own DSL and using Xtend to generate codes. I need some static resources to be copied to my generate code. I was trying to use commons-io, but I couldn't get anywhere with that! What is the best way to do so? I am trying to avoid reading each file and writing to the corresponding file in output path...
This should do (taken from this web site, slightly modified, not tested)
def static void copyFileUsingChannel(File source, File dest) throws IOException {
FileChannel sourceChannel = null;
FileChannel destChannel = null;
try {
sourceChannel = new FileInputStream(source).getChannel();
destChannel = new FileOutputStream(dest).getChannel();
destChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
}finally{
sourceChannel.close();
destChannel.close();
}
}
I want to make a plugin for Eclipse. The thing is that I looked into the API, and examples, and I managed to make a button on main bar, with a specific icon, and when I click it, open up an InputDialog.
The hard part, is that I want to start an aplication from this button, but not with Runtime as it was a new process. I simply want to start a class inside plugin, which will log in to a server and get some output from it. I want it to be opened in a console, like launching a normal application, or a separate console.
The best example of this kind is a Tomcat plugin which starts Tomcat, and then outputs the console to the Eclipse console. I want to do that too. I've looked at the Tomcat source plugin, but I got stuck there too. They use their own launcher.
I am not sure what you mean by "I want to simply start a class". I assume there is a command line tool that you want to execute and redirect its output to the console window.
To be able to do that without spawning a new process, you have to be able to control the output stream of the tool. If it cannot be controlled, then you have no choice but to start a new process to properly capture the tool's output.
It is technically possible to call System.setOut instead, but it will redirect output from all threads to your console which is not what you want.
Nevertheless you start by creating a console:
// function findConsole copied from:
// http://wiki.eclipse.org/FAQ_How_do_I_write_to_the_console_from_a_plug-in%3F
private MessageConsole findConsole(String name) {
ConsolePlugin plugin = ConsolePlugin.getDefault();
IConsoleManager conMan = plugin.getConsoleManager();
IConsole[] existing = conMan.getConsoles();
for (int i = 0; i < existing.length; i++)
if (name.equals(existing[i].getName()))
return (MessageConsole) existing[i];
//No console found, so create a new one.
MessageConsole myConsole = new MessageConsole(name, null);
conMan.addConsoles(new IConsole[]{myConsole});
return myConsole;
}
// Find my console
MessageConsole cons = findConsole("MyTool Console");
MessageConsoleStream out = cons.newMessageStream();
// Optionally get it's input stream so user can interact with my tool
IOConsoleInputStream in = cons.getInputStream();
// Optionally make a differently coloured error stream
MessageConsoleStream err = cons.newMessageStream();
err.setColor(display.getSystemColor(SWT.COLOR_RED));
// Display the console.
// Obtain the active page. See: http://wiki.eclipse.org/FAQ_How_do_I_find_the_active_workbench_page%3F
IWorkbenchPage page = ...;
String id = IConsoleConstants.ID_CONSOLE_VIEW;
IConsoleView view = (IConsoleView) page.showView(id);
view.display(cons);
Then set the input and output streams of my tool and start processing in a different thread so the UI will not block.
// Create my tool and redirect its output
final MyTool myTool = new MyTool();
myTool.setOutputStream(out);
myTool.setErrorStream(err);
myTool.setInputStream(in);
// Start it in another thread
Thread t = new Thread(new Runnable() {
public void run() {
myTool.startExecuting();
}
});
t.start();
If your tool does not support I/O redirection, you have no choice but to start it in another process with the ProcessBuilder and use a number of threads to move data between console and process streams See: Process.getInputStream(), Process.getOutputStream() and Process.getErrorStream().
The following links have additional useful details:
Executing a Java application in a separate process
FAQ How do I write to the console from a plug-in?
FAQ How do I find the active workbench page?
This is the code for running a new console with controls, like stop delete, and deleteAll! This is what I asked for in the beginning, but the message console is good to know!
ILaunchConfigurationType launchType = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurationType("org.eclipse.jdt.launching.localJavaApplication");
ILaunchConfigurationWorkingCopy config = null;
try {
config = launchType.newInstance(null, "My Plugin working");
} catch (CoreException e) {
System.err.println(e.getMessage());
}
config.setAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_ID, "org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector");
String[] classpath = new String[] { "C:\\Users\\Administrator\\Documents\\myjr.jar" };
ArrayList classpathMementos = new ArrayList();
for (int i = 0; i < classpath.length; i++) {
IRuntimeClasspathEntry cpEntry = JavaRuntime.newArchiveRuntimeClasspathEntry(new Path(classpath[i]));
cpEntry.setClasspathProperty(IRuntimeClasspathEntry.USER_CLASSES);
try {
classpathMementos.add(cpEntry.getMemento());
} catch (CoreException e) {
System.err.println(e.getMessage());
}
}
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false);
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, classpathMementos);
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, "collectorlog.handlers.MyClass");
try {
ILAUNCH = config.launch(ILaunchManager.RUN_MODE, null);
} catch (CoreException e) {
System.err.println(e.getMessage());
}
I have a personal Eclipse RCP product (com.example.product) based on one personal feature (com.example.feature) which is composed of one personal plugin (com.example.plugin) and a bunch of others from Eclipse Helios (3.6). I want the app to check for updates and update itself if necessary from a p2 site. I want it to be headless, ie the user does not interact in the update process, but may see progress in a dialog.
I based my implementation for the updates on the RCP Mail application. I changed the P2Util.checkForUpdates method a bit to include some logging so I can see what, if anything, is going wrong there:
static IStatus checkForUpdates(IProvisioningAgent agent,
IProgressMonitor monitor) throws OperationCanceledException,
InvocationTargetException {
ProvisioningSession session = new ProvisioningSession(agent);
UpdateOperation operation = new UpdateOperation(session);
SubMonitor sub = SubMonitor.convert(monitor,
"Checking for application updates...", 200);
IStatus status = operation.resolveModal(sub.newChild(100));
if (status.getCode() == UpdateOperation.STATUS_NOTHING_TO_UPDATE) {
return status;
}
if (status.getSeverity() == IStatus.CANCEL)
throw new OperationCanceledException();
if (status.getSeverity() != IStatus.ERROR) {
try {
logger.info( "Status is " + status );
Update[] updates = operation.getPossibleUpdates();
for( Update u : updates){
logger.info( "Update is " + u );
}
ProvisioningJob job = operation.getProvisioningJob(null);
if( job == null ){
logger.error( "Provisioning Job is null" );
}
status = job.runModal(sub.newChild(100));
if (status.getSeverity() == IStatus.CANCEL) {
throw new OperationCanceledException();
}
} catch ( Exception e ){
logger.error( "Exception while trying to get updates", e);
}
}
return status;
}
I have a p2.inf file in my feature at the same level as my example.product file. It contains:
org.eclipse.equinox.p2.touchpoint.eclipse.addRepository":
instructions.configure=\
org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:0,location:file${#58}/C${#58}/workspace/updatesite/);\
org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:1,location:file${#58}/C${#58}/workspace/updatesite/);
I build the product with plugin, feature and product IDs set to 1.0.0.
I can export and run my product from eclipse using the product export wizard. I tick generate metadata repository when I do this.
I create my update site using the Create an Update Site Project option in the Feature Manfiest Editor. I add my `com.example.feature' and build it. Just to see if it works I try browsing it via eclipse IDE Install New Software option and I can see the feature there.
I build the update site with all 3 IDs changed to 1.0.1. When I start the app it says there are no updates to install, there are no errors in the logs.
I don't know why it doesn't update from the update site, but things that have crossed my mind are:
1) I may need more info in the p2.inf file, but I'm not sure what, maybe something like namespace, name and range, but I can't find a good practical example.
2) In the checkForUpdates method I may need to do something with profiles to change what installable units are being updated. Again, I only found comments hinting at this and not any example code that shows how.
Any hints or ideas are much appreciated here, this is eating a lot of time.
Look at this code. Rebuild your product with a new product version and try to setup a http server. It didnt work with file repo for me. Just publishing the feature will not work.
final IRunnableWithProgress runnable = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
sub = SubMonitor.convert(monitor, Messages.getString("UpdateManager.searchforupdates"), 200); //$NON-NLS-1$
final Update update = getUpdate(profile, provisioningContext, engine, context);
status = operation.resolveModal(sub.newChild(100));
LogHelper.log(status);
if (status.getCode() == UpdateOperation.STATUS_NOTHING_TO_UPDATE) {
status = null;
return;
}
if (status.getSeverity() == IStatus.CANCEL)
throw new OperationCanceledException();
if (status.getSeverity() != IStatus.ERROR) {
log(IStatus.INFO, "Checking for available update matches", null); //$NON-NLS-1$
Update[] selected = new Update[1];
operation.setSelectedUpdates(new Update[0]);
for (Update available : operation.getPossibleUpdates()) {
if (available.equals(update)) {
log(IStatus.INFO, "Update matches available: " + update, null); //$NON-NLS-1$
selected[0] = available;
operation.setSelectedUpdates(selected);
}
}
if (selected[0] == null) {
status = null;
monitor.setCanceled(true);
log(IStatus.WARNING, "No Update matches selected", null); //$NON-NLS-1$
return;
}
ProvisioningJob job = operation.getProvisioningJob(monitor);
if (job != null) {
status = job.runModal(sub.newChild(100));
if (status.getSeverity() != IStatus.ERROR) {
prefStore.setValue(JUSTUPDATED, true);
Display.getDefault().syncExec(new Runnable() {
public void run() {
PlatformUI.getWorkbench().restart();
}
});
} else {
LogHelper.log(status);
}
} else {
log(IStatus.INFO, "getJob returned null", null); //$NON-NLS-1$
status = null;
}
if (status != null && status.getSeverity() == IStatus.CANCEL)
throw new OperationCanceledException();
}
}
};
Display.getDefault().asyncExec(new Runnable() {
public void run() {
try {
new ProgressMonitorDialog(null).run(true, true, runnable);
} catch (InvocationTargetException x) {
log(IStatus.ERROR, "Runnable failure", x); //$NON-NLS-1$
} catch (InterruptedException e) {
}
}
});
#user473284's answer had some suggestions that I used but I don't know if they were definite requirements
1) using a local web server instead of trying to point to a file
2) Incrementing the product version and using the update repository generated by the export product wizard.
I never did find the implementation for the getUpdate method referenced from the code sample so I couldn't make use of the snippet.
After the above changes I was still left with the app detecting no updates on startup. Debugging showed that my repository was not showing up in the session. I had to explicitly add the update url in the code, despite having it in the p2.inf and in set in the feature manifest editor form field. Here's the code for this:
public static void addUpdateSite(IProvisioningAgent provisioningAgent)
throws InvocationTargetException {
// Load repository manager
IMetadataRepositoryManager metadataManager = (IMetadataRepositoryManager) provisioningAgent
.getService(IMetadataRepositoryManager.SERVICE_NAME);
if (metadataManager == null) {
logger.error( "Metadata manager was null");
Throwable throwable = new
Throwable("Could not load Metadata Repository Manager");
throwable.fillInStackTrace();
throw new InvocationTargetException(throwable);
}
// Load artifact manager
IArtifactRepositoryManager artifactManager = (IArtifactRepositoryManager) provisioningAgent
.getService(IArtifactRepositoryManager.SERVICE_NAME);
if (artifactManager == null) {
logger.error( "Artifact manager was null");
Throwable throwable = new Throwable(
"Could not load Artifact Repository Manager");
throwable.fillInStackTrace();
throw new InvocationTargetException(throwable);
}
// Load repo
try {
URI repoLocation = new URI("http://localhost/respository");
logger.info( "Adding repository " + repoLocation );
metadataManager.loadRepository(repoLocation, null);
artifactManager.loadRepository(repoLocation, null);
} catch (ProvisionException pe) {
logger.error( "Caught provisioning exception " + pe.getMessage(), pe);
throw new InvocationTargetException(pe);
} catch (URISyntaxException e) {
logger.error( "Caught URI syntax exception " + e.getMessage(), e);
throw new InvocationTargetException(e);
}
}
I now call this first thing in the checkForUpdates method from the original question. After this change my app at least now sees the update and attempts to install it. I'm still having problem but that deserves a separate question of its own which I've created at https://stackoverflow.com/questions/3944953/error-during-p2-eclipse-rcp-app-headless-update
Web server is not mandatory, you can get updates with file location.
It is mandatory to change product version too.
You can't update those features with Update Site Project build which are exported with product, however you can do that with some hacking in exported product.
If you add some other features with (Install New Softwares) option then you can update these features with Update Site Project build.
Hopefully this will be helpful. If you need more clarification you can ask.