Oscar, fyi (as you might have noticed) I decided to further refactor the EventBusService so that the underlying guava EventBus is now in effect a singleton, rather than request-scoped. Many fewer moving parts, at the expense of a lack of symmetry.
The noteworthy changes are: - both request-scoped and singleton services can register with the EventBusService. - in both cases, this should be done in @PostConstruct. - Neither need (nor should try) to unregister in a @PreDestroy method. - for request-scoped services, the service's proxy should actually be registered. The javadoc for EventBusService#register() and the JIRA ticket show the idiom. - in addition, it is now possible to inject services into request-scoped services, using @Inject. Hope all that makes sense. Cheers Dan On 13 November 2014 08:49, Dan Haywood <[email protected]> wrote: > My ideas about how to leverage CDI are slowly forming. I envisage that > there being a CDI container that bootstraps the various singleton and > request-scoped services, and then the rest of the runtime "reaches" in for > objects as needs be. Meanwhile, if I can identify a component/piece of > functionality of the framework that could be extracted out into a separate > service (I mean @DomainService), then that makes it easier to CDI-ify in > the future. > > Slowly, slowly... > > Cheers > Dan > > > > On 13 November 2014 08:25, GESCONSULTOR - Óscar Bou < > [email protected]> wrote: > >> Thanks, Dan. >> >> I’ve read the discussion between the 2 alternatives. Also that there are >> some plans to “extend” support for CDI-style. Really interesting. >> >> >> Regards, >> >> Oscar >> >> >> >> El 13/11/2014, a las 1:51, Dan Haywood <[email protected]> >> escribió: >> >> I've raised ISIS-948 for this. >> >> On 21 October 2014 10:38, GESCONSULTOR - Óscar Bou < >> [email protected]> wrote: >> >>> Thanks for your response, Dan. >>> >>> Have you seen the usage pattern at [1] using @PostConstruct and >>> @PreDestroy? >>> >>> Probably it could be used for both request-scoped and singleton >>> services, don't you think? >>> >>> >>> Thanks, >>> >>> Oscar >>> >>> >>> [1] http://es.slideshare.net/koneru9999/guavas-event-bus >>> >>> >>> >>> >>> El 21/10/2014, a las 11:33, Dan Haywood <[email protected]> >>> escribió: >>> >>> Hi Oscar, >>> >>> I need to think this through a bit... I have a strong suspicion that >>> what we have is too complex. My thinking is that I've implemented the >>> registration/deregistratrion for each request/transaction. However, given >>> that services are basically singletons, I'm wondering if there is any >>> reason not to just register them once at system startup and then leave it >>> at that. >>> >>> The thing that's uncertain to me is how any request-scoped services >>> would behave... these are implemented as Javassist wrappers around the >>> underlying service pojo, setup and torn down for each request. >>> >>> None of the Isis-provided request-scoped services (eg QueryResultsCache, >>> Scratchpad) subscribe to the event bus, so there would be no issue with >>> those. And my guess is that there's probably no-one in the user community >>> who has written their own. >>> >>> So perhaps a first-cut simplification would be to register all singleton >>> services with the event bus, but to ignore any request-scoped services. >>> >>> Thoughts? >>> >>> In the meantime, the good news is that you can register your own >>> implementation of EventBusService (via isis.properties) and it will be used >>> instead of the default impl. >>> >>> Dan >>> >>> >>> On 21 October 2014 10:10, GESCONSULTOR - Óscar Bou < >>> [email protected]> wrote: >>> >>>> >>>> There's a thread discussing the multiple registration and unregister >>>> exception thrown subjects here [1]. >>>> >>>> Perhaps a similar implementation on the EventBusService will also do >>>> the trick. >>>> >>>> Any opinions? >>>> >>>> >>>> Thanks, >>>> >>>> Oscar >>>> >>>> [1] https://code.google.com/p/guava-libraries/issues/detail?id=784 >>>> >>>> >>>> >>>> El 21/10/2014, a las 00:52, GESCONSULTOR - Óscar Bou < >>>> [email protected]> escribió: >>>> >>>> >>>> Just to clarify in order to being able to determine the root cause. >>>> >>>> We are currently registering the EventBusService as this: >>>> >>>> public class AssetsRiskInformation extends >>>> AbstractSingletonMultiTenantObjectRepositoryAndFactory<AssetRiskInformation> >>>> { >>>> >>>> ... >>>> >>>> // {{ injected: EventBusService >>>> private EventBusService eventBusService; >>>> >>>> public final void injectEventBusService(final EventBusService >>>> eventBusService) { >>>> if (this.eventBusService == null) { >>>> this.eventBusService = eventBusService; >>>> eventBusService.register(this); >>>> } >>>> } >>>> // }} >>>> >>>> } >>>> >>>> As seemed that the injectEventBusService(xxx) method was called more >>>> than once, the register(this) must verify it's the first time. >>>> >>>> Also, as seemed that the Service was "unregistered" by Isis when ending >>>> a transaction, as per: >>>> >>>> Caused by: java.lang.IllegalArgumentException: missing event subscriber >>>> for an annotated method. Is >>>> com.xms.framework.risk.domain.model.continuity.AssetsBCMInformation@552cc955 >>>> registered? >>>> at com.google.common.eventbus.EventBus.unregister(EventBus.java:230) >>>> at >>>> org.apache.isis.core.runtime.services.eventbus.EventBusServiceDefault.close(EventBusServiceDefault.java:63) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransaction.closeOtherApplibServicesIfConfigured(IsisTransaction.java:776) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransaction.closeServices(IsisTransaction.java:758) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransaction.preCommitServices(IsisTransaction.java:748) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransaction.preCommit(IsisTransaction.java:727) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.endTransaction(IsisTransactionManager.java:470) >>>> at >>>> com.xms.framework.devstudio.service.AbstractDevStudioDomainService.executeAction(AbstractDevStudioDomainService.java:184) >>>> >>>> >>>> >>>> We are not sure if the service that the service is re-registered when >>>> starting a new transaction: >>>> >>>> java.util.ConcurrentModificationException >>>> at java.util.HashMap$HashIterator.nextEntry(HashMap.java:894) >>>> at java.util.HashMap$KeyIterator.next(HashMap.java:928) >>>> at >>>> org.apache.isis.core.runtime.services.eventbus.EventBusServiceDefault.open(EventBusServiceDefault.java:55) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.initOtherApplibServicesIfConfigured(IsisTransactionManager.java:291) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.startTransaction(IsisTransactionManager.java:269) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.executeWithinTransaction(IsisTransactionManager.java:155) >>>> at >>>> com.xms.framework.api.domain.model.isis.AbstractXMSDomainObjectRepositoryAndFactory.doFindByPropMultiTenant(AbstractXMSDomainObjectRepositoryAndFactory.java:695) >>>> at >>>> com.xms.framework.api.domain.model.isis.AbstractXMSDomainObjectRepositoryAndFactory.doFindByPropMultiTenant(AbstractXMSDomainObjectRepositoryAndFactory.java:682) >>>> >>>> >>>> >>>> >>>> Despite that, I think that this concurrency exception should be avoided: >>>> >>>> java.util.ConcurrentModificationException >>>> at java.util.HashMap$HashIterator.nextEntry(HashMap.java:894) >>>> at java.util.HashMap$KeyIterator.next(HashMap.java:928) >>>> at >>>> org.apache.isis.core.runtime.services.eventbus.EventBusServiceDefault.open(EventBusServiceDefault.java:55) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.initOtherApplibServicesIfConfigured(IsisTransactionManager.java:291) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.startTransaction(IsisTransactionManager.java:269) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.executeWithinTransaction(IsisTransactionManager.java:155) >>>> at >>>> com.xms.framework.api.domain.model.isis.AbstractXMSDomainObjectRepositoryAndFactory.doFindByPropMultiTenant(AbstractXMSDomainObjectRepositoryAndFactory.java:695) >>>> at >>>> com.xms.framework.api.domain.model.isis.AbstractXMSDomainObjectRepositoryAndFactory.doFindByPropMultiTenant(AbstractXMSDomainObjectRepositoryAndFactory.java:682) >>>> >>>> >>>> >>>> On last page (22) of the following slideshare presentation [1], seems >>>> that once injected, the safest way to register and unregister it is by >>>> means of the @PostConstruct and @PreDestroy annotations, but not sure if >>>> that would eliminate the exceptions thrown. >>>> >>>> >>>> HTH, >>>> >>>> Oscar >>>> >>>> >>>> [1] http://es.slideshare.net/koneru9999/guavas-event-bus >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> El 20/10/2014, a las 22:31, GESCONSULTOR - Óscar Bou < >>>> [email protected]> escribió: >>>> >>>> Hi to all. >>>> >>>> Our app is using the EventBusServiceDefault implementation and the >>>> following exception is thrown occasionally: >>>> >>>> java.util.ConcurrentModificationException >>>> at java.util.HashMap$HashIterator.nextEntry(HashMap.java:894) >>>> at java.util.HashMap$KeyIterator.next(HashMap.java:928) >>>> at >>>> org.apache.isis.core.runtime.services.eventbus.EventBusServiceDefault.open(EventBusServiceDefault.java:55) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.initOtherApplibServicesIfConfigured(IsisTransactionManager.java:291) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.startTransaction(IsisTransactionManager.java:269) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.executeWithinTransaction(IsisTransactionManager.java:155) >>>> at >>>> com.xms.framework.api.domain.model.isis.AbstractXMSDomainObjectRepositoryAndFactory.doFindByPropMultiTenant(AbstractXMSDomainObjectRepositoryAndFactory.java:695) >>>> at >>>> com.xms.framework.api.domain.model.isis.AbstractXMSDomainObjectRepositoryAndFactory.doFindByPropMultiTenant(AbstractXMSDomainObjectRepositoryAndFactory.java:682) >>>> >>>> >>>> After that, some objects domain services that were previously >>>> registered seem to not being found, is it possible? >>>> >>>> Exceptions like the following one are found on the webapp log: >>>> >>>> at >>>> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) >>>> at >>>> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) >>>> at >>>> org.apache.isis.core.webapp.IsisSessionFilter$SessionState$1.handle(IsisSessionFilter.java:320) >>>> at >>>> org.apache.isis.core.webapp.IsisSessionFilter.doFilter(IsisSessionFilter.java:409) >>>> at >>>> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) >>>> at >>>> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) >>>> at >>>> org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449) >>>> at >>>> org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365) >>>> at >>>> org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90) >>>> at >>>> org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83) >>>> at >>>> org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383) >>>> at >>>> org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362) >>>> at >>>> org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) >>>> at >>>> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) >>>> at >>>> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) >>>> at >>>> org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224) >>>> at >>>> org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169) >>>> at >>>> org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) >>>> at >>>> org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168) >>>> at >>>> org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98) >>>> at >>>> org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927) >>>> at >>>> org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) >>>> at >>>> org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407) >>>> at org.apache.coyote.ajp.AjpProcessor.process(AjpProcessor.java:200) >>>> at >>>> org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:579) >>>> at >>>> org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:307) >>>> at >>>> java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) >>>> at >>>> java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) >>>> at java.lang.Thread.run(Thread.java:724) >>>> Caused by: java.lang.IllegalArgumentException: missing event subscriber >>>> for an annotated method. Is >>>> com.xms.framework.risk.domain.model.continuity.AssetsBCMInformation@552cc955 >>>> registered? >>>> at com.google.common.eventbus.EventBus.unregister(EventBus.java:230) >>>> at >>>> org.apache.isis.core.runtime.services.eventbus.EventBusServiceDefault.close(EventBusServiceDefault.java:63) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransaction.closeOtherApplibServicesIfConfigured(IsisTransaction.java:776) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransaction.closeServices(IsisTransaction.java:758) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransaction.preCommitServices(IsisTransaction.java:748) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransaction.preCommit(IsisTransaction.java:727) >>>> at >>>> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.endTransaction(IsisTransactionManager.java:470) >>>> at >>>> com.xms.framework.devstudio.service.AbstractDevStudioDomainService.executeAction(AbstractDevStudioDomainService.java:184) >>>> ... 76 more >>>> 2014-10-20 11:26:15,092 INFO >>>> [org.jasig.cas.services.DefaultServicesManagerImpl] - <Reloading registered >>>> services.> >>>> 2014-10-20 11:26:15,092 INFO >>>> [org.jasig.cas.services.DefaultServicesManagerImpl] - <Loaded 4 services.> >>>> 11:26:31,572 [JSONRPCController ajp-bio-18009-exec-358 ERROR] >>>> missing event subscriber for an annotated method. Is >>>> com.xms.framework.risk.domain.model.*AssetsRiskInformation*@2f6ce962 >>>> registered? >>>> com.xms.framework.api.exception.XMSRuntimeException: missing event >>>> subscriber for an annotated method. Is com.xms.framework.risk.domain.model. >>>> *AssetsRiskInformation*@2f6ce962 registered? >>>> at >>>> com.xms.framework.devstudio.service.AbstractDevStudioDomainService.executeAction(AbstractDevStudioDomainService.java:189) >>>> at >>>> com.xms.framework.impl.devstudio.service.XMSDomainCustomizeMethodsService.assetsBCMInformation_businessContinuityInformation(XMSDomainCustomizeMethodsService.java:16396) >>>> at sun.reflect.GeneratedMethodAccessor10181.invoke(Unknown Source) >>>> at >>>> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) >>>> at java.lang.reflect.Method.invoke(Method.java:606) >>>> at >>>> com.wavemaker.runtime.service.reflect.ReflectServiceType.invokeMethod(ReflectServiceType.java:115) >>>> >>>> >>>> >>>> >>>> >>>> Not sure if last exception is caused by first one, and if first one >>>> solution is "simply" to declare a concurrent Set... >>>> >>>> Thanks in advance! >>>> >>>> >>>> >>>> *Óscar Bou Bou* >>>> Responsable de Producto >>>> Auditor Jefe de Certificación ISO 27001 en BSI >>>> CISA, CRISC, APMG ISO 20000, ITIL-F >>>> >>>> <contactenos.html.gif> 902 900 231 / 620 267 520 >>>> <Pasted Graphic 1.tiff> http://www.twitter.com/oscarbou >>>> >>>> <gesdatos-software.gif> http://es.linkedin.com/in/oscarbou >>>> >>>> <blog.png> http://www.GesConsultor.com <http://www.gesconsultor.com/> >>>> >>>> >>>> <gesconsultor_logo_blue_email.png> >>>> >>>> >>>> Este mensaje y los ficheros anexos son confidenciales. Los mismos >>>> contienen información reservada que no puede ser difundida. Si usted ha >>>> recibido este correo por error, tenga la amabilidad de eliminarlo de su >>>> sistema y avisar al remitente mediante reenvío a su dirección electrónica; >>>> no deberá copiar el mensaje ni divulgar su contenido a ninguna persona. >>>> Su dirección de correo electrónico junto a sus datos personales constan >>>> en un fichero titularidad de Gesdatos Software, S.L. cuya finalidad es la >>>> de mantener el contacto con Ud. Si quiere saber de qué información >>>> disponemos de Ud., modificarla, y en su caso, cancelarla, puede hacerlo >>>> enviando un escrito al efecto, acompañado de una fotocopia de su D.N.I. a >>>> la siguiente dirección: Gesdatos Software, S.L. , Paseo de la Castellana, >>>> 153 bajo - 28046 (Madrid), y Avda. Cortes Valencianas num. 50, 1ºC - 46015 >>>> (Valencia). Asimismo, es su responsabilidad comprobar que este mensaje o >>>> sus archivos adjuntos no contengan virus informáticos, y en caso que los >>>> tuvieran eliminarlos. >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> *Óscar Bou Bou* >>>> Responsable de Producto >>>> Auditor Jefe de Certificación ISO 27001 en BSI >>>> CISA, CRISC, APMG ISO 20000, ITIL-F >>>> >>>> <contactenos.html.gif> 902 900 231 / 620 267 520 >>>> <Pasted Graphic 1.tiff> http://www.twitter.com/oscarbou >>>> >>>> <gesdatos-software.gif> http://es.linkedin.com/in/oscarbou >>>> >>>> <blog.png> http://www.GesConsultor.com <http://www.gesconsultor.com/> >>>> >>>> >>>> <gesconsultor_logo_blue_email.png> >>>> >>>> >>>> Este mensaje y los ficheros anexos son confidenciales. Los mismos >>>> contienen información reservada que no puede ser difundida. Si usted ha >>>> recibido este correo por error, tenga la amabilidad de eliminarlo de su >>>> sistema y avisar al remitente mediante reenvío a su dirección electrónica; >>>> no deberá copiar el mensaje ni divulgar su contenido a ninguna persona. >>>> Su dirección de correo electrónico junto a sus datos personales constan >>>> en un fichero titularidad de Gesdatos Software, S.L. cuya finalidad es la >>>> de mantener el contacto con Ud. Si quiere saber de qué información >>>> disponemos de Ud., modificarla, y en su caso, cancelarla, puede hacerlo >>>> enviando un escrito al efecto, acompañado de una fotocopia de su D.N.I. a >>>> la siguiente dirección: Gesdatos Software, S.L. , Paseo de la Castellana, >>>> 153 bajo - 28046 (Madrid), y Avda. Cortes Valencianas num. 50, 1ºC - 46015 >>>> (Valencia). Asimismo, es su responsabilidad comprobar que este mensaje o >>>> sus archivos adjuntos no contengan virus informáticos, y en caso que los >>>> tuvieran eliminarlos. >>>> >>>> >>>> >>>> >>>> >>>> >>> >>> >>> *Óscar Bou Bou* >>> Responsable de Producto >>> Auditor Jefe de Certificación ISO 27001 en BSI >>> CISA, CRISC, APMG ISO 20000, ITIL-F >>> >>> <contactenos.html.gif> 902 900 231 / 620 267 520 >>> <Pasted Graphic 1.tiff> http://www.twitter.com/oscarbou >>> >>> <gesdatos-software.gif> http://es.linkedin.com/in/oscarbou >>> >>> <blog.png> http://www.GesConsultor.com <http://www.gesconsultor.com/> >>> >>> <gesconsultor_logo_blue_email.png> >>> >>> >>> Este mensaje y los ficheros anexos son confidenciales. Los mismos >>> contienen información reservada que no puede ser difundida. Si usted ha >>> recibido este correo por error, tenga la amabilidad de eliminarlo de su >>> sistema y avisar al remitente mediante reenvío a su dirección electrónica; >>> no deberá copiar el mensaje ni divulgar su contenido a ninguna persona. >>> Su dirección de correo electrónico junto a sus datos personales constan >>> en un fichero titularidad de Gesdatos Software, S.L. cuya finalidad es la >>> de mantener el contacto con Ud. Si quiere saber de qué información >>> disponemos de Ud., modificarla, y en su caso, cancelarla, puede hacerlo >>> enviando un escrito al efecto, acompañado de una fotocopia de su D.N.I. a >>> la siguiente dirección: Gesdatos Software, S.L. , Paseo de la Castellana, >>> 153 bajo - 28046 (Madrid), y Avda. Cortes Valencianas num. 50, 1ºC - 46015 >>> (Valencia). Asimismo, es su responsabilidad comprobar que este mensaje o >>> sus archivos adjuntos no contengan virus informáticos, y en caso que los >>> tuvieran eliminarlos. >>> >> >> >> *Óscar Bou Bou* >> Responsable de Producto >> Auditor Jefe de Certificación ISO 27001 en BSI >> CISA, CRISC, APMG ISO 20000, ITIL-F >> >> 902 900 231 / 620 267 520 >> http://www.twitter.com/oscarbou >> >> http://es.linkedin.com/in/oscarbou >> >> http://www.GesConsultor.com <http://www.gesconsultor.com/> >> >> >> >> Este mensaje y los ficheros anexos son confidenciales. Los mismos >> contienen información reservada que no puede ser difundida. Si usted ha >> recibido este correo por error, tenga la amabilidad de eliminarlo de su >> sistema y avisar al remitente mediante reenvío a su dirección electrónica; >> no deberá copiar el mensaje ni divulgar su contenido a ninguna persona. >> Su dirección de correo electrónico junto a sus datos personales constan >> en un fichero titularidad de Gesdatos Software, S.L. cuya finalidad es la >> de mantener el contacto con Ud. Si quiere saber de qué información >> disponemos de Ud., modificarla, y en su caso, cancelarla, puede hacerlo >> enviando un escrito al efecto, acompañado de una fotocopia de su D.N.I. a >> la siguiente dirección: Gesdatos Software, S.L. , Paseo de la Castellana, >> 153 bajo - 28046 (Madrid), y Avda. Cortes Valencianas num. 50, 1ºC - 46015 >> (Valencia). Asimismo, es su responsabilidad comprobar que este mensaje o >> sus archivos adjuntos no contengan virus informáticos, y en caso que los >> tuvieran eliminarlos. >> >> >> >> >> >> >
