That is essentially the same as Gary’s use case. Ralph
> On Sep 17, 2016, at 9:06 AM, Matt Sicker <boa...@gmail.com> wrote: > > I can see another use case here, though I'm not sure if it's covered already > by RoutingAppender. In a development environment, you'd have log messages > going to a log file, but in production, you'd have log messages being going > to a KafkaAppender or FlumeAppender. It'd be useful in microservice > development, that's for sure. An easy way some developers might implement > that would be checking the OS for Mac or Windows versus Linux to determine > the environment, or even just a system property. > > On 17 September 2016 at 00:31, Gary Gregory <garydgreg...@gmail.com > <mailto:garydgreg...@gmail.com>> wrote: > On Fri, Sep 16, 2016 at 8:38 PM, Ralph Goers <ralph.go...@dslextreme.com > <mailto:ralph.go...@dslextreme.com>> wrote: > Gary, > > I have no problem with components that can be dumbed down to do simple > things. I do have a problem with components that only do simple things > because people will constantly asked to have them be enhanced. > > As for what you are proposing here, can I just say “No”? > > Sure! :-) You can say whatever you want! :-) > > Having the Appenders element deferred just smells to me and having an > arbitrary script there just seems weird to me. Does it even have a contract > or is it a free-for-all? How does it cause multiple appenders to be > initialized? > > I think the RoutingAppender is a more appropriate solution. However, if you > want to dumb it down a bit and turn it into an AppenderSelector I’d be ok > with that. However, it would still be fairly similar to the RoutingAppender. > > OK, so going back to one of your eariler messages: > > ==copy start== > > This sort of sounds like you want an Appender Selector, which would be an > Appender that uses a Selector to figure out which Appender to delegate to. > This is a bit like the PatternSelector. I would imagine it would make sense > to implement AppenderSelectors and LayoutSelectors. You probably would want > to dynamically initialize the Appenders much like the RoutingAppender does. > > Maybe it would look like: > > <Appenders> > <ScriptSelector name=“" default=“”> > <Script language=“groovy”><![CDATA[ > if (System.getProperty”os.name > <http://os.name/>”).contains(“OS/390”)) then { > return “Socket”; > } else { > return “File”; > } > </Script> > <Appenders> > <SocketAppender name=“Socket” …/> > <FileAppender name=“File” …/> > </Appenders> > </ScriptSelector> > </Appenders> > > The thing is that this script would run every time the Selector was accessed > while it sounds like you would only want the script to run when the Selector > is initialized. We could do that too but the script would need to be declared > in a property that would only be used when the selector is initialized. I > would want to support being able to do both. > > ==copy end== > > This is indeed like the RoutingAppender _except_ that the whole point is to > do the script selection on start up. When you say that you'd want it both > ways, on start up and on each log event; what would the configuration > difference look like? > > But.. "Appender that uses a Selector to figure out which Appender to delegate > to" ... that is _so_ much like a RoutingAppender as to be redundant, no? > > What I want is for the script to determine which appender to use (once), and > instantiate that appender (once). There is no need for one appender to > delegate to another appender. > > The more general case is for the script to determine which appenders (plural) > to use (once), and instantiate those appenders (plural) (once). There is no > need for one appender to delegate to another appender list. I do not have a > use case for this today, but I do for the one appender case. > > My goal would be explained to a user like this: "This feature helps you build > your configuration dynamically, all from the configuration file, to determine > which appender(s) to configure. This is different from using a > RoutingAppender which creates a level of indirection and decides what to do > for each log event _at runtime_" Yes, this is a simpler explanation than also > explaining the new role of scripts in the RoutingAppender but you get the > idea. > > I am open different solutions that meet the goal of building the > configuration dynamically, as if you'd done it in XML explicitly (or JSON) > but does not end up with one appender delegating to another. > > Thoughts? > > Gary > > > > Ralph > > >> On Sep 16, 2016, at 11:43 AM, Gary Gregory <garydgreg...@gmail.com >> <mailto:garydgreg...@gmail.com>> wrote: >> >> Now I've dived into this part of the code and consider what this >> configuration means for my use case, I see that it works and that the new >> feature has merit on its own but... It feels to me like my specific use case >> is an edge case of this new routing appender feature: I will only ever have >> one route and that route is determined at start up time and will never >> change. So it feels rather a heavy hammer for my fly. >> >> What I think would be nicer is this: >> >> <Configuration status="WARN" name="RoutingTest"> >> <Appenders> >> <Script name="AddAppender" language="JavaScript"><![CDATA[ >> "OSNameFoo".search("Foo") > -1 ? "List2" : "List1";]]> >> </Script> >> <List name="List1" /> >> <List name="List2" /> >> </Appenders> >> <Loggers> >> <Root level="error"> >> <AppenderRef ref="Routing" /> >> </Root> >> </Loggers> >> </Configuration> >> >> The script AddAppender runs when Appenders is instantiated and picks which >> appender to add. >> >> I think this means that the Appenders plugin must have deferChildren=true. >> When created the Appender checks the name of the script, right now there is >> only "AddAppender" but you could imagine other names like "AddAppenders" >> (plural). If there is no script, the Appenders plugin converts the nodes >> into configurations, which gives us the same result as before this change, >> it's just that the convertion from nodes to configured items happens a >> little later. If there is a script, then it is run and the semantics are >> applied, in my case, pick the one Appender node and convert it to a >> configured appender. >> >> Thoughts? >> >> Gary >> >> On Tue, Sep 13, 2016 at 4:53 PM, Gary Gregory <garydgreg...@gmail.com >> <mailto:garydgreg...@gmail.com>> wrote: >> I committed a first cut, see comments in >> https://issues.apache.org/jira/browse/LOG4J2-1578 >> <https://issues.apache.org/jira/browse/LOG4J2-1578> >> >> Gary >> >> On Mon, Sep 12, 2016 at 11:40 PM, Ralph Goers <ralph.go...@dslextreme.com >> <mailto:ralph.go...@dslextreme.com>> wrote: >> Yes, it returns the key. Remember, a Route can dynamically create an >> Appender so it isn’t required to be a reference. At the same time we can >> (and probably should) pass variables and/or a Map to the script that it can >> update in any way it wants for later usage by the Routing script. As is >> shown, it can also return null which leaves the default route in place. So >> it need not be strictly for returning the default route. >> >> Ralph >> >>> On Sep 12, 2016, at 10:30 PM, Gary Gregory <garydgreg...@gmail.com >>> <mailto:garydgreg...@gmail.com>> wrote: >>> >>> Wait a sec, the DefaultRouteScript should return the Route key, not the >>> Route ref. Right? >>> >>> Gary >>> >>> On Mon, Sep 12, 2016 at 9:53 PM, Gary Gregory <garydgreg...@gmail.com >>> <mailto:garydgreg...@gmail.com>> wrote: >>> "First, the init script changes the default route based on the OS." >>> >>> Maybe the tag should be called "DefaultRouteScript" since it's job is to >>> return the default route name? >>> >>> Gary >>> >>> On Mon, Sep 12, 2016 at 8:05 PM, Ralph Goers <ralph.go...@dslextreme.com >>> <mailto:ralph.go...@dslextreme.com>> wrote: >>> After reviewing what I wrote below and looking at the Routing Appender I >>> think the best thing to do is just to add script support to it. It already >>> has support for a default Route. The init script, if present, could >>> override which Route to use as I described below. Then we could add a >>> script attribute to the Routes plugin which could be used to select the >>> Route instead of only matching on the ThreadContext key. >>> >>> With that I think you would have everything you want, plus it could be used >>> as a more intelligent way to route to existing appenders. >>> >>> The configuration would then look like: >>> >>> <Appenders> >>> <Console name="STDOUT" target="SYSTEM_OUT"> >>> <PatternLayout pattern="%m%n"/> >>> </Console> >>> <Flume name="AuditLogger" compress="true"> >>> <Agent host="192.168.10.101" port="8800"/> >>> <Agent host="192.168.10.102" port="8800"/> >>> <RFC5424Layout enterpriseNumber="18060" includeMDC="true" >>> appName="MyApp"/> >>> </Flume> >>> <Routing name=?Routing?> >>> <InitScript name=?RoutingInit" language="groovy"><![CDATA[ >>> if (System.getProperty(?os.name?).contains(?OS/390 >>> <http://os.name/?).contains(?OS/390>") { >>> return “OS390"; >>> } >>> return null;]]> >>> </InitScript> >>> <Routes> >>> <Script name="Router" language="groovy"><![CDATA[ >>> if (logEvent.getMarker() != null && >>> logEvent.getMarker().isInstanceOf("AUDIT")) { >>> return "AUDIT"; >>> } else if (logEvent.getContextMap().containsKey("UserId")) { >>> return logEvent.getContextMap().get("UserId"); >>> } >>> return "STDOUT"; >>> ]]> >>> </Script> >>> <Route> >>> <OS390Appender name=“OS390-${mdc:UserId”/> >>> <RollingFile name="Rolling-${mdc:UserId}" >>> fileName="${mdc:UserId}.log" >>> filePattern="${mdc:UserId}.%i.log.gz"> >>> <PatternLayout> >>> <pattern>%d %p %c{1.} [%t] %m%n</pattern> >>> </PatternLayout> >>> <SizeBasedTriggeringPolicy size="500" /> >>> </RollingFile> >>> </Route> >>> <Route ref="AuditLogger" key="Audit"/> >>> <Route ref="STDOUT" key="STDOUT"/> >>> </Routes> >>> <IdlePurgePolicy timeToLive="15" timeUnit="minutes"/> >>> </Routing> >>> </Appenders> >>> First, the init script changes the default route based on the OS. >>> Second, notice that “Routes” has a new Script element and does not have a >>> pattern specified, so the script is determining the key instead of the >>> pattern. >>> Third, the real default route is now “STDOUT” since the actual default >>> Route is only referenced when a UserId is present in the thread context map. >>> >>> What would also be nice is if there was a way to have the returned value be >>> usable as a Lookup value in the default Appender definition, instead of >>> relying on the MDC as the code above does. I should be able to pick >>> something out of the message itself and use that as the key. That should be >>> doable but I am still pondering how I would implement that. >>> >>> Ralph >>> >>> >>> >>>> On Sep 12, 2016, at 6:06 PM, Ralph Goers <ralph.go...@dslextreme.com >>>> <mailto:ralph.go...@dslextreme.com>> wrote: >>>> >>>> I’ll try to describe it better but I’m not sure how good a job I’ll do if >>>> the dots aren’t clicking yet. Also, even though I might say to do it one >>>> way if I was coding I could very well change my mind as I implement it. >>>> That said: >>>> Create an Appender plugin named ScriptSelector or ScriptAppenderSelector. >>>> It needs the following parameters >>>> name, a String PluginAttribute >>>> default, a String PluginAttribute >>>> initScript or startupScript, an AbstractScript PluginElement >>>> script, an AbstractScript PluginElement >>>> appenderList, an AppenderList PluginElement. >>>> As always, the builder (or factory) creates an instance of the >>>> ScriptAppenderSelector. >>>> If there is an init script then the builder (or factory) executes it. >>>> If the returned value is not null then instantiate the Appender with that >>>> name using the configuration in the AppenderList. >>>> Whatever Appender the init script names should become the default Appender. >>>> If no init script is present or the init script returns null use the value >>>> of the default setting as the name of the default Appender to use. >>>> Create the default Appender and save it in the Map of created appenders >>>> wrapped by an AppenderControl. >>>> When the append method is called check for a script setting. >>>> If a script is found, run it. >>>> If it returns a value see if that appender is saved in the AppenderMap. >>>> If it is, call the appender and return. >>>> If it is not, locate the configuration for the appender, create it and add >>>> it to the AppenderMap. Then call it and return. >>>> If it returns null or no script is defined then call the default Appender >>>> and return. >>>> When the stop method is called call the stop method on each of the >>>> Appenders in the AppenderMap. >>>> >>>> Note that signatures for scripts are defined by the components that use >>>> them. In this case both the init script and script return the name of the >>>> appender to execute. >>>> >>>> Ralph >>>> >>>> >>>>> On Sep 12, 2016, at 12:54 PM, Gary Gregory <garydgreg...@gmail.com >>>>> <mailto:garydgreg...@gmail.com>> wrote: >>>>> >>>>> On Sun, Sep 11, 2016 at 12:47 PM, Ralph Goers <ralph.go...@dslextreme.com >>>>> <mailto:ralph.go...@dslextreme.com>> wrote: >>>>> Yes. The Appenders tag inside the ScriptSelector are the Appenders that >>>>> are to be created. But now that I think about it, we can’t use >>>>> “Appenders” for this. If you look at the RoutingAppender you will notice >>>>> that Appenders there are declared under a Route element. The Route plugin >>>>> is defined with deferChildren=true. This means that whatever is >>>>> configured under the Route will not be created during initial >>>>> configuration. Instead the Route keeps a reference to the Node and then >>>>> configures the Appender when it is required. So we would need a new >>>>> plugin to wrap the Appenders that are to be created. >>>>> >>>>> Can you please describe in more detail how this new plug fits in and what >>>>> it does? I can't quite connect the dots with the parallel of the routing >>>>> appender. I'm willing to implement this as I need the feature ASAP. >>>>> >>>>> Gary >>>>> >>>>> >>>>> Ralph >>>>> >>>>>> On Sep 11, 2016, at 11:10 AM, Gary Gregory <garydgreg...@gmail.com >>>>>> <mailto:garydgreg...@gmail.com>> wrote: >>>>>> >>>>>> Are the <Appenders> tags really meant to be nested? >>>>>> >>>>>> Gary >>>>>> >>>>>> On Sat, Sep 10, 2016 at 11:48 AM, Ralph Goers >>>>>> <ralph.go...@dslextreme.com <mailto:ralph.go...@dslextreme.com>> wrote: >>>>>> Oops. I forgot the closing CDATA tag in the script. >>>>>> >>>>>> Ralph >>>>>> >>>>>>> On Sep 10, 2016, at 11:43 AM, Ralph Goers <ralph.go...@dslextreme.com >>>>>>> <mailto:ralph.go...@dslextreme.com>> wrote: >>>>>>> >>>>>>> Interesting. OS/390. I worked on MVS, OS/370, z/OS, etc many moons ago >>>>>>> but haven’t worked on a mainframe since 2001. >>>>>>> >>>>>>> This sort of sounds like you want an Appender Selector, which would be >>>>>>> an Appender that uses a Selector to figure out which Appender to >>>>>>> delegate to. This is a bit like the PatternSelector. I would imagine it >>>>>>> would make sense to implement AppenderSelectors and LayoutSelectors. >>>>>>> You probably would want to dynamically initialize the Appenders much >>>>>>> like the RoutingAppender does. >>>>>>> >>>>>>> Maybe it would look like: >>>>>>> >>>>>>> <Appenders> >>>>>>> <ScriptSelector name=“" default=“”> >>>>>>> <Script language=“groovy”><![CDATA[ >>>>>>> if (System.getProperty”os.name >>>>>>> <http://os.name/>”).contains(“OS/390”)) then { >>>>>>> return “Socket”; >>>>>>> } else { >>>>>>> return “File”; >>>>>>> } >>>>>>> </Script> >>>>>>> <Appenders> >>>>>>> <SocketAppender name=“Socket” …/> >>>>>>> <FileAppender name=“File” …/> >>>>>>> </Appenders> >>>>>>> </ScriptSelector> >>>>>>> </Appenders> >>>>>>> >>>>>>> The thing is that this script would run every time the Selector was >>>>>>> accessed while it sounds like you would only want the script to run >>>>>>> when the Selector is initialized. We could do that too but the script >>>>>>> would need to be declared in a property that would only be used when >>>>>>> the selector is initialized. I would want to support being able to do >>>>>>> both. >>>>>>> >>>>>>> Ralph >>>>>>> >>>>>>>> On Sep 10, 2016, at 11:04 AM, Gary Gregory <garydgreg...@gmail.com >>>>>>>> <mailto:garydgreg...@gmail.com>> wrote: >>>>>>>> >>>>>>>> <Appenders> >>>>>>>> <ScriptTest language="JavaScript"> >>>>>>>> <If>System.getProperty("os.name >>>>>>>> <http://os.name/>").contains("OS/390")</If> >>>>>>>> <True> >>>>>>>> <SocketAppender ...> >>>>>>>> </True> >>>>>>>> <False> >>>>>>>> <FileAppender ...> >>>>>>>> </False> >>>>>>>> </ScriptTest> >>>>>>>> </Appenders> >>>>>>>> >>>>>>>> ? >>>>>>>> >>>>>>>> >>>>>>>> On Sat, Sep 10, 2016 at 10:40 AM, Gary Gregory <garydgreg...@gmail.com >>>>>>>> <mailto:garydgreg...@gmail.com>> wrote: >>>>>>>> OK, I found >>>>>>>> https://logging.apache.org/log4j/2.x/manual/configuration.html#Scripts >>>>>>>> <https://logging.apache.org/log4j/2.x/manual/configuration.html#Scripts> >>>>>>>> and I think I could use either: >>>>>>>> >>>>>>>> - Use composite configurations: One file for OS/390, one for all other >>>>>>>> OSs; or >>>>>>>> - Do it all in one configuration file (that seems simpler) >>>>>>>> >>>>>>>> It seems like there are some pieces missing to do what I want >>>>>>>> conveniently. >>>>>>>> >>>>>>>> Should I define all appenders in <Appenders> and later use a script to >>>>>>>> only add the one(s) I want in the <Root> section? >>>>>>>> >>>>>>>> Or, should the <Appenders> section itself be scripted to only add the >>>>>>>> appenders I want? >>>>>>>> >>>>>>>> Since I expect the OS/390 appender will likely blow up running on a >>>>>>>> different OK I do not want to create it unless I know it can run OK. >>>>>>>> >>>>>>>> I guess then I have a conditional section in both the Appenders and in >>>>>>>> the Root section so that when I say <AppenderRef =...> we do not go >>>>>>>> look for an appender that is not defined. >>>>>>>> >>>>>>>> Thoughts? >>>>>>>> >>>>>>>> A narrow solution would be to add an "os" attribute to all appenders >>>>>>>> but that seems lame. os="OS/390" and os="!OS/390" means also knowing >>>>>>>> about "not", yikes. >>>>>>>> >>>>>>>> Gary >>>>>>>> >>>>>>>> On Sat, Sep 10, 2016 at 10:05 AM, Gary Gregory <garydgreg...@gmail.com >>>>>>>> <mailto:garydgreg...@gmail.com>> wrote: >>>>>>>> Hi, >>>>>>>> >>>>>>>> I can't seem to find on our site the scripting support that was >>>>>>>> recently added (or is that only in master?). >>>>>>>> >>>>>>>> What I need to do is only add a specific appender when running on a >>>>>>>> specific OS (USS on OS/390 if you must know). Then only add a >>>>>>>> different appender when not running on that OS. >>>>>>>> >>>>>>>> I'd rather not have to hard-code this and make thing more complicated. >>>>>>>> >>>>>>>> Thoughts? >>>>>>>> >>>>>>>> Gary >>>>>>>> >>>>>>>> -- >>>>>>>> E-Mail: garydgreg...@gmail.com <mailto:garydgreg...@gmail.com> | >>>>>>>> ggreg...@apache.org <mailto:ggreg...@apache.org> >>>>>>>> Java Persistence with Hibernate, Second Edition >>>>>>>> <http://www.manning.com/bauer3/> >>>>>>>> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >>>>>>>> Spring Batch in Action <http://www.manning.com/templier/> >>>>>>>> Blog: http://garygregory.wordpress.com >>>>>>>> <http://garygregory.wordpress.com/> >>>>>>>> Home: http://garygregory.com/ <http://garygregory.com/> >>>>>>>> Tweet! http://twitter.com/GaryGregory <http://twitter.com/GaryGregory> >>>>>>>> >>>>>>>> >>>>>>>> -- >>>>>>>> E-Mail: garydgreg...@gmail.com <mailto:garydgreg...@gmail.com> | >>>>>>>> ggreg...@apache.org <mailto:ggreg...@apache.org> >>>>>>>> Java Persistence with Hibernate, Second Edition >>>>>>>> <http://www.manning.com/bauer3/> >>>>>>>> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >>>>>>>> Spring Batch in Action <http://www.manning.com/templier/> >>>>>>>> Blog: http://garygregory.wordpress.com >>>>>>>> <http://garygregory.wordpress.com/> >>>>>>>> Home: http://garygregory.com/ <http://garygregory.com/> >>>>>>>> Tweet! http://twitter.com/GaryGregory <http://twitter.com/GaryGregory> >>>>>>>> >>>>>>>> >>>>>>>> -- >>>>>>>> E-Mail: garydgreg...@gmail.com <mailto:garydgreg...@gmail.com> | >>>>>>>> ggreg...@apache.org <mailto:ggreg...@apache.org> >>>>>>>> Java Persistence with Hibernate, Second Edition >>>>>>>> <http://www.manning.com/bauer3/> >>>>>>>> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >>>>>>>> Spring Batch in Action <http://www.manning.com/templier/> >>>>>>>> Blog: http://garygregory.wordpress.com >>>>>>>> <http://garygregory.wordpress.com/> >>>>>>>> Home: http://garygregory.com/ <http://garygregory.com/> >>>>>>>> Tweet! http://twitter.com/GaryGregory <http://twitter.com/GaryGregory> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> -- >>>>>> E-Mail: garydgreg...@gmail.com <mailto:garydgreg...@gmail.com> | >>>>>> ggreg...@apache.org <mailto:ggreg...@apache.org> >>>>>> Java Persistence with Hibernate, Second Edition >>>>>> <http://www.manning.com/bauer3/> >>>>>> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >>>>>> Spring Batch in Action <http://www.manning.com/templier/> >>>>>> Blog: http://garygregory.wordpress.com >>>>>> <http://garygregory.wordpress.com/> >>>>>> Home: http://garygregory.com/ <http://garygregory.com/> >>>>>> Tweet! http://twitter.com/GaryGregory <http://twitter.com/GaryGregory> >>>>> >>>>> >>>>> >>>>> -- >>>>> E-Mail: garydgreg...@gmail.com <mailto:garydgreg...@gmail.com> | >>>>> ggreg...@apache.org <mailto:ggreg...@apache.org> >>>>> Java Persistence with Hibernate, Second Edition >>>>> <http://www.manning.com/bauer3/> >>>>> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >>>>> Spring Batch in Action <http://www.manning.com/templier/> >>>>> Blog: http://garygregory.wordpress.com >>>>> <http://garygregory.wordpress.com/> >>>>> Home: http://garygregory.com/ <http://garygregory.com/> >>>>> Tweet! http://twitter.com/GaryGregory <http://twitter.com/GaryGregory> >>> >>> >>> >>> >>> -- >>> E-Mail: garydgreg...@gmail.com <mailto:garydgreg...@gmail.com> | >>> ggreg...@apache.org <mailto:ggreg...@apache.org> >>> Java Persistence with Hibernate, Second Edition >>> <http://www.manning.com/bauer3/> >>> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >>> Spring Batch in Action <http://www.manning.com/templier/> >>> Blog: http://garygregory.wordpress.com <http://garygregory.wordpress.com/> >>> Home: http://garygregory.com/ <http://garygregory.com/> >>> Tweet! http://twitter.com/GaryGregory <http://twitter.com/GaryGregory> >>> >>> >>> -- >>> E-Mail: garydgreg...@gmail.com <mailto:garydgreg...@gmail.com> | >>> ggreg...@apache.org <mailto:ggreg...@apache.org> >>> Java Persistence with Hibernate, Second Edition >>> <http://www.manning.com/bauer3/> >>> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >>> Spring Batch in Action <http://www.manning.com/templier/> >>> Blog: http://garygregory.wordpress.com <http://garygregory.wordpress.com/> >>> Home: http://garygregory.com/ <http://garygregory.com/> >>> Tweet! http://twitter.com/GaryGregory <http://twitter.com/GaryGregory> >> >> >> >> -- >> E-Mail: garydgreg...@gmail.com <mailto:garydgreg...@gmail.com> | >> ggreg...@apache.org <mailto:ggreg...@apache.org> >> Java Persistence with Hibernate, Second Edition >> <http://www.manning.com/bauer3/> >> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >> Spring Batch in Action <http://www.manning.com/templier/> >> Blog: http://garygregory.wordpress.com <http://garygregory.wordpress.com/> >> Home: http://garygregory.com/ <http://garygregory.com/> >> Tweet! http://twitter.com/GaryGregory <http://twitter.com/GaryGregory> >> >> >> -- >> E-Mail: garydgreg...@gmail.com <mailto:garydgreg...@gmail.com> | >> ggreg...@apache.org <mailto:ggreg...@apache.org> >> Java Persistence with Hibernate, Second Edition >> <http://www.manning.com/bauer3/> >> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >> Spring Batch in Action <http://www.manning.com/templier/> >> Blog: http://garygregory.wordpress.com <http://garygregory.wordpress.com/> >> Home: http://garygregory.com/ <http://garygregory.com/> >> Tweet! http://twitter.com/GaryGregory <http://twitter.com/GaryGregory> > > > > -- > E-Mail: garydgreg...@gmail.com <mailto:garydgreg...@gmail.com> | > ggreg...@apache.org <mailto:ggreg...@apache.org> > Java Persistence with Hibernate, Second Edition > <http://www.manning.com/bauer3/> > JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> > Spring Batch in Action <http://www.manning.com/templier/> > Blog: http://garygregory.wordpress.com <http://garygregory.wordpress.com/> > Home: http://garygregory.com/ <http://garygregory.com/> > Tweet! http://twitter.com/GaryGregory <http://twitter.com/GaryGregory> > > > -- > Matt Sicker <boa...@gmail.com <mailto:boa...@gmail.com>>