If you can work put what a common reflective constructor signature you need is, any lifecycle methods required, I'll include these for every existing Jini service and we can make a standard.
We can also support JSR330 which is AL2 licensed. Support @Inject for the constructor. Provider<JavaSpace05> Regards, Peter. ----- Original message ----- > Hopefully Startable will get enough votes to get across the line, we > need to make progress, if you care about River you'll vote for it, not > because it's the perfect solution, simply because it's an acceptable > solution that makes sense and it marks a progress milestone. > > The reflective constructor signature is acceptable and is used by the > starter kit and Activation. > > We've got Groovy Configuration. > > The service class can also define a static main method. > > If we at least accept these signatures for our services, they could run > in any container. > > Perhaps you also want a public shutdown method for lifecycle management? > Closeable would also be acceptable? > > Is there anything else you need? > > Regards, > > Peter. > > ----- Original message ----- > > "What we need is a discussion on a more developer-friendly service > > startup convention to replace ServiceStarter." > > > > Maybe, that is to say, I'm not anti but I feel there's an additional > > wider aspect to kickaround as well.... > > > > Should there be a failure of the type we've discussed so far, where > > does the responsibility for sorting it out lie? The service writer, > > they created the bug in the first place. BUT, has it occurred to > > anyone that export() isn't the only place multi-threading problems can > > occur in a service implementation? > > > > Even if they've got service construction right (with whatever > > convention or pattern you care to adopt, me, I'm in the camp of if it > > works because it accounts for the JMM it's fine) have they got all > > their multi-threading right elsewhere? What about all those service > > methods exported and advertised by JoinManager? > > > > Do we actually think it's possible to address that challenge? (You > > could do it by single-locking the entire service so only one call can > > be dispatched at a time, yeah right, that's palatable). What makes > > export a special case in this context? Why does that matter so when > > there are many other ways to screw up? > > > > > > On 19 December 2013 20:16, Greg Trasuk <tras...@stratuscom.com> wrote: > > > > > > > > +0. Let me explain… > > > > > > Having a separate interface to say start() is all very well and > > > harmless in itself, but: > > > > > > (a) It doesn’t really address the issue of how service instances > > > should be started. We don’t actually have solid recommendations on > > > this issue, although there have been attempts in various containers > > > to systematize it. > > > > > > (b) the proposed warning addresses a case where a service instance is > > > doing “exporter.export(this);”, which I’d call bad practice in any > > > case. It happens that ServiceStarter appears to encourage this use > > > case because it works by calling a constructor. But the called > > > constructor could (and should) construct an instance of some other > > > class, and then export and register that instance rather than > > > “this”. That style doesn’t mandate a Startable interface, and > > > having Startable doesn’t mandate good multi-threaded code. In > > > other words, you could implement Startable and still have poor > > > thread-safety. > > > > > > (c) even where there is an apparent problem, the exposure window is > > > very small. Consider the last bits of the init(…) method in > > > “com.sun.jini.reggie.ServiceRegistrarImpl” > > > > > > myRef = (Registrar) serverExporter.export(this); > > > proxy = RegistrarProxy.getInstance(myRef, myServiceID); > > > myLocator = (proxy instanceof RemoteMethodControl) ? > > > new ConstrainableLookupLocator( > > > unicastDiscoveryHost, unicaster.port, null) : > > > new LookupLocator(unicastDiscoveryHost, unicaster.port); > > > /* register myself */ > > > Item item = new Item(new ServiceItem(myServiceID, > > > proxy, > > > lookupAttrs)); > > > SvcReg reg = new SvcReg(item, myLeaseID, Long.MAX_VALUE); > > > addService(reg); > > > if (log != null) { > > > log.snapshot(); > > > } > > > > > > try { > > > DiscoveryGroupManagement dgm = (DiscoveryGroupManagement) > > > discoer; > > > String[] groups = dgm.getGroups(); > > > if (groups == null || groups.length > 0) { > > > throw new ConfigurationException( > > > "discoveryManager must be initially configured with " + > > > "no groups"); > > > } > > > DiscoveryLocatorManagement dlm = > > > (DiscoveryLocatorManagement) discoer; > > > if (dlm.getLocators().length > 0) { > > > throw new ConfigurationException( > > > "discoveryManager must be initially configured with " + > > > "no locators"); > > > } > > > dgm.setGroups(lookupGroups); > > > dlm.setLocators(lookupLocators); > > > } catch (ClassCastException e) { > > > throw new ConfigurationException(null, e); > > > } > > > joiner = new JoinManager(proxy, lookupAttrs, myServiceID, > > > discoer, null, config); > > > > > > /* start up all the daemon threads */ > > > serviceExpirer.start(); > > > eventExpirer.start(); > > > unicaster.start(); > > > multicaster.start(); > > > announcer.start(); > > > > > > /* Shutdown hook so reggie sends a final announcement > > > * packet if VM is terminated. If reggie is terminated > > > * through DestroyAdmin.destroy() this hook will have no effect. > > > * A timeout on announcer.join() was considered but not deemed > > > * necessary at this point in time. > > > */ > > > Runtime.getRuntime().addShutdownHook(new Thread( new Runnable() { > > > public void run() { > > > try { > > > announcer.interrupt(); > > > announcer.join(); > > > } catch (Throwable t) { > > > logThrow(Level.FINEST, getClass().getName(), > > > "run", "exception shutting announcer down", > > > new Object[]{}, t); > > > } > > > } > > > })); > > > > > > snapshotter.start(); > > > if (logger.isLoggable(Level.INFO)) { > > > logger.log(Level.INFO, "started Reggie: {0}, {1}, {2}", > > > new Object[]{ myServiceID, > > > Arrays.asList(memberGroups), > > > myLocator }); > > > } > > > ready.ready(); > > > } > > > > > > > > > The window from when the join manager is started up (ignore the > > > serverExporter.export(). The only way a client gets to this > > > endpoint is through a proxy downloaded from the registrar) to when > > > the init() method exits is effectively 9 lines of code, during which > > > time a _lot_ of network i/o would have to happen in order for the > > > object to be used prior to the constructor exit. As well, the > > > actual remote calls are filtered through a command interface and > > > through the tasker object, and the command classes often go through > > > synchronized() blocks, which (I think) act as happens-before fences. > > > > > > The whole thing seems to me a little like the old servlet days, when > > > we got the SingleThreadModel interface and it only served to give > > > naive developers a false sense of security. > > > > > > What we need is a discussion on a more developer-friendly service > > > startup convention to replace ServiceStarter. > > > > > > Having said that, I’m not overly opposed to having Startable, given > > > that as Dan suggested, any output from the ServiceStarter is in the > > > form of a suggestion rather than an error. > > > > > > Cheers, > > > > > > Greg. > > > > > > > > > > > > On Dec 18, 2013, at 7:42 PM, Peter <j...@zeus.net.au> wrote: > > > > > > > +1 Peter. > > > > > > > > > > > > I've been going over the Exporter implementations and Exporter > > > > interface. > > > > > > > > The Exporter interface specifies that export and unexport behaviour > > > > is > > > defined by the implementation. > > > > > > > > That means it's up to the implementation to specify whether a > > > > happens > > > before edge occures before export. > > > > > > > > I propose we document in Exporter, that if export is called during > > > object construction then that object's implementation cannot safely > > > use final fields. > > > > > > > > We should also provide a @see reference to Startable to reccommend > > > > its > > > use when final fields are desirable and include Startable in the > > > spec. > > > > > > > > We don't need to rewrite the example of exporting in > > > > net.jini.config as > > > export isn't performed during construction. > > > > > > > > This is a minor update to the Jini Specification. > > > > > > > > The FINE log message is an implementation detail and won't be > > > > included. > > > > > > > > Regards, > > > > > > > > Peter. > > > > > > > > ----- Original message ----- > > > > > No, an effectively immutable field is one whose reference doesn't > > > > > change after the service objects safe publication. > > > > > > > > > > An immutable field is final. > > > > > > > > > > It would be Jeri's responsibility to safely publish the service > > > > > during export to ensure a happens before event occurs, ensuring > > > > > all non volatile fields the service contains are safely published > > > > > and visible. > > > > > > > > > > It's possible to have mutable fields guarded by synchronized > > > > > methods, or volatile fields. > > > > > > > > > > But it isn't possible to have final fields when exporting within > > > > > the constructor. > > > > > > > > > > If the service contains effectively immutable field references, > > > > > it must be safely published or if another thread accesses those > > > > > fields through unsynchronized methods, the thread may only see > > > > > their default value. > > > > > > > > > > All our service implementations have final fields. All our > > > > > services > > > had > > > > > some unsynchronized access to non volatile, non final fields. > > > > > > > > > > Trouble is, when you can't have final fields, it's very difficult > > > > > to determine which fields are effectively immutable and the > > > > > original programmers design intent, if there's no comment. > > > > > > > > > > Our service implementations all had unsynchronized init() methods > > > > > called from with the constructor that started threads and > > > > > exported Some > > > fields > > > > > were final. > > > > > > > > > > For me, a service that contains final fields and is exported > > > > > after construction is easier to maintain and understand. > > > > > > > > > > I suspect, like our Jini service imlementations, most people get > > > > > it wrong most of the time. > > > > > > > > > > I get that others won't want to use this method, however it would > > > > > be detrimental if we don't have such a method for those who > > > > > would. > > > > > > > > > > Regards, > > > > > > > > > > Peter. > > > > > > > > > > ----- Original message ----- > > > > > > > > > > > > I feel like we’re going down a rabbit hole here when you start > > > > > > talking about exporting immutable objects. Wouldn’t it be > > > > > > kind of silly to export an immutable service? Isn’t the > > > > > > whole point that you interact with the service (i.e. alter its > > > > > > state) over a remote interface? > > > > > > > > > > > > Perhaps it’s better to say that exported services need to be > > > > > > thread-safe (which should be fairly obvious). Yes, > > > > > > immutable objects are inherently thread-safe, so for sharing > > > > > > information inside a VM, it makes some sense to minimize the > > > > > > number of mutable objects you pass around. But > > > > > > fundamentally, we’re talking about shared-state systems here. > > > > > > > > > > > > Cheers, > > > > > > > > > > > > Greg. > > > > > > > > > > > > On Dec 18, 2013, at 7:42 AM, Peter <j...@zeus.net.au> wrote: > > > > > > > > > > > > > Pat your comment about non final fields is interesting. > > > > > > > > > > > > > > Isn't it also the case that we need to safely publish an > > > > > > > effectively immutable object to share it among threads? > > > > > > > That usually means copying it to a thread safe collection or > > > > > > > shared via a synchronized method, volatile field, or final > > > > > > > field in another object? > > > > > > > > > > > > > > So we should also make sure that Jeri uses safe publication > > > > > > > during export. > > > > > > > > > > > > > > That would allow a service that has no final fields to start > > > > > > > threads, then export from within a constructor safely, > > > > > > > provided all operations on non final fields happen before > > > > > > > starting threads and exporting. > > > > > > > > > > > > > > All our services have final fields, so Starter is more > > > > > > > appropriate for River's own services. > > > > > > > > > > > > > > Regards, > > > > > > > > > > > > > > Peter. > > > > > > > > > > > > > > ----- Original message ----- > > > > > > > > Hmm, good point, Startable, makes more sense. > > > > > > > > > > > > > > > > An object can be exported using Startable. > > > > > > > > > > > > > > > > I think we should have a policy to strongly discourage > > > > > > > > exporting from constructors. > > > > > > > > > > > > > > > > Regards, > > > > > > > > > > > > > > > > Peter. > > > > > > > > > > > > > > > > ----- Original message ----- > > > > > > > > > As far as I can tell, the special properties of > > > > > > > > > completing a constructor in the JLS memory > > > > > > > > > model are: > > > > > > > > > > > > > > > > > > 1. A happens-before edge from the end of the constructor > > > > > > > > > to the start of a finalizer. (17.4.5) > > > > > > > > > > > > > > > > > > 2. The guarantee that any thread that only sees a > > > > > > > > > reference to an object after the end of the > > > > > > > > > constructor will see the correctly initialized > > > > > > > > > values of all final fields. (17.5) > > > > > > > > > > > > > > > > > > The special issue with final fields is that > > > > > > > > > implementations have freedom to optimize > > > > > > > > > access to final fields in ways that are not permitted > > > > > > > > > for non-final fields. Strategies for thread > > > > > > > > > safety that work for non-final fields do not > > > > > > > > > necessarily work for final fields. The requirement for > > > > > > > > > final field safety is that the constructor end > > > > > > > > > before another thread accesses the newly constructed > > > > > > > > > object. > > > > > > > > > > > > > > > > > > Calling a start() method after construction if the class > > > > > > > > > implements a new interface seems to me to be > > > > > > > > > harmless, backwards compatible, and useful. It > > > > > > > > > enables the simplest and most direct way of preventing > > > > > > > > > access to the new object by another thread > > > > > > > > > during construction. > > > > > > > > > > > > > > > > > > The roadmap issue is whether it should be required, and > > > > > > > > > if so the level of enforcement. For example, > > > > > > > > > there is no reason to require it if the class > > > > > > > > > does not declare any final fields. > > > > > > > > > > > > > > > > > > Incidentally, and as a detail, "Commission" does not > > > > > > > > > immediately make me think of having a start() > > > > > > > > > method that should be called after construction. > > > > > > > > > If you do go this way, the name needs thought. > > > > > > > > > "Startable" would be more obvious, more > > > > > > > > > memorable, more likely to be found on > > > > > > > > > searches, and more compatible with familiar interface > > > > > > > > > names such as "Cloneable" and "Iterable". > > > > > > > > > > > > > > > > > > Patricia > > > > > > > > > > > > > > > > > > > > > > > > > > > On 12/18/2013 2:18 AM, Peter wrote: > > > > > > > > > > Well, now seems like a good time to have the > > > > > > > > > > conversation. > > > > > > > > > > > > > > > > > > > > Yes there are other ways, but I haven't seen one safe > > > > > > > > > > implementation yet, so... > > > > > > > > > > > > > > > > > > > > Does someone have a better way to solve this problem, > > > > > > > > > > has someone already solved this problem I'm unaware of > > > > > > > > > > that we can adopt, or is there a way that's more > > > > > > > > > > satisfactory? > > > > > > > > > > > > > > > > > > > > If not, is there something objectionable with the > > > > > > > > > > Commission interface and if so, how can we fix it? > > > > > > > > > > > > > > > > > > > > The SEVERE log message is logged by the River start > > > > > > > > > > package, other containers or frameworks can choose > > > > > > > > > > whether or not to do so, but I'd encourage them to do > > > > > > > > > > something similar, yes we can change it to WARN. > > > > > > > > > > > > > > > > > > > > A much harsher option is to throw an exception during > > > > > > > > > > export which breaks backward compatibility. > > > > > > > > > > > > > > > > > > > > Regards, > > > > > > > > > > > > > > > > > > > > Peter. > > > > > > > > > > > > > > > > > > > > ----- Original message ----- > > > > > > > > > > > "org.apache.river.api.util.Commission is an interface > > > > > > > > > > > services should implement" > > > > > > > > > > > > > > > > > > > > > > If it's a SHOULD, not a MUST, chucking out a SEVERE > > > > > > > > > > > is incorrect logger behaviour IMO. You could issue a > > > > > > > > > > > WARN if you like but for even that I'd say you need > > > > > > > > > > > to provide a roadmap explaining why the warning and > > > > > > > > > > > what you intend to do in future and what you expect > > > > > > > > > > > of service writers such as myself. > > > > > > > > > > > > > > > > > > > > > > Commission, at least from my point of view, is your > > > > > > > > > > > means (maybe the River community's - did you ask us?) > > > > > > > > > > > for satisfying your needs in respect of the JMM. As > > > > > > > > > > > we've discussed previously, there are other ways too > > > > > > > > > > > and they work and they are safe if you know what > > > > > > > > > > > you're doing. Your contention was that most don't > > > > > > > > > > > know what they're doing hence, presumably, > > > > > > > > > > > Commission. > > > > > > > > > > > > > > > > > > > > > > So the thing is, you are seemingly on a road to > > > > > > > > > > > asserting more structure (gosh, a standard?) on the > > > > > > > > > > > way people write their services. If so, you'd best > > > > > > > > > > > start flagging that honestly and openly via a > > > > > > > > > > > roadmap, deprecation and such/whatever rather than > > > > > > > > > > > sticking out logger messages with no clear guidance > > > > > > > > > > > and at the cost of a certain amount of nuisance (no > > > > > > > > > > > admin I know likes SEVERE's being logged for > > > > > > > > > > > something which isn't critical cos it's noise they > > > > > > > > > > > don't want in log files). > > > > > > > > > > > > > > > > > > > > > > And of course, we all know that when some entity > > > > > > > > > > > asserts a standard or requirement on others for > > > > > > > > > > > entry, they may choose not to enter. Does this help > > > > > > > > > > > your community or hinder it? The answer to that is, > > > > > > > > > > > it depends. On what? Have you asked or tested? How > > > > > > > > > > > have you tested? What would be considered validation > > > > > > > > > > > or lack of support? > > > > > > > > > > > > > > > > > > > > > > I am not out to flame or troll rather I want to see > > > > > > > > > > > this community demonstrating good behaviour and I'm > > > > > > > > > > > not feeling like what's going on around Commission > > > > > > > > > > > (what is that big change in version number really > > > > > > > > > > > saying?) is such. > > > > > > > > > > > > > > > > > > > > > > On 18 December 2013 08:52, Peter <j...@zeus.net.au> > > > > > > > > > > > wrote: > > > > > > > > > > > > > > > > > > > > > > > Just to clarify > > > > > > > > > > > > org.apache.river.api.util.Commission is an > > > > > > > > > > > > interface services should implement, I would > > > > > > > > > > > > encourage all container projects to pick up the > > > > > > > > > > > > interface and make suggestions for improvement if > > > > > > > > > > > > there are any issues. > > > > > > > > > > > > > > > > > > > > > > > > Interface Commission { > > > > > > > > > > > > void start () throws Exception; > > > > > > > > > > > > } > > > > > > > > > > > > > > > > > > > > > > > > It's called after JMM safe construction to allow > > > > > > > > > > > > the service to start any threads and be exported. > > > > > > > > > > > > > > > > > > > > > > > > Regards, > > > > > > > > > > > > > > > > > > > > > > > > Peter. > > > > > > > > > > > > > > > > > > > > > > > > ----- Original message ----- > > > > > > > > > > > > > > > > > > > > > > > > > > The way that services are instantiated and setup > > > > > > > > > > > > > is an implementation detail. > > > > > > > > > > > > > When I think of compatibility > > > > > > > > > > > > > I think of the API and the lookup methods. We > > > > > > > > > > > > > think of compatibility from a client point of > > > > > > > > > > > > > view. From > > > > > > > > > > > > > the client point of view, using a service looks > > > > > > > > > > > > > like this: > > > > > > > > > > > > > > > > > > > > > > > > > > - Use multicast of unicast discovery to find one > > > > > > > > > > > > > or more ServiceRegistrar instances > > > > > > > > > > > > > - Call > > > > > > > > > > > > > lookup(…) on one or more of these instances to > > > > > > > > > > > > > get a set of service candidates - Choose a > > > > > > > > > > > > > candidate and prepare() it using a > > > > > > > > > > > > > ProxyPreparer, to yield a usable service proxy. > > > > > > > > > > > > > > > > > > > > > > > > > > - Make calls on it. > > > > > > > > > > > > > Ideally hang on to this proxy > > > > > > > > > > > > > instance, so you can skip the discovery and > > > > > > > > > > > > > lookup next time you need it. > > > > > > > > > > > > > - If the call > > > > > > > > > > > > > fails, repeat the lookup (and possibly > > > > > > > > > > > > > discovery) til you get a proxy that works. > > > > > > > > > > > > > > > > > > > > > > > > > > Nowhere does the client need to know whether the > > > > > > > > > > > > > service instance is started up using the > > > > > > > > > > > > > “com.sun.jini.start” mechanism, your Commission > > > > > > > > > > > > > interface, some other IOC container (Rio, > > > > > > > > > > > > > Harvester, Seven or RiverContainer) or some > > > > > > > > > > > > > unknown mechanism that starts with a static > > > > > > > > > > > > > main() method. > > > > > > > > > > > > > > > > > > > > > > > > > > JSK2.0 was 2.0 because of the introduction of the > > > > > > > > > > > > > proxy verification mechanisms, as well as JERI. > > > > > > > > > > > > > Absent some new client usage mechanism, River > > > > > > > > > > > > > doesn’t need to go to 3.0. > > > > > > > > > > > > > > > > > > > > > > > > > > Cheers, > > > > > > > > > > > > > > > > > > > > > > > > > > Greg. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > On Dec 17, 2013, at 1:58 PM, Peter > > > > > > > > > > > > > <j...@zeus.net.au> wrote: > > > > > > > > > > > > > > > > > > > > > > > > > > > I think changing services to use safe > > > > > > > > > > > > > > construction techniques is enough to cause the > > > > > > > > > > > > > > version jump. > > > > > > > > > > > > > > > > > > > > > > > > > > > > At this point I've allowed services to continue > > > > > > > > > > > > > > unsafe construction practices, while logging a > > > > > > > > > > > > > > SEVERE warning when the Commission interface > > > > > > > > > > > > > > isn't implemented, rather than fail. > > > > > > > > > > > > > > > > > > > > > > > > > > > > This is a fundamental change to the way > > > > > > > > > > > > > > services are written. > > > > > > > > > > > > > > > > > > > > > > > > > > > > Regards, > > > > > > > > > > > > > > > > > > > > > > > > > > > > Peter. > > > > > > > > > > > > > > > > > > > > > > > > > > > > ----- Original message ----- > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Assuming that there aren’t major > > > > > > > > > > > > > > > incompatibilities, I think that would be a > > > > > > > > > > > > > > > “minor” version change according to our > > > > > > > > > > > > > > > versioning policy, so we’d be looking at the > > > > > > > > > > > > > > > “2.3” branch rather than a “3.0” release. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > I’m still unnerved by the massive amounts of > > > > > > > > > > > > > > > changes to both code and tests in the > > > > > > > > > > > > > > > qa_refactor branch, as well as the apparent > > > > > > > > > > > > > > > instability of the code, although that seems > > > > > > > > > > > > > > > to be improving. In the next few weeks I’m > > > > > > > > > > > > > > > going to try and setup a cross-test case, to > > > > > > > > > > > > > > > see what the “2.2” tests say about the > > > > > > > > > > > > > > > potential “2.3” release and vice-versa. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > I think what I’d really like to see is an > > > > > > > > > > > > > > > incremental approach where we update limited > > > > > > > > > > > > > > > components of the “2.2” branch, one at a > > > > > > > > > > > > > > > time. Is there anything that we could pull > > > > > > > > > > > > > > > out piecemeal? Maybe it > > > > > > > > > > > > would > > > > > > > > > > > > > > > make sense to split out the infrastructure > > > > > > > > > > > > > > > services, like Reggie, Mahalo, and Outrigger > > > > > > > > > > > > > > > into different sub-projects that could be > > > > > > > > > > > > > > > updated separately? > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Any thoughts? > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Greg. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > On Dec 17, 2013, at 5:03 AM, Peter > > > > > > > > > > > > > > > <j...@zeus.net.au> wrote: > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > When the qa_refactor branch stabilises, I > > > > > > > > > > > > > > > > plan to merge trunk and provide a beta > > > > > > > > > > > > > > > > release for client compatibility testing. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Changes made have been focused on making > > > > > > > > > > > > > > > > our code thread safe, there are significant > > > > > > > > > > > > > > > > changes internally, the public api remains > > > > > > > > > > > > > > > > focused on backward compatibility, however > > > > > > > > > > > > > > > > it is advisable that client services adopt > > > > > > > > > > > > > > > > new safe construction techniques for > > > > > > > > > > > > > > > > services and implement the new Commission > > > > > > > > > > > > > > > > interface. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > What's a suitable test period for client > > > > > > > > > > > > > > > > testing? > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Regards, > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Peter. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >