Community
Participate
Working Groups
Build Identifier: Version: 3.7.0, Build id: I20110613-1736 Our company uses a self developed eclipse plugin for resolving maven dependencies. For each maven scope we define an own classpath container. The containers are defined using the standard extension points and implementations of ClasspathContainerInitializer and IClasspathContainer. The content of the containers is set using JavaCore.setClasspathContainer(...). Now we're faced with the problem that the content is not properly restored when a project is closed and afterwards opened again (withouth closing eclipse). When eclipse is closed, the content is sometimes restored for some projects, but not for all projects. When looking into the .metadata\.plugins\org.eclipse.jdt.core\variablesAndContainers.dat file within the workspace we observed that the content of the classpath containers is available for some of the projects, but not for all of them. We tried to force an update by using IJavaProject.save() or IJavaProject.makeConsistent(). Reproducible: Always Steps to Reproduce: 1. use a plugin which defines own classpath containers and fill these containers 2. close the project 3. open the project 4. validate the content of the classpath container (the container itself is available, but not the content).
Created attachment 213445 [details] The classpath containers and its content.
Jay, please take a look.
Roland, Do you notice anything in the log file? It's here: <workspace_loc>/.metadata/.log It would also help if you let us know what you find after running with the following debug options: org.eclipse.jdt.core/debug/cpresolution=true org.eclipse.jdt.core/debug/cpresolution/advanced=true org.eclipse.jdt.core/debug/cpresolution/failure=true
Hi Jay Sorry for my late reply. I did another test with the options turned on, but the log doesn't contain any special information regarding my problem. I added a zip-file with some additional information from my test. Hopefully they're useful. 1.) I started eclipse with a new and clean workspace 2.) I import one single project and resolve the maven dependencies which generates the classpath containers as shown in "after_import_and_resolve.png" -> the full build shows no errors 3.) I close the project using "close project" 4.) I open the project using "open project" 5.) The project doesn't compile anymore since the classpath containers are empty (they're available within the project's property (empty), but not visible in the project tree on the left side) -> see "after_close_open.png" 6.) As visual check of "variablesAndContainers.dat" shows that most of the information is persisted and therefore available. All information is also attached within the zip file. Ps. I realized that the official m2plugin seems to have this problem too. I found a workaround for the problem. Within the classpath initializer the m2plugin restores a previously saved classpath container content. Any change of the classpath container forces a save to the local drive within the workspace. The m2plugin uses a simple object serialization for the storage. Cheers Roland
Created attachment 213798 [details] Screenshots and other files
Thanks for the update, Roland. Looking at the buildpath page where the dependencies look to be resolved, it appears that the persisted containers are indeed restored. But something seem to be wrong with the package explorer tree. Looking at the UI code, I understand we use the same code (IClasspathContainer#getClasspathEntries) to get the entries. Not sure what the problem is. Moving to JDT/UI for further investigation.
Hi Jay You got me wrong: As you can see in the screenshot after_close_open.png within the attached zip-file. The containers itself are restored since they are defined within the .classpath settings file. But the content is definitely not restored (therefore I expanded each defined folder). It's a standard behaviour of the package explorer that only classpath containers with at least one classpath entry are displayed. In the project's buildpath page you always see all defined conainers. Another indicator that the content is not restored is that the project itself is not compilable. None of the import statements can be resolved. It's definitely not a problem of the package view. Addition to the variablesAndContainers.dat: This file is often out of sync. When I looked into it while doing a test with multiple projects I observed that for some projects the content of the containers are persisted in this file, but other projects aren't. Interestingly none of the projects can be restored after a close and open operation (independent of the content of the variablesAndContainers.dat). The fact that even the m2plugin has an own workaround for restoring the content of the classpath containers is a strong indicator that there seems to be a problem within Eclipse JDT itself. Cheers Roland
(In reply to comment #4) > Ps. I realized that the official m2plugin seems to have this problem too. I > found a workaround for the problem. Within the classpath initializer the > m2plugin restores a previously saved classpath container content. Do you refer to org.eclipse.jdt.core.ClasspathContainerInitializer.requestClasspathContainerUpdate(IPath, IJavaProject, IClasspathContainer)? Note that by default it does not do anything, each classpath container implementation needs to take care of this. (In reply to comment #6) > Thanks for the update, Roland. Looking at the buildpath page where the > dependencies look to be resolved, it appears that the persisted containers are > indeed restored. But something seem to be wrong with the package explorer tree. > Looking at the UI code, I understand we use the same code > (IClasspathContainer#getClasspathEntries) to get the entries. > Not sure what the > problem is. > > Moving to JDT/UI for further investigation. Jay, all the API's mentioned here JDT/Core APIs. I fail to see how this is a UI bug.
(In reply to comment #8) > (In reply to comment #4) > > Ps. I realized that the official m2plugin seems to have this problem too. I > > found a workaround for the problem. Within the classpath initializer the > > m2plugin restores a previously saved classpath container content. > Do you refer to > org.eclipse.jdt.core.ClasspathContainerInitializer.requestClasspathContainerUpdate(IPath, > IJavaProject, IClasspathContainer)? Note that by default it does not do > anything, each classpath container implementation needs to take care of this. > True, but setting a break point at any method in this abstract class shows you that the content of the container is not delivered by Eclipse JDT. The content of the containers is always empty. So therefore you're forced to implement an own persistency layer which saves and loads the content of the classpath containers from and to the disk (or somewhere else). I attached some snippets of the m2plugin's workaround. /** * MavenClasspathContainerInitializer * * @author Eugene Kuleshov */ public class MavenClasspathContainerInitializer extends ClasspathContainerInitializer { private static final Logger log = LoggerFactory.getLogger(MavenClasspathContainerInitializer.class); public void initialize(IPath containerPath, IJavaProject project) { if(MavenClasspathHelpers.isMaven2ClasspathContainer(containerPath)) { try { IClasspathContainer mavenContainer = getBuildPathManager().getSavedContainer(project.getProject()); if(mavenContainer != null) { JavaCore.setClasspathContainer(containerPath, new IJavaProject[] {project}, new IClasspathContainer[] {mavenContainer}, new NullProgressMonitor()); return; } } catch(CoreException ex) { log.error("Exception initializing classpath container " + containerPath.toString(), ex); } // force refresh if can't read persisted state IMavenConfiguration configuration = MavenPlugin.getMavenConfiguration(); MavenUpdateRequest request = new MavenUpdateRequest(project.getProject(), configuration.isOffline(), false); getMavenProjectManager().refresh(request); } } public boolean canUpdateClasspathContainer(IPath containerPath, IJavaProject project) { return true; } public void requestClasspathContainerUpdate(IPath containerPath, final IJavaProject project, final IClasspathContainer containerSuggestion) { // one job per request. assumption that users are not going to change hundreds of containers simultaneously. new Job(Messages.MavenClasspathContainerInitializer_job_name) { protected IStatus run(IProgressMonitor monitor) { try { getBuildPathManager().persistAttachedSourcesAndJavadoc(project, containerSuggestion, monitor); } catch(CoreException ex) { log.error(ex.getMessage(), ex); return new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, 0, Messages.MavenClasspathContainerInitializer_error_cannot_persist, ex); } return Status.OK_STATUS; } }.schedule(); } BuildPathManager getBuildPathManager() { return (BuildPathManager) MavenJdtPlugin.getDefault().getBuildpathManager(); } IMavenProjectRegistry getMavenProjectManager() { return MavenPlugin.getMavenProjectRegistry(); } } ---- public IClasspathContainer getSavedContainer(IProject project) throws CoreException { File containerStateFile = getContainerStateFile(project); if(!containerStateFile.exists()) { return null; } FileInputStream is = null; try { is = new FileInputStream(containerStateFile); return new MavenClasspathContainerSaveHelper().readContainer(is); } catch(IOException ex) { throw new CoreException(new Status(IStatus.ERROR, MavenJdtPlugin.PLUGIN_ID, -1, // "Can't read classpath container state for " + project.getName(), ex)); } catch(ClassNotFoundException ex) { throw new CoreException(new Status(IStatus.ERROR, MavenJdtPlugin.PLUGIN_ID, -1, // "Can't read classpath container state for " + project.getName(), ex)); } finally { if(is != null) { try { is.close(); } catch(IOException ex) { log.error("Can't close output stream for " + containerStateFile.getAbsolutePath(), ex); //$NON-NLS-1$ } } } } --- public IClasspathContainer readContainer(InputStream input) throws IOException, ClassNotFoundException { ObjectInputStream is = new ObjectInputStream(new BufferedInputStream(input)) { { enableResolveObject(true); } protected Object resolveObject(Object o) throws IOException { if(o instanceof ProjectEntryReplace) { return ((ProjectEntryReplace) o).getEntry(); } else if(o instanceof LibraryEntryReplace) { return ((LibraryEntryReplace) o).getEntry(); } else if(o instanceof ClasspathAttributeReplace) { return ((ClasspathAttributeReplace) o).getAttribute(); } else if(o instanceof AccessRuleReplace) { return ((AccessRuleReplace) o).getAccessRule(); } else if(o instanceof PathReplace) { return ((PathReplace) o).getPath(); } return super.resolveObject(o); } }; return (IClasspathContainer) is.readObject(); }
(In reply to comment #9) > True, but setting a break point at any method in this abstract class shows you > that the content of the container is not delivered by Eclipse JDT. The content > of the containers is always empty. So therefore you're forced to implement an > own persistency layer which saves and loads the content of the classpath > containers from and to the disk (or somewhere else). Right, so the bug is in the code of your custom classpath container, no? Or are you asking for a default implementation of ClasspathContainerInitializer.requestClasspathContainerUpdate(..) ?
I agree that this looks like a bug in the container. Roland, if you disagree, please attach an example / test case that allows us to reproduce the problem.
Verified for 3.8 M7