| Summary: | [ds] ClassCircularityError | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Product: | [Eclipse Project] Equinox | Reporter: | Bryan Hunt <bhunt> | ||||||||||
| Component: | Compendium | Assignee: | Stoyan Boshev <s.boshev> | ||||||||||
| Status: | RESOLVED FIXED | QA Contact: | |||||||||||
| Severity: | major | ||||||||||||
| Priority: | P3 | CC: | phperret, remy.suen, s.boshev, slewis, tjwatson | ||||||||||
| Version: | 3.6 | Flags: | tjwatson:
review+
|
||||||||||
| Target Milestone: | 3.6.1 | ||||||||||||
| Hardware: | All | ||||||||||||
| OS: | Mac OS X - Carbon (unsup.) | ||||||||||||
| Whiteboard: | |||||||||||||
| Attachments: |
|
||||||||||||
Why if you declare com.companyX.core.service as a local remote service ? Sorry , I mean WHAT if com.companyX.core.service is declared as a remote local service ? (In reply to comment #2) > Sorry , I mean WHAT if com.companyX.core.service is declared as a remote local > service ? I'm not sure what you mean by "remote local". In my world remote services are related to RFC119 which has no relationship to this problem. This looks like a duplicate of bug #308021. Although this bug was evaluated and partially fixed in 3.6RC1 it was noted that there is no clean solution that would fully solve the ClassCircularityError issues. A suitable workaround that would solve your ClassCircularityError issues if they appear is to remove the lazy activation policy for the bundle which DS components are involved in the ClassCircularityError error. Could you check if that works for you? I'll be glad to check the lazy activation policy. What happens when the bundles really do come from two different companies and I don't have access to the source code? OSGi just breaks? (In reply to comment #5) > What happens when the bundles really do come from two different companies and I > don't have access to the source code? OSGi just breaks? Another workaround would be to change the start order of the bundles. > Another workaround would be to change the start order of the bundles.
That goes against OSGi best practices, but yes, that would be a workaround.
We are dealing with conflicting requirements here. We want lazy activated bundles to make their services available synchronously at startup. But this leads to firing more ServiceEvents that can lead to more components getting activated which then can lead to more lazy trigger classes getting loaded. I agree with Stoyan, this is likely another variation on bug308021. I don't think we can change the DS implementation to all of a sudden stop providing services from lazy activated bundles asynchronously. That will undoubtedly cause many things to break in eclipse (p2 being one of them). I wonder if a work around in DS is to catch the circularity error when building the component and moving the activation to another thread in that case. Created attachment 172087 [details] experiment patch Here is a patch that is an attempt to do what I suggested in comment 8. It seems to fix the issue from this testcase. This is just an example of what I was suggesting, I am not confident or familiar enough with the DS code to suggest this as the real fix. Stoyan, is this an appropriate approach to fixing this issue? BTW, I know some older VMs throw LinkageErrors instead of ClassCircularityErrors but I think it would be a mistake to catch LinkageErrors. (In reply to comment #9) > Created an attachment (id=172087) [details] > experiment patch > > Here is a patch that is an attempt to do what I suggested in comment 8. It > seems to fix the issue from this testcase. This is just an example of what I > was suggesting, I am not confident or familiar enough with the DS code to > suggest this as the real fix. Stoyan, is this an appropriate approach to > fixing this issue? Thanks for the patch Tom. I had a quick look at it. It does not seems correct to me because the method buildComponent() must either return a built component instance or throw a ComponentException. In your case if ClassCircularityError is caught, the method will return null. Perhaps if you throw a ComponentException after the resolver.mgr.enqueueWork() call then it would be fine. I will be able to make more analysis on this issue next week. Created attachment 172116 [details] experiment patch 2 (In reply to comment #10) > Thanks for the patch Tom. I had a quick look at it. It does not seems correct > to me because the method buildComponent() must either return a built component > instance or throw a ComponentException. In your case if ClassCircularityError > is caught, the method will return null. Perhaps if you throw a > ComponentException after the resolver.mgr.enqueueWork() call then it would be > fine. Yeah, I was unsure of the consequences of throwing a ComponentException in this case. But it appears it is caught and handled correctly. I was worried that would bubble up to the client. > I will be able to make more analysis on this issue next week. Thanks, this probably will need to be considered for 3.6.1. Bryan, is this blocking one of your products? I am sure we can come up with a workaround for you in the mean time. Here is an updated patch to throw a ComponentExcpetion in case of the circularity error. Also see http://www.eclipse.org/forums/index.php?t=msg&th=169856&start=0& It appears we need to handle the dynamic bind case as well. (In reply to comment #11) > Thanks, this probably will need to be considered for 3.6.1. Bryan, is this > blocking one of your products? I am sure we can come up with a workaround for > you in the mean time. Tom, this is not blocking a product until we move our target platform to 3.6. Setting the start order may be an acceptable workaround, and if not, I'll bring the problem up again. Reducing to major. I think we cannot fix the problem with the proposed patch. The reason is that when the method InstanceProcess.buildComponent() is executed, in some cases we really count on the returned value by this method. These are the cases when: - the user call ComponentFactory.newInstance() - the user wants to get a service registered by a DS component. In this case the call stack will reach to ServiceReg.getService() or FactoryReg.getService(). In all these cases the current patch would cause to return null as a service or the user will get a ComponentException. This would surely not fit into the user's logic. I still think there is no clean solution in this case. There are only workarounds available. Probably the best workaround would be to change the launch configuration and start the bundle which contains DS components that are in the root of the ClassCircularityError. In the provided test case this would be the "com.companyX.core" bundle. If I change in the launch configuration its auto-start value from "Default" to "true" the test case is successfully run. My problem with using the workaround is that it violates the fundamental OSGi rule: Your code must be independent of start order. (In reply to comment #16) > My problem with using the workaround is that it violates the fundamental OSGi > rule: Your code must be independent of start order. My last suggested workaround does not suggest changing of the bundles start order. I just suggest starting of the affected bundle. (In reply to comment #15) > In all these cases the current patch would cause to return null as a service or > the user will get a ComponentException. This would surely not fit into the > user's logic. I am not sure I agree. The circularity error also does not fit into the user's logic, so which is better? If the user is getting the service then I assume BundleContext.getService(ServiceReference) is ultimately getting called. This method is always allowed to return null and clients should protect against that incases where the service may have been unregistered. ServiceFactory service are also allowed to return null any time they want which would result in null being returned to the client. At any rate, if that is a concern then is there any way to limit this to the case when the DS resolver is enabling a component? Created attachment 173661 [details]
proposed patch 3
The proposed patch contains patch 2 changes. It also includes additional logic that handles the cases when dynamic binding fails. In these cases the failed to bound references will be asynchronously bound later.
This should cover all known cases where ClassCircularityError appear.
+1, thanks for providing a more complete patch Stoyan. I think we should release this into both HEAD and 3.6.1 to get more testing. Patch committed to HEAD and branch R3_6_maintenance |
Created attachment 171842 [details] testcase Eclipse 3.6 build I20100608-0911 In trying to create a testcase to reproduce an EMF problem, I ran across a ClassCircularityError in DS. I've reduced the testcase to eliminate EMF. If you use the supplied launcher, you should get the following on the console: osgi> Activating company Y's secret code !SESSION 2010-06-14 10:24:15.980 ----------------------------------------------- eclipse.buildId=unknown java.version=1.6.0_20 java.vendor=Apple Inc. BootLoader constants: OS=macosx, ARCH=x86_64, WS=cocoa, NL=en_US Command-line arguments: -dev file:/Users/bhunt/Documents/Programming/Code/Workspaces/ds/.metadata/.plugins/org.eclipse.pde.core/ds/dev.properties -os macosx -ws cocoa -arch x86_64 -consoleLog -console !ENTRY org.eclipse.equinox.ds 4 0 2010-06-14 10:24:17.682 !MESSAGE Error while building configuration of component Component[ name = com.companyX.core.service activate = activate deactivate = deactivate modified = configuration-policy = optional factory = null autoenable = true immediate = true implementation = com.companyX.internal.core.OurService state = Unsatisfied properties = serviceFactory = false serviceInterface = null references = { Reference[name = IService, interface = com.companyY.core.IService, policy = static, cardinality = 1..1, target = null, bind = bindService, unbind = null] } located in bundle = com.companyX.core_1.0.0.qualifier [2] ] !STACK 0 java.lang.ClassCircularityError: com/companyY/core/IService at java.lang.Class.getDeclaredMethods0(Native Method) at java.lang.Class.privateGetDeclaredMethods(Class.java:2427) at java.lang.Class.getDeclaredMethod(Class.java:1935) at org.eclipse.equinox.internal.ds.model.ComponentReference.getMethod(ComponentReference.java:96) at org.eclipse.equinox.internal.ds.model.ComponentReference.bind(ComponentReference.java:315) at org.eclipse.equinox.internal.ds.model.ServiceComponentProp.bindReference(ServiceComponentProp.java:413) at org.eclipse.equinox.internal.ds.model.ServiceComponentProp.bind(ServiceComponentProp.java:209) at org.eclipse.equinox.internal.ds.model.ServiceComponentProp.build(ServiceComponentProp.java:327) at org.eclipse.equinox.internal.ds.InstanceProcess.buildComponent(InstanceProcess.java:580) at org.eclipse.equinox.internal.ds.InstanceProcess.buildComponents(InstanceProcess.java:196) at org.eclipse.equinox.internal.ds.Resolver.getEligible(Resolver.java:323) at org.eclipse.equinox.internal.ds.SCRManager.serviceChanged(SCRManager.java:221) at org.eclipse.osgi.internal.serviceregistry.FilteredServiceListener.serviceChanged(FilteredServiceListener.java:104) at org.eclipse.osgi.framework.internal.core.BundleContextImpl.dispatchEvent(BundleContextImpl.java:933) at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:227) at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:149) at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEventPrivileged(ServiceRegistry.java:756) at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEvent(ServiceRegistry.java:711) at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.register(ServiceRegistrationImpl.java:130) at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.registerService(ServiceRegistry.java:206) at org.eclipse.osgi.framework.internal.core.BundleContextImpl.registerService(BundleContextImpl.java:507) at org.eclipse.equinox.internal.ds.InstanceProcess.registerService(InstanceProcess.java:496) at org.eclipse.equinox.internal.ds.InstanceProcess.buildComponents(InstanceProcess.java:212) at org.eclipse.equinox.internal.ds.Resolver.buildNewlySatisfied(Resolver.java:431) at org.eclipse.equinox.internal.ds.Resolver.enableComponents(Resolver.java:213) at org.eclipse.equinox.internal.ds.SCRManager.startedBundle(SCRManager.java:641) at org.eclipse.equinox.internal.ds.SCRManager.bundleChanged(SCRManager.java:234) at org.eclipse.osgi.framework.internal.core.BundleContextImpl.dispatchEvent(BundleContextImpl.java:919) at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:227) at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:149) at org.eclipse.osgi.framework.internal.core.Framework.publishBundleEventPrivileged(Framework.java:1349) at org.eclipse.osgi.framework.internal.core.Framework.publishBundleEvent(Framework.java:1300) at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:380) at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:284) at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:417) at org.eclipse.osgi.internal.loader.BundleLoader.setLazyTrigger(BundleLoader.java:265) at org.eclipse.core.runtime.internal.adaptor.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:106) at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClass(ClasspathManager.java:453) at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.findLocalClass(DefaultClassLoader.java:216) at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:393) at org.eclipse.osgi.internal.loader.SingleSourcePackage.loadClass(SingleSourcePackage.java:33) at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:466) at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:422) at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:410) at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107) at java.lang.ClassLoader.loadClass(ClassLoader.java:248) at com.companyX.internal.core.Activator.start(Activator.java:15) at org.eclipse.osgi.framework.internal.core.BundleContextImpl$1.run(BundleContextImpl.java:783) at java.security.AccessController.doPrivileged(Native Method) at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:774) at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:755) at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:370) at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:284) at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:417) at org.eclipse.osgi.internal.loader.BundleLoader.setLazyTrigger(BundleLoader.java:265) at org.eclipse.core.runtime.internal.adaptor.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:106) at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClass(ClasspathManager.java:453) at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.findLocalClass(DefaultClassLoader.java:216) at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:393) at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:469) at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:422) at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:410) at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107) at java.lang.ClassLoader.loadClass(ClassLoader.java:248) at org.eclipse.osgi.internal.loader.BundleLoader.loadClass(BundleLoader.java:338) at org.eclipse.osgi.framework.internal.core.BundleHost.loadClass(BundleHost.java:232) at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadClass(AbstractBundle.java:1197) at org.eclipse.equinox.internal.ds.model.ServiceComponent.createInstance(ServiceComponent.java:457) at org.eclipse.equinox.internal.ds.model.ServiceComponentProp.createInstance(ServiceComponentProp.java:254) at org.eclipse.equinox.internal.ds.model.ServiceComponentProp.build(ServiceComponentProp.java:315) at org.eclipse.equinox.internal.ds.InstanceProcess.buildComponent(InstanceProcess.java:580) at org.eclipse.equinox.internal.ds.InstanceProcess.buildComponents(InstanceProcess.java:196) at org.eclipse.equinox.internal.ds.Resolver.buildNewlySatisfied(Resolver.java:431) at org.eclipse.equinox.internal.ds.Resolver.enableComponents(Resolver.java:213) at org.eclipse.equinox.internal.ds.SCRManager.performWork(SCRManager.java:800) at org.eclipse.equinox.internal.ds.SCRManager$QueuedJob.dispatch(SCRManager.java:767) at org.eclipse.equinox.internal.ds.WorkThread.run(WorkThread.java:89) at org.eclipse.equinox.internal.util.impl.tpt.threadpool.Executor.run(Executor.java:70) Activating company X's secret code