You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@freemarker.apache.org by Daniel Dekany <dd...@freemail.hu> on 2017/03/01 19:09:10 UTC

[FM3] Opinions about the package structure?

You can see the current FM3 package structure here:
http://freemarker.org/builds/fm3/api/index.html
https://github.com/apache/incubator-freemarker/tree/3/src/main/java/org/apache/freemarker

Some explanation follows.

As it was discussed earlier, FM2 freemarker.core and FM2
freemarker.template were united under org.apache.freemarker.core, and
then to decrease the size of the resulting package, I have moved out
the aspects that don't depend on core internals into their own
subpackages (model, templateresolver, outputformat, etc.). So we have
ended up with a org.apache.freemarker.core that is actually
substantially smaller than FM2 freemarker.core was, also it has
smaller and more focused public API than FM2 freemarker.template has.

The name directly after org.apache.freemarker denotes the module. So
you can see 3 modules from the current package structure: core, dom,
and servlet (which will be freemarker-core.jar, freemarker-dom.jar,
etc., after we have converted to multi module project, but we haven't
yet). There will be more actually, like "test" for common JUnit test
utilities (not a published API), and core-java8 for Java 8 support,
and who know what else.

Any opinions/insights?

-- 
Thanks,
 Daniel Dekany


Re: [FM3] Opinions about the package structure?

Posted by Woonsan Ko <wo...@apache.org>.
On Fri, Mar 3, 2017 at 7:16 PM, Daniel Dekany <dd...@freemail.hu> wrote:
> Saturday, March 4, 2017, 12:15:30 AM, Woonsan Ko wrote:
>
>> On Fri, Mar 3, 2017 at 12:54 AM, Daniel Dekany <dd...@freemail.hu> wrote:
> [snip]
>>> or with Properties syntax:
>>>
>>>   objectWrapper = DefaultObjectWrapper( \
>>>      3.0.0, \
>>>      typeHandlers=[ org.apache.freemarker.dom.DomTypeHandler ])
>>>
>>> That's how users could add special treatment for their
>>> application/domain specific types too (and if we switch to MOP-s, then
>>> just replace objectWrapper with mopImplementationProvider or whatever
>>> it will be called). So we are consistent with the idea that
>>> freemarker-core only contains (and is aware of!) things that are
>>> related to the core (minimal) functionality. But then, what are the
>>> practical implications of the users? They get a freemarker-core.jar
>>> that's a few kilobytes leaner, but in exchange they have to go through
>>> the above extra configuring if they want to use the DOM wrapping. I'm
>>> not sure if it worths it... mostly because I don't now how many
>>> percentage of the users are using DOM wrapping. Certainly it's a
>>> minority, a few percentages maybe, but it's just a wild guess.
>>
>> Could we let the core scan classpath resources from all the jar
>> files
>
> (The SPI feature of the Java platform does that, to be more precise.)
>
>> (e.g, classpath:META-INF/org/apache/freemarker/core/TypeHandler) and
>> register those handlers automatically? Then users will simply need
>> to add extra dependencies such as dom and they can use it without
>> any custom registration.
>
> I don't think that activating a FreeMarker extension merely based on
> its presence in the class path is acceptable, for most kind of
> extensions. Real world projects easily use 100+ dependencies, most of
> them being transitive (so you have never asked for them explicitly),
> so practically, whether a particular jar (like freemarker-dom) appears
> in the class path is out of your control.

Right.

>
> The valid cases for automatically activating extensions are when
> without the extension you simply ran into an error. Like FreeMarker
> wants to wrap a java.time class, but freemarker-core-java-8 is
> missing, that's an error, so we should auto-discover[*] that extension.
> But with freemarker-dom, you can wrap DOM nodes without it, only they
> will look different (like regular beans). So this extensions doesn't
> prevent an error, but alters behavior, and thus I believe it must be
> activated explicitly, independently for each Configuration.

I see.

>
> BTW, when thinking about configuring FreeMarker, it should always be
> considered that multiple independently developed components might use
> FreeMarker internally. That's one reason why modifiable static fields
> are wrong, and adding something to the classpath is very much like
> that if it triggers some sort of auto-configuring.
>
> *: Auto discovering of freemarker-core-java-8 is actually a special
>    case, because freemarker-core already knows about it and looks for
>    it itself (pull VS push basically). Because, it has to know that
>    seeing a java.time object but not freemarker-core-java-8 is an
>    error.
>
>> I've seen something similar in Apache Camel. For example, when a
>> custom Camel component is provided by a new jar file in the classpath,
>> Camel engine automatically detects all the available component by
>> scanning a dedicated classpath resource path [1], or it even scans all
>> the type converters from provided jar files at runtime once [2].
>
> I hope these are the error avoiding use cases though (as with
> freemarker-core-java-8).

Totally makes sense. Thanks a lot for sharing the full insights.

Cheers,

Woonsan

>
>> If we can apply the same technique, then I guess users wouldn't have
>> any difficulty. They can simply drop an optional jar files.
>>
>> Regards,
>>
>> Woonsan
>>
>> [1] http://camel.apache.org/how-do-i-add-a-component.html
>> [2]
>> http://camel.apache.org/type-converter.html#TypeConverter-DiscoveringTypeConverters
>>
>>
>>>
>>> --
>>> Thanks,
>>>  Daniel Dekany
>>>
>>
>
> --
> Thanks,
>  Daniel Dekany
>

Re: [FM3] Opinions about the package structure?

Posted by Daniel Dekany <dd...@apache.org>.
Saturday, March 4, 2017, 2:16:53 AM, Daniel Dekany wrote:

[snip]
> I don't think that activating a FreeMarker extension merely based on
> its presence in the class path is acceptable, for most kind of
> extensions. Real world projects easily use 100+ dependencies, most of
> them being transitive (so you have never asked for them explicitly),
> so practically, whether a particular jar (like freemarker-dom) appears
> in the class path is out of your control.
>
> The valid cases for automatically activating extensions are when
> without the extension you simply ran into an error. Like FreeMarker
> wants to wrap a java.time class, but freemarker-core-java-8 is
> missing, that's an error, so we should auto-discover[*] that extension.
> But with freemarker-dom, you can wrap DOM nodes without it, only they
> will look different (like regular beans). So this extensions doesn't
> prevent an error, but alters behavior, and thus I believe it must be
> activated explicitly, independently for each Configuration.
[snip]
> *: Auto discovering of freemarker-core-java-8 is actually a special
>    case, because freemarker-core already knows about it and looks for
>    it itself (pull VS push basically). Because, it has to know that
>    seeing a java.time object but not freemarker-core-java-8 is an
>    error.

I'm fiddling with the modularization (using Gradle 3.5), and avoiding
circular dependencies between freemarker-core-java-8 and
freemarker-core leads to quite monstrous solution.

The way it works (in FM2) is that there's a freemarker.core._Java8
interface compiled with Java 7, which is implemented by
freemarker.core._Java8Impl that is compiled with Java 8. That works
for Ant and on runtime, but no IDE supports having files with
different Java versions inside the same "project" (module in
IntelliJ), and the same looks like a problem (but at least a big hack
to achieve) for Gradle as well. So the idea was that we bite the
bullet and add a freemarker-core-java-8 module (a separate jar), which
we build with Java 8, and freemarker-core, which we build with Java 7,
depends on that. Nice and clean. Except, in which module do you have
the freemarker.core._Java8 interface then? If in freemarker-core, you
have a circular dependency (because freemarker-core-java-8 implements
the _Java8 interface, so it must depend on freemarker-core). If in
freemarker-core-java8, then the class will be compiled to Java 8 class
format so the JVM will fail to load the interface on Java 7. So in
theory we have to add a third module (jar),
freemarker-core-java-adapte, which contains the _Java8 interface (and
later _Java9, etc.) compiled with Java 7. Then freemarker-core-java-8
depends on that instead of freemarker-core, and freemarker-core just
depends on freemarker-core-java-8 as planned (and gets the
freemarker-core-java-adapter dependency transitively). Yeah... add to
this that at the moment all we have in _Java8 is `boolean
isDefaultMethod(Method)`. It will certainly expand in the future, but
still. Madness.

But it gets funnier. Where do I put the Java 8 unit tests? You would
think that the right place is freemarker-core-java8. But nope, because
the tests aren't much about testing _Java8Impl, but the behavior of
FreeMarker itself when it meets Java 8 default methods and such. So
obviously the tests depend freemarker-core, a circular module
dependency again (Gradle might understands that it's not really that,
as its just a dependency of the tests, but Eclipse certainly doesn't),
and anyway you are really testing freemarker-core. So let's put it
there. But you have to compile and run these tests on Java 8, so you
again get a module that uses multiple JDK-s, even if the split is at
least between the "main" and test part (though ideally you run all the
other tests on Java 7). So clearly, you need a separate
freemarker-core-java8-test module... 4 modules so far (to call
isDefaultMethod for now).

I'm very tempted to go for a reflection hack inside freemarker-core,
but that won't "scale" well if more complex stuff, like Java 8 time
API support will appear, and who knows what later Java versions will
bring.

Any ideas?

-- 
Thanks,
 Daniel Dekany


Re: [FM3] Opinions about the package structure?

Posted by Daniel Dekany <dd...@freemail.hu>.
Saturday, March 4, 2017, 12:15:30 AM, Woonsan Ko wrote:

> On Fri, Mar 3, 2017 at 12:54 AM, Daniel Dekany <dd...@freemail.hu> wrote:
[snip]
>> or with Properties syntax:
>>
>>   objectWrapper = DefaultObjectWrapper( \
>>      3.0.0, \
>>      typeHandlers=[ org.apache.freemarker.dom.DomTypeHandler ])
>>
>> That's how users could add special treatment for their
>> application/domain specific types too (and if we switch to MOP-s, then
>> just replace objectWrapper with mopImplementationProvider or whatever
>> it will be called). So we are consistent with the idea that
>> freemarker-core only contains (and is aware of!) things that are
>> related to the core (minimal) functionality. But then, what are the
>> practical implications of the users? They get a freemarker-core.jar
>> that's a few kilobytes leaner, but in exchange they have to go through
>> the above extra configuring if they want to use the DOM wrapping. I'm
>> not sure if it worths it... mostly because I don't now how many
>> percentage of the users are using DOM wrapping. Certainly it's a
>> minority, a few percentages maybe, but it's just a wild guess.
>
> Could we let the core scan classpath resources from all the jar
> files

(The SPI feature of the Java platform does that, to be more precise.)

> (e.g, classpath:META-INF/org/apache/freemarker/core/TypeHandler) and
> register those handlers automatically? Then users will simply need
> to add extra dependencies such as dom and they can use it without
> any custom registration.

I don't think that activating a FreeMarker extension merely based on
its presence in the class path is acceptable, for most kind of
extensions. Real world projects easily use 100+ dependencies, most of
them being transitive (so you have never asked for them explicitly),
so practically, whether a particular jar (like freemarker-dom) appears
in the class path is out of your control.

The valid cases for automatically activating extensions are when
without the extension you simply ran into an error. Like FreeMarker
wants to wrap a java.time class, but freemarker-core-java-8 is
missing, that's an error, so we should auto-discover[*] that extension.
But with freemarker-dom, you can wrap DOM nodes without it, only they
will look different (like regular beans). So this extensions doesn't
prevent an error, but alters behavior, and thus I believe it must be
activated explicitly, independently for each Configuration.

BTW, when thinking about configuring FreeMarker, it should always be
considered that multiple independently developed components might use
FreeMarker internally. That's one reason why modifiable static fields
are wrong, and adding something to the classpath is very much like
that if it triggers some sort of auto-configuring.

*: Auto discovering of freemarker-core-java-8 is actually a special
   case, because freemarker-core already knows about it and looks for
   it itself (pull VS push basically). Because, it has to know that
   seeing a java.time object but not freemarker-core-java-8 is an
   error.

> I've seen something similar in Apache Camel. For example, when a
> custom Camel component is provided by a new jar file in the classpath,
> Camel engine automatically detects all the available component by
> scanning a dedicated classpath resource path [1], or it even scans all
> the type converters from provided jar files at runtime once [2].

I hope these are the error avoiding use cases though (as with
freemarker-core-java-8).

> If we can apply the same technique, then I guess users wouldn't have
> any difficulty. They can simply drop an optional jar files.
>
> Regards,
>
> Woonsan
>
> [1] http://camel.apache.org/how-do-i-add-a-component.html
> [2]
> http://camel.apache.org/type-converter.html#TypeConverter-DiscoveringTypeConverters
>
>
>>
>> --
>> Thanks,
>>  Daniel Dekany
>>
>

-- 
Thanks,
 Daniel Dekany


Re: [FM3] Opinions about the package structure?

Posted by Woonsan Ko <wo...@apache.org>.
On Fri, Mar 3, 2017 at 12:54 AM, Daniel Dekany <dd...@freemail.hu> wrote:
> Thursday, March 2, 2017, 6:41:35 PM, Woonsan Ko wrote:
>
>> On Wed, Mar 1, 2017 at 2:09 PM, Daniel Dekany <dd...@freemail.hu> wrote:
>>> You can see the current FM3 package structure here:
>>> http://freemarker.org/builds/fm3/api/index.html
>>> https://github.com/apache/incubator-freemarker/tree/3/src/main/java/org/apache/freemarker
>>>
>>> Some explanation follows.
>>>
>>> As it was discussed earlier, FM2 freemarker.core and FM2
>>> freemarker.template were united under org.apache.freemarker.core, and
>>> then to decrease the size of the resulting package, I have moved out
>>> the aspects that don't depend on core internals into their own
>>> subpackages (model, templateresolver, outputformat, etc.). So we have
>>> ended up with a org.apache.freemarker.core that is actually
>>> substantially smaller than FM2 freemarker.core was, also it has
>>> smaller and more focused public API than FM2 freemarker.template has.
>>>
>>> The name directly after org.apache.freemarker denotes the module. So
>>> you can see 3 modules from the current package structure: core, dom,
>>> and servlet (which will be freemarker-core.jar, freemarker-dom.jar,
>>> etc., after we have converted to multi module project, but we haven't
>>> yet). There will be more actually, like "test" for common JUnit test
>>> utilities (not a published API), and core-java8 for Java 8 support,
>>> and who know what else.
>>>
>>> Any opinions/insights?
>>
>> Looks good to me! Thanks!
>
> I wonder if freemarker-dom should be separate, or part of
> freemarker-core under o.a.f.core.model.impl.dom. If freemarker-dom is
> separate, then if the user wants to use it, they will have to plug it
> into the DefaultObjectWrapper manually. Something like:

I would prefer separating dom in a separate jar module from the core.

>
>   new Configuration.Builder(Configuration.VERSION_3_0_0)
>           ...
>           .objectWrapperBuilder(
>                   new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0)
>                           .typeHandlers(DomTypeHandler.INSTANCE))
>           ...
>
> or with Properties syntax:
>
>   objectWrapper = DefaultObjectWrapper( \
>      3.0.0, \
>      typeHandlers=[ org.apache.freemarker.dom.DomTypeHandler ])
>
> That's how users could add special treatment for their
> application/domain specific types too (and if we switch to MOP-s, then
> just replace objectWrapper with mopImplementationProvider or whatever
> it will be called). So we are consistent with the idea that
> freemarker-core only contains (and is aware of!) things that are
> related to the core (minimal) functionality. But then, what are the
> practical implications of the users? They get a freemarker-core.jar
> that's a few kilobytes leaner, but in exchange they have to go through
> the above extra configuring if they want to use the DOM wrapping. I'm
> not sure if it worths it... mostly because I don't now how many
> percentage of the users are using DOM wrapping. Certainly it's a
> minority, a few percentages maybe, but it's just a wild guess.

Could we let the core scan classpath resources from all the jar files
(e.g, classpath:META-INF/org/apache/freemarker/core/TypeHandler) and
register those handlers automatically?
Then users will simply need to add extra dependencies such as dom and
they can use it without any custom registration.
I've seen something similar in Apache Camel. For example, when a
custom Camel component is provided by a new jar file in the classpath,
Camel engine automatically detects all the available component by
scanning a dedicated classpath resource path [1], or it even scans all
the type converters from provided jar files at runtime once [2].
If we can apply the same technique, then I guess users wouldn't have
any difficulty. They can simply drop an optional jar files.

Regards,

Woonsan

[1] http://camel.apache.org/how-do-i-add-a-component.html
[2] http://camel.apache.org/type-converter.html#TypeConverter-DiscoveringTypeConverters


>
> --
> Thanks,
>  Daniel Dekany
>

Re: [FM3] Opinions about the package structure?

Posted by Daniel Dekany <dd...@freemail.hu>.
Thursday, March 2, 2017, 6:41:35 PM, Woonsan Ko wrote:

> On Wed, Mar 1, 2017 at 2:09 PM, Daniel Dekany <dd...@freemail.hu> wrote:
>> You can see the current FM3 package structure here:
>> http://freemarker.org/builds/fm3/api/index.html
>> https://github.com/apache/incubator-freemarker/tree/3/src/main/java/org/apache/freemarker
>>
>> Some explanation follows.
>>
>> As it was discussed earlier, FM2 freemarker.core and FM2
>> freemarker.template were united under org.apache.freemarker.core, and
>> then to decrease the size of the resulting package, I have moved out
>> the aspects that don't depend on core internals into their own
>> subpackages (model, templateresolver, outputformat, etc.). So we have
>> ended up with a org.apache.freemarker.core that is actually
>> substantially smaller than FM2 freemarker.core was, also it has
>> smaller and more focused public API than FM2 freemarker.template has.
>>
>> The name directly after org.apache.freemarker denotes the module. So
>> you can see 3 modules from the current package structure: core, dom,
>> and servlet (which will be freemarker-core.jar, freemarker-dom.jar,
>> etc., after we have converted to multi module project, but we haven't
>> yet). There will be more actually, like "test" for common JUnit test
>> utilities (not a published API), and core-java8 for Java 8 support,
>> and who know what else.
>>
>> Any opinions/insights?
>
> Looks good to me! Thanks!

I wonder if freemarker-dom should be separate, or part of
freemarker-core under o.a.f.core.model.impl.dom. If freemarker-dom is
separate, then if the user wants to use it, they will have to plug it
into the DefaultObjectWrapper manually. Something like:

  new Configuration.Builder(Configuration.VERSION_3_0_0)
          ...
          .objectWrapperBuilder(
                  new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0)
                          .typeHandlers(DomTypeHandler.INSTANCE))
          ...

or with Properties syntax:

  objectWrapper = DefaultObjectWrapper( \
     3.0.0, \
     typeHandlers=[ org.apache.freemarker.dom.DomTypeHandler ])

That's how users could add special treatment for their
application/domain specific types too (and if we switch to MOP-s, then
just replace objectWrapper with mopImplementationProvider or whatever
it will be called). So we are consistent with the idea that
freemarker-core only contains (and is aware of!) things that are
related to the core (minimal) functionality. But then, what are the
practical implications of the users? They get a freemarker-core.jar
that's a few kilobytes leaner, but in exchange they have to go through
the above extra configuring if they want to use the DOM wrapping. I'm
not sure if it worths it... mostly because I don't now how many
percentage of the users are using DOM wrapping. Certainly it's a
minority, a few percentages maybe, but it's just a wild guess.

-- 
Thanks,
 Daniel Dekany


Re: [FM3] Opinions about the package structure?

Posted by Woonsan Ko <wo...@apache.org>.
On Wed, Mar 1, 2017 at 2:09 PM, Daniel Dekany <dd...@freemail.hu> wrote:
> You can see the current FM3 package structure here:
> http://freemarker.org/builds/fm3/api/index.html
> https://github.com/apache/incubator-freemarker/tree/3/src/main/java/org/apache/freemarker
>
> Some explanation follows.
>
> As it was discussed earlier, FM2 freemarker.core and FM2
> freemarker.template were united under org.apache.freemarker.core, and
> then to decrease the size of the resulting package, I have moved out
> the aspects that don't depend on core internals into their own
> subpackages (model, templateresolver, outputformat, etc.). So we have
> ended up with a org.apache.freemarker.core that is actually
> substantially smaller than FM2 freemarker.core was, also it has
> smaller and more focused public API than FM2 freemarker.template has.
>
> The name directly after org.apache.freemarker denotes the module. So
> you can see 3 modules from the current package structure: core, dom,
> and servlet (which will be freemarker-core.jar, freemarker-dom.jar,
> etc., after we have converted to multi module project, but we haven't
> yet). There will be more actually, like "test" for common JUnit test
> utilities (not a published API), and core-java8 for Java 8 support,
> and who know what else.
>
> Any opinions/insights?

Looks good to me! Thanks!

Woonsan

>
> --
> Thanks,
>  Daniel Dekany
>