Spring Framework
  1. Spring Framework
  2. SPR-8728

Support not (!) operator for profile selection

    Details

    • Type: Improvement Improvement
    • Status: Closed
    • Priority: Minor Minor
    • Resolution: Complete
    • Affects Version/s: 3.1 M2
    • Fix Version/s: 3.2 M1
    • Component/s: Core
    • Labels:
    • Last commented by a User:
      false

      Description

      It would be really helpful if the profile attribute could take names of profiles that all has to be active.

      For instance:

      <beans profile="production,datacenter_us">
          ...
      </beans>
      <beans profile="production,datacenter_eu">
          ...
      </beans>
      

      As for syntax there would need to be a notation that can express both OR and AND, possibly using | and ,

      Like:

      <beans profile="staging|production">
          ...
      </beans>
      

      Even better would be if the syntax was in line with other expressions such as SpEL.

        Issue Links

          Activity

          Hide
          Chris Beams added a comment - - edited

          Hi Tobias,

          I've been expecting this request

          For the uninitiated, the semantics of <beans profile="..."/> are such that comma-delimited values signify boolean OR, e.g. for <beans profile="x,y">, individual <bean> elements declared within will be registered if profile x or profile y are currently active.

          To re-iterate your request, you would like to introduce additional boolean operations – particularly AND – to indicate that individual <bean> elements declared within will be registered only if profile x and profile y are both currently active.

          Let me note at this point, that OR logic was selected as the only available operation and mapped to the comma (',') delimiter because it is the most reasonable default, i.e. what most users would expect in most situations, reads well, and also provides the most flexibility. You can effectively achieve boolean AND logic simply by nesting <beans> elements, e.g.:

          <beans profile="x">
              <beans profile="y">
                  <bean id="beanOnlyRegisteredIfXandYActive" ... />
              </beans>
          </beans>
          

          With that said, we understand that this is not an ideal approach if indeed AND logic ends up being commonplace for bean definition profile users. Therefore, we're open to implementing something here, but I would like to see additional votes and concrete use cases before doing so. In the meantime, I hope that the simple alternative above can meet your needs more effectively. Please feel free to rally support for this issue. We'd like to hear from other users.

          Also note that any such change must also be reflected in the @Profile annotation. This is somewhat more tricky given that @Profile's value attribute accepts a proper String array rather than a single comma-delimited string. For example, @Profile({"x", "y"}) currently indicates that the annotated component should be registered if profile x or profile y are currently active. Were we to introduce additional boolean operations, this would need to be reflected as an additional attribute to the annotation, e.g.: @Profile(value={"x", "y"}, operation=AND); or optionally this could be supported by parsing individual value strings in the same fashion as the <beans profile="..."> attribute, e.g. @Profile("x&&y"), however this creates some ambiguity about the semantics of the following: @profile({"x&&y", "a||b"}). Does the latter mean either "x and y" or "a or b"? Or does it mean "x and y" and "a or b"? All of this would need to be defined and documented, without losing the initial simplicity of the comma-delimited OR approach.

          As you can see, this is a seemingly simple request that can quickly spiral into complexity. For that reason, and as mentioned above, I'd like to get more feedback before heading in any one direction.

          Cheers

          Chris

          Show
          Chris Beams added a comment - - edited Hi Tobias, I've been expecting this request For the uninitiated, the semantics of <beans profile="..."/> are such that comma-delimited values signify boolean OR, e.g. for <beans profile="x,y"> , individual <bean> elements declared within will be registered if profile x or profile y are currently active. To re-iterate your request, you would like to introduce additional boolean operations – particularly AND – to indicate that individual <bean> elements declared within will be registered only if profile x and profile y are both currently active. Let me note at this point, that OR logic was selected as the only available operation and mapped to the comma (',') delimiter because it is the most reasonable default, i.e. what most users would expect in most situations, reads well, and also provides the most flexibility. You can effectively achieve boolean AND logic simply by nesting <beans> elements, e.g.: <beans profile= "x" > <beans profile= "y" > <bean id= "beanOnlyRegisteredIfXandYActive" ... /> </beans> </beans> With that said, we understand that this is not an ideal approach if indeed AND logic ends up being commonplace for bean definition profile users. Therefore, we're open to implementing something here, but I would like to see additional votes and concrete use cases before doing so. In the meantime, I hope that the simple alternative above can meet your needs more effectively. Please feel free to rally support for this issue. We'd like to hear from other users. Also note that any such change must also be reflected in the @Profile annotation. This is somewhat more tricky given that @Profile 's value attribute accepts a proper String array rather than a single comma-delimited string. For example, @Profile({"x", "y"}) currently indicates that the annotated component should be registered if profile x or profile y are currently active. Were we to introduce additional boolean operations, this would need to be reflected as an additional attribute to the annotation, e.g.: @Profile(value={"x", "y"}, operation=AND) ; or optionally this could be supported by parsing individual value strings in the same fashion as the <beans profile="..."> attribute, e.g. @Profile("x&&y") , however this creates some ambiguity about the semantics of the following: @profile({"x&&y", "a||b"}) . Does the latter mean either "x and y" or "a or b"? Or does it mean "x and y" and "a or b"? All of this would need to be defined and documented, without losing the initial simplicity of the comma-delimited OR approach. As you can see, this is a seemingly simple request that can quickly spiral into complexity. For that reason, and as mentioned above, I'd like to get more feedback before heading in any one direction. Cheers Chris
          Hide
          Erwin Vervaet added a comment - - edited

          Next to AND logic, NOT would also be very useful.
          I just spend a few days introducing bean definition profiles in a large Spring based application. In quite a few places I had just two variants for a particular set of bean definitions: one that should normally be used, and one that should only be used in special situations. Right now your only option is using two profiles:

          <beans profile="normal">...</beans>
          <beans profile="special">...</beans>

          The downside here is that you need to explicitly activate the 'normal' profile, which can be a bit of a pain. IMHO, it would be more elegant to be able to write something like this:

          <beans profile="!special">...</beans>
          <beans profile="special">...</beans>

          If you find yourself in a special situation it makes sense that you need to activate the 'special' profile.

          Show
          Erwin Vervaet added a comment - - edited Next to AND logic, NOT would also be very useful. I just spend a few days introducing bean definition profiles in a large Spring based application. In quite a few places I had just two variants for a particular set of bean definitions: one that should normally be used, and one that should only be used in special situations. Right now your only option is using two profiles: <beans profile="normal">...</beans> <beans profile="special">...</beans> The downside here is that you need to explicitly activate the 'normal' profile, which can be a bit of a pain. IMHO, it would be more elegant to be able to write something like this: <beans profile="!special">...</beans> <beans profile="special">...</beans> If you find yourself in a special situation it makes sense that you need to activate the 'special' profile.
          Show
          Chris Beams added a comment - Hi Erwin, For this particular use case, could the default profile meet your needs? http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/core/env/Environment.html#getDefaultProfiles( ) You may even rename the default profile to "normal" if you wish through the spring.profiles.default property or ConfigurableEnvironment#setDefaultProfiles http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/core/env/AbstractEnvironment.html#DEFAULT_PROFILES_PROPERTY_NAME http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/core/env/ConfigurableEnvironment.html#setDefaultProfiles(java.lang.String ...)
          Hide
          Erwin Vervaet added a comment - - edited

          Hey Chris,

          I don't think the default profiles would cover my case. The problem is that we actually have a large number of bean definition profiles (a least 20) and the "just two variants" remark only applies to a few of those. For other cases I need to be explicit and select a particular variant. In other words: I have explicitly activated profiles (Environment.getActiveProfiles()), and as a result the default profiles are no longer considered (AbstractEnvironment.acceptsProfiles()).

          Show
          Erwin Vervaet added a comment - - edited Hey Chris, I don't think the default profiles would cover my case. The problem is that we actually have a large number of bean definition profiles (a least 20) and the "just two variants" remark only applies to a few of those. For other cases I need to be explicit and select a particular variant. In other words: I have explicitly activated profiles (Environment.getActiveProfiles()), and as a result the default profiles are no longer considered (AbstractEnvironment.acceptsProfiles()).
          Hide
          Peter Wilkinson added a comment -

          I'm with Erwin. I have several components that can run with a 'normal' or 'special' profile and I want to be able to switch between modes on each component individually. At the moment this requires me setting all profiles explicitly.

          A not operator as Erwin suggested would be very helpful.

          Show
          Peter Wilkinson added a comment - I'm with Erwin. I have several components that can run with a 'normal' or 'special' profile and I want to be able to switch between modes on each component individually. At the moment this requires me setting all profiles explicitly. A not operator as Erwin suggested would be very helpful.
          Hide
          Chris Beams added a comment -

          ok – it certainly does look like there's demand for this change. I'm moving the fixVersion to 3.2 M1 so that I can at least take a stab at a NOT operator. I'll also look at the feasibility of full SpEL expression support, but at this point I would favor as simple as possible a set of changes for the reasons I initially listed above.

          One thing that I'd appreciate feedback on:

          The current semantics of comma-delimited profile strings is that the comma represents OR logic, e.g.:

          <bean profile="p1,p2" ... />
          

          indicates that this bean should be registered if p1 is active OR p2 is active.

          Introducing a simple NOT (!) operator,

          <bean profile="p1,!p2" ... />
          

          would by extension indicate that this bean should be registered if p1 is active OR p2 is NOT active

          This syntax would also allow for strange loopholes like the following:

          <bean profile="p1,!p1" ... />
          

          in which case the given bean would always be registered. Harmless perhaps, but worth noting.

          Does anyone see issues with the above?

          Show
          Chris Beams added a comment - ok – it certainly does look like there's demand for this change. I'm moving the fixVersion to 3.2 M1 so that I can at least take a stab at a NOT operator. I'll also look at the feasibility of full SpEL expression support, but at this point I would favor as simple as possible a set of changes for the reasons I initially listed above. One thing that I'd appreciate feedback on: The current semantics of comma-delimited profile strings is that the comma represents OR logic, e.g.: <bean profile= "p1,p2" ... /> indicates that this bean should be registered if p1 is active OR p2 is active. Introducing a simple NOT ( ! ) operator, <bean profile= "p1,!p2" ... /> would by extension indicate that this bean should be registered if p1 is active OR p2 is NOT active This syntax would also allow for strange loopholes like the following: <bean profile= "p1,!p1" ... /> in which case the given bean would always be registered. Harmless perhaps, but worth noting. Does anyone see issues with the above?
          Hide
          Peter Wilkinson added a comment -

          @Chris : Looks good to me. =)

          Show
          Peter Wilkinson added a comment - @Chris : Looks good to me. =)
          Hide
          Erwin Vervaet added a comment -

          I like the approach.
          Since the NOT operator is well understood it is easy to reason through a profile specification like "p1,!p2" or even "p1,!p1" and understand when a set beans will be activated.
          Furthermore, simply introducing the "!" prefix also works elegantly in @Profile.

          So +1 from me.

          Show
          Erwin Vervaet added a comment - I like the approach. Since the NOT operator is well understood it is easy to reason through a profile specification like "p1,!p2" or even "p1,!p1" and understand when a set beans will be activated. Furthermore, simply introducing the "!" prefix also works elegantly in @Profile. So +1 from me.
          Hide
          Chris Beams added a comment -
          commit bcd44f3798ed06c0704d2a3564b8a9735e747e87
          Author: Chris Beams <cbeams@vmware.com>
          Date:   Sun May 27 08:10:40 2012 +0300
          
              Support not (!) operator for profile selection
              
              The following syntax is now supported
              
                <beans profile="p1,!p2">
              
                @Profile("p1", "!p2")
              
              indicating that the <beans> element or annotated component should
              be processed only if profile 'p1' is active or profile 'p2' is not
              active.
              
              Issue: SPR-8728
          
          Show
          Chris Beams added a comment - commit bcd44f3798ed06c0704d2a3564b8a9735e747e87 Author: Chris Beams <cbeams@vmware.com> Date: Sun May 27 08:10:40 2012 +0300 Support not (!) operator for profile selection The following syntax is now supported <beans profile="p1,!p2"> @Profile("p1", "!p2") indicating that the <beans> element or annotated component should be processed only if profile 'p1' is active or profile 'p2' is not active. Issue: SPR-8728

            People

            • Assignee:
              Chris Beams
              Reporter:
              Tobias Mattsson
              Last updater:
              Chris Beams
            • Votes:
              7 Vote for this issue
              Watchers:
              12 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:
                Days since last comment:
                1 year, 46 weeks, 4 days ago

                Time Tracking

                Estimated:
                Original Estimate - Not Specified
                Not Specified
                Remaining:
                Remaining Estimate - Not Specified
                Not Specified
                Logged:
                Time Spent - 2.5h
                2.5h