Is the `std::exception`, in its current form, redundant?












9














Usually, when I want to create my own exception, I inherit from std::exception or std::runtime_error.



Is there anything that stops me from creating my own empty "tag-class"?



class out_of_bounds_access {}; // or:
class memory_leak {};


and throw just that?

After all, mostly, it's the class-name that carries information about what went wrong not the members of the exception class.



Ok, so I assumed this is a bad idea, but why? Why is this a bad idea?



P.S. I know there are cases in which "custom-made" exceptions carry information that latter is used to determine the correct approach to solve the problem...

However, if you think about it, cases like that can, very often (not always, but often), be re-done to throw & catch multiple different tag-classes instead of just a single one (with "content").










share|improve this question




















  • 3




    For fun, your coworkers will love you for it, you can also just throw string literals or integers. It's not required to throw objects at all.
    – john
    Dec 3 '18 at 12:30






  • 2




    The main difference is that std::exception has what, so in case of catching std::exception &, you can print/log some info about it. If you catch (...), you cannot print/log it in a standard conformant way. If you don't care about this, feel free to use your own exception classes.
    – geza
    Dec 3 '18 at 12:35








  • 5




    @john Well, those are objects too :)
    – Lightness Races in Orbit
    Dec 3 '18 at 12:36










  • @LightnessRacesinOrbit maybe in Java. Int is not a class or object in c++.
    – lalala
    Dec 3 '18 at 18:43






  • 3




    @lalala The opposite, actually. The C++ standard uses the term "object" to refer to values of any type at all, including non-class types: anything that has a storage location. Java does no such thing, and uses "object" to mean "an instance of a class", which does not include ints.
    – amalloy
    Dec 3 '18 at 19:38
















9














Usually, when I want to create my own exception, I inherit from std::exception or std::runtime_error.



Is there anything that stops me from creating my own empty "tag-class"?



class out_of_bounds_access {}; // or:
class memory_leak {};


and throw just that?

After all, mostly, it's the class-name that carries information about what went wrong not the members of the exception class.



Ok, so I assumed this is a bad idea, but why? Why is this a bad idea?



P.S. I know there are cases in which "custom-made" exceptions carry information that latter is used to determine the correct approach to solve the problem...

However, if you think about it, cases like that can, very often (not always, but often), be re-done to throw & catch multiple different tag-classes instead of just a single one (with "content").










share|improve this question




















  • 3




    For fun, your coworkers will love you for it, you can also just throw string literals or integers. It's not required to throw objects at all.
    – john
    Dec 3 '18 at 12:30






  • 2




    The main difference is that std::exception has what, so in case of catching std::exception &, you can print/log some info about it. If you catch (...), you cannot print/log it in a standard conformant way. If you don't care about this, feel free to use your own exception classes.
    – geza
    Dec 3 '18 at 12:35








  • 5




    @john Well, those are objects too :)
    – Lightness Races in Orbit
    Dec 3 '18 at 12:36










  • @LightnessRacesinOrbit maybe in Java. Int is not a class or object in c++.
    – lalala
    Dec 3 '18 at 18:43






  • 3




    @lalala The opposite, actually. The C++ standard uses the term "object" to refer to values of any type at all, including non-class types: anything that has a storage location. Java does no such thing, and uses "object" to mean "an instance of a class", which does not include ints.
    – amalloy
    Dec 3 '18 at 19:38














9












9








9







Usually, when I want to create my own exception, I inherit from std::exception or std::runtime_error.



Is there anything that stops me from creating my own empty "tag-class"?



class out_of_bounds_access {}; // or:
class memory_leak {};


and throw just that?

After all, mostly, it's the class-name that carries information about what went wrong not the members of the exception class.



Ok, so I assumed this is a bad idea, but why? Why is this a bad idea?



P.S. I know there are cases in which "custom-made" exceptions carry information that latter is used to determine the correct approach to solve the problem...

However, if you think about it, cases like that can, very often (not always, but often), be re-done to throw & catch multiple different tag-classes instead of just a single one (with "content").










share|improve this question















Usually, when I want to create my own exception, I inherit from std::exception or std::runtime_error.



Is there anything that stops me from creating my own empty "tag-class"?



class out_of_bounds_access {}; // or:
class memory_leak {};


and throw just that?

After all, mostly, it's the class-name that carries information about what went wrong not the members of the exception class.



Ok, so I assumed this is a bad idea, but why? Why is this a bad idea?



P.S. I know there are cases in which "custom-made" exceptions carry information that latter is used to determine the correct approach to solve the problem...

However, if you think about it, cases like that can, very often (not always, but often), be re-done to throw & catch multiple different tag-classes instead of just a single one (with "content").







c++ exception






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 3 '18 at 12:23

























asked Dec 3 '18 at 12:16









cukier9a7b5

513415




513415








  • 3




    For fun, your coworkers will love you for it, you can also just throw string literals or integers. It's not required to throw objects at all.
    – john
    Dec 3 '18 at 12:30






  • 2




    The main difference is that std::exception has what, so in case of catching std::exception &, you can print/log some info about it. If you catch (...), you cannot print/log it in a standard conformant way. If you don't care about this, feel free to use your own exception classes.
    – geza
    Dec 3 '18 at 12:35








  • 5




    @john Well, those are objects too :)
    – Lightness Races in Orbit
    Dec 3 '18 at 12:36










  • @LightnessRacesinOrbit maybe in Java. Int is not a class or object in c++.
    – lalala
    Dec 3 '18 at 18:43






  • 3




    @lalala The opposite, actually. The C++ standard uses the term "object" to refer to values of any type at all, including non-class types: anything that has a storage location. Java does no such thing, and uses "object" to mean "an instance of a class", which does not include ints.
    – amalloy
    Dec 3 '18 at 19:38














  • 3




    For fun, your coworkers will love you for it, you can also just throw string literals or integers. It's not required to throw objects at all.
    – john
    Dec 3 '18 at 12:30






  • 2




    The main difference is that std::exception has what, so in case of catching std::exception &, you can print/log some info about it. If you catch (...), you cannot print/log it in a standard conformant way. If you don't care about this, feel free to use your own exception classes.
    – geza
    Dec 3 '18 at 12:35








  • 5




    @john Well, those are objects too :)
    – Lightness Races in Orbit
    Dec 3 '18 at 12:36










  • @LightnessRacesinOrbit maybe in Java. Int is not a class or object in c++.
    – lalala
    Dec 3 '18 at 18:43






  • 3




    @lalala The opposite, actually. The C++ standard uses the term "object" to refer to values of any type at all, including non-class types: anything that has a storage location. Java does no such thing, and uses "object" to mean "an instance of a class", which does not include ints.
    – amalloy
    Dec 3 '18 at 19:38








3




3




For fun, your coworkers will love you for it, you can also just throw string literals or integers. It's not required to throw objects at all.
– john
Dec 3 '18 at 12:30




For fun, your coworkers will love you for it, you can also just throw string literals or integers. It's not required to throw objects at all.
– john
Dec 3 '18 at 12:30




2




2




The main difference is that std::exception has what, so in case of catching std::exception &, you can print/log some info about it. If you catch (...), you cannot print/log it in a standard conformant way. If you don't care about this, feel free to use your own exception classes.
– geza
Dec 3 '18 at 12:35






The main difference is that std::exception has what, so in case of catching std::exception &, you can print/log some info about it. If you catch (...), you cannot print/log it in a standard conformant way. If you don't care about this, feel free to use your own exception classes.
– geza
Dec 3 '18 at 12:35






5




5




@john Well, those are objects too :)
– Lightness Races in Orbit
Dec 3 '18 at 12:36




@john Well, those are objects too :)
– Lightness Races in Orbit
Dec 3 '18 at 12:36












@LightnessRacesinOrbit maybe in Java. Int is not a class or object in c++.
– lalala
Dec 3 '18 at 18:43




@LightnessRacesinOrbit maybe in Java. Int is not a class or object in c++.
– lalala
Dec 3 '18 at 18:43




3




3




@lalala The opposite, actually. The C++ standard uses the term "object" to refer to values of any type at all, including non-class types: anything that has a storage location. Java does no such thing, and uses "object" to mean "an instance of a class", which does not include ints.
– amalloy
Dec 3 '18 at 19:38




@lalala The opposite, actually. The C++ standard uses the term "object" to refer to values of any type at all, including non-class types: anything that has a storage location. Java does no such thing, and uses "object" to mean "an instance of a class", which does not include ints.
– amalloy
Dec 3 '18 at 19:38












1 Answer
1






active

oldest

votes


















26














No, nothing stops you from doing this.



However, some code that wants to catch "any exception" will catch const std::exception&, and if your exception type doesn't derive from std::exception then that won't work.



Sure, we can catch ... instead but that's, in my experience, used as a last-ditch "blunt instrument" for avoiding termination due to uncaught exceptions, and can't tell you anything about the exception itself.



Boost exceptions don't derive from std::exception and it's really annoying.



Why not just make all the exceptions part of this standard hierarchy?



If you don't intend to ever let your exception types make get all the way to the top, then there may not be a practical problem here. But, why take the chance? You lose nothing by adding : std::runtime_error or somesuch, and the text string that you'll pass to the base is useful information for the diagnosing programmer.






share|improve this answer



















  • 5




    tl;dr: the benefit of using std::exception is that I will then be able to approve your pull request ;)
    – Lightness Races in Orbit
    Dec 3 '18 at 12:41






  • 1




    Yes... I completely forget it is easier to just catch(const std::exception& e) instead of something like catch(const out_of_bounds_access& e). + the case of e.what() Thank you very much!
    – cukier9a7b5
    Dec 3 '18 at 12:45








  • 5




    "Boost exceptions don't derive from std::exception and it's really annoying." – You're meant to use boost::throw_exception which enables a "normal" catch: "The emitted exception can be intercepted as E &, std::exception &, or boost::exception &."; I'm not defending the design decision, but they do have some magic that "fixes" things.
    – Arne Vogel
    Dec 3 '18 at 16:07








  • 4




    @ArneVogel Ah, bollocks. Do you think it's too late to raise a bug at my old company? :D
    – Lightness Races in Orbit
    Dec 3 '18 at 16:09






  • 3




    @LightnessRacesinOrbit Depends on how old. It was post-dotcom bust, right? ;-) … @cukier9a7b5: BTW, in addition to ex.what() you can use typeid(ex).name(), which will give you the mangled type name of the most derived object (because std::exception has virtual methods). This can at least be useful for debug logging. In theory, you could even have associative container of handlers (keyed on std::type_index) – but don't overdesign it.
    – Arne Vogel
    Dec 3 '18 at 16:21











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53593701%2fis-the-stdexception-in-its-current-form-redundant%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









26














No, nothing stops you from doing this.



However, some code that wants to catch "any exception" will catch const std::exception&, and if your exception type doesn't derive from std::exception then that won't work.



Sure, we can catch ... instead but that's, in my experience, used as a last-ditch "blunt instrument" for avoiding termination due to uncaught exceptions, and can't tell you anything about the exception itself.



Boost exceptions don't derive from std::exception and it's really annoying.



Why not just make all the exceptions part of this standard hierarchy?



If you don't intend to ever let your exception types make get all the way to the top, then there may not be a practical problem here. But, why take the chance? You lose nothing by adding : std::runtime_error or somesuch, and the text string that you'll pass to the base is useful information for the diagnosing programmer.






share|improve this answer



















  • 5




    tl;dr: the benefit of using std::exception is that I will then be able to approve your pull request ;)
    – Lightness Races in Orbit
    Dec 3 '18 at 12:41






  • 1




    Yes... I completely forget it is easier to just catch(const std::exception& e) instead of something like catch(const out_of_bounds_access& e). + the case of e.what() Thank you very much!
    – cukier9a7b5
    Dec 3 '18 at 12:45








  • 5




    "Boost exceptions don't derive from std::exception and it's really annoying." – You're meant to use boost::throw_exception which enables a "normal" catch: "The emitted exception can be intercepted as E &, std::exception &, or boost::exception &."; I'm not defending the design decision, but they do have some magic that "fixes" things.
    – Arne Vogel
    Dec 3 '18 at 16:07








  • 4




    @ArneVogel Ah, bollocks. Do you think it's too late to raise a bug at my old company? :D
    – Lightness Races in Orbit
    Dec 3 '18 at 16:09






  • 3




    @LightnessRacesinOrbit Depends on how old. It was post-dotcom bust, right? ;-) … @cukier9a7b5: BTW, in addition to ex.what() you can use typeid(ex).name(), which will give you the mangled type name of the most derived object (because std::exception has virtual methods). This can at least be useful for debug logging. In theory, you could even have associative container of handlers (keyed on std::type_index) – but don't overdesign it.
    – Arne Vogel
    Dec 3 '18 at 16:21
















26














No, nothing stops you from doing this.



However, some code that wants to catch "any exception" will catch const std::exception&, and if your exception type doesn't derive from std::exception then that won't work.



Sure, we can catch ... instead but that's, in my experience, used as a last-ditch "blunt instrument" for avoiding termination due to uncaught exceptions, and can't tell you anything about the exception itself.



Boost exceptions don't derive from std::exception and it's really annoying.



Why not just make all the exceptions part of this standard hierarchy?



If you don't intend to ever let your exception types make get all the way to the top, then there may not be a practical problem here. But, why take the chance? You lose nothing by adding : std::runtime_error or somesuch, and the text string that you'll pass to the base is useful information for the diagnosing programmer.






share|improve this answer



















  • 5




    tl;dr: the benefit of using std::exception is that I will then be able to approve your pull request ;)
    – Lightness Races in Orbit
    Dec 3 '18 at 12:41






  • 1




    Yes... I completely forget it is easier to just catch(const std::exception& e) instead of something like catch(const out_of_bounds_access& e). + the case of e.what() Thank you very much!
    – cukier9a7b5
    Dec 3 '18 at 12:45








  • 5




    "Boost exceptions don't derive from std::exception and it's really annoying." – You're meant to use boost::throw_exception which enables a "normal" catch: "The emitted exception can be intercepted as E &, std::exception &, or boost::exception &."; I'm not defending the design decision, but they do have some magic that "fixes" things.
    – Arne Vogel
    Dec 3 '18 at 16:07








  • 4




    @ArneVogel Ah, bollocks. Do you think it's too late to raise a bug at my old company? :D
    – Lightness Races in Orbit
    Dec 3 '18 at 16:09






  • 3




    @LightnessRacesinOrbit Depends on how old. It was post-dotcom bust, right? ;-) … @cukier9a7b5: BTW, in addition to ex.what() you can use typeid(ex).name(), which will give you the mangled type name of the most derived object (because std::exception has virtual methods). This can at least be useful for debug logging. In theory, you could even have associative container of handlers (keyed on std::type_index) – but don't overdesign it.
    – Arne Vogel
    Dec 3 '18 at 16:21














26












26








26






No, nothing stops you from doing this.



However, some code that wants to catch "any exception" will catch const std::exception&, and if your exception type doesn't derive from std::exception then that won't work.



Sure, we can catch ... instead but that's, in my experience, used as a last-ditch "blunt instrument" for avoiding termination due to uncaught exceptions, and can't tell you anything about the exception itself.



Boost exceptions don't derive from std::exception and it's really annoying.



Why not just make all the exceptions part of this standard hierarchy?



If you don't intend to ever let your exception types make get all the way to the top, then there may not be a practical problem here. But, why take the chance? You lose nothing by adding : std::runtime_error or somesuch, and the text string that you'll pass to the base is useful information for the diagnosing programmer.






share|improve this answer














No, nothing stops you from doing this.



However, some code that wants to catch "any exception" will catch const std::exception&, and if your exception type doesn't derive from std::exception then that won't work.



Sure, we can catch ... instead but that's, in my experience, used as a last-ditch "blunt instrument" for avoiding termination due to uncaught exceptions, and can't tell you anything about the exception itself.



Boost exceptions don't derive from std::exception and it's really annoying.



Why not just make all the exceptions part of this standard hierarchy?



If you don't intend to ever let your exception types make get all the way to the top, then there may not be a practical problem here. But, why take the chance? You lose nothing by adding : std::runtime_error or somesuch, and the text string that you'll pass to the base is useful information for the diagnosing programmer.







share|improve this answer














share|improve this answer



share|improve this answer








edited Dec 3 '18 at 16:09

























answered Dec 3 '18 at 12:20









Lightness Races in Orbit

284k51460781




284k51460781








  • 5




    tl;dr: the benefit of using std::exception is that I will then be able to approve your pull request ;)
    – Lightness Races in Orbit
    Dec 3 '18 at 12:41






  • 1




    Yes... I completely forget it is easier to just catch(const std::exception& e) instead of something like catch(const out_of_bounds_access& e). + the case of e.what() Thank you very much!
    – cukier9a7b5
    Dec 3 '18 at 12:45








  • 5




    "Boost exceptions don't derive from std::exception and it's really annoying." – You're meant to use boost::throw_exception which enables a "normal" catch: "The emitted exception can be intercepted as E &, std::exception &, or boost::exception &."; I'm not defending the design decision, but they do have some magic that "fixes" things.
    – Arne Vogel
    Dec 3 '18 at 16:07








  • 4




    @ArneVogel Ah, bollocks. Do you think it's too late to raise a bug at my old company? :D
    – Lightness Races in Orbit
    Dec 3 '18 at 16:09






  • 3




    @LightnessRacesinOrbit Depends on how old. It was post-dotcom bust, right? ;-) … @cukier9a7b5: BTW, in addition to ex.what() you can use typeid(ex).name(), which will give you the mangled type name of the most derived object (because std::exception has virtual methods). This can at least be useful for debug logging. In theory, you could even have associative container of handlers (keyed on std::type_index) – but don't overdesign it.
    – Arne Vogel
    Dec 3 '18 at 16:21














  • 5




    tl;dr: the benefit of using std::exception is that I will then be able to approve your pull request ;)
    – Lightness Races in Orbit
    Dec 3 '18 at 12:41






  • 1




    Yes... I completely forget it is easier to just catch(const std::exception& e) instead of something like catch(const out_of_bounds_access& e). + the case of e.what() Thank you very much!
    – cukier9a7b5
    Dec 3 '18 at 12:45








  • 5




    "Boost exceptions don't derive from std::exception and it's really annoying." – You're meant to use boost::throw_exception which enables a "normal" catch: "The emitted exception can be intercepted as E &, std::exception &, or boost::exception &."; I'm not defending the design decision, but they do have some magic that "fixes" things.
    – Arne Vogel
    Dec 3 '18 at 16:07








  • 4




    @ArneVogel Ah, bollocks. Do you think it's too late to raise a bug at my old company? :D
    – Lightness Races in Orbit
    Dec 3 '18 at 16:09






  • 3




    @LightnessRacesinOrbit Depends on how old. It was post-dotcom bust, right? ;-) … @cukier9a7b5: BTW, in addition to ex.what() you can use typeid(ex).name(), which will give you the mangled type name of the most derived object (because std::exception has virtual methods). This can at least be useful for debug logging. In theory, you could even have associative container of handlers (keyed on std::type_index) – but don't overdesign it.
    – Arne Vogel
    Dec 3 '18 at 16:21








5




5




tl;dr: the benefit of using std::exception is that I will then be able to approve your pull request ;)
– Lightness Races in Orbit
Dec 3 '18 at 12:41




tl;dr: the benefit of using std::exception is that I will then be able to approve your pull request ;)
– Lightness Races in Orbit
Dec 3 '18 at 12:41




1




1




Yes... I completely forget it is easier to just catch(const std::exception& e) instead of something like catch(const out_of_bounds_access& e). + the case of e.what() Thank you very much!
– cukier9a7b5
Dec 3 '18 at 12:45






Yes... I completely forget it is easier to just catch(const std::exception& e) instead of something like catch(const out_of_bounds_access& e). + the case of e.what() Thank you very much!
– cukier9a7b5
Dec 3 '18 at 12:45






5




5




"Boost exceptions don't derive from std::exception and it's really annoying." – You're meant to use boost::throw_exception which enables a "normal" catch: "The emitted exception can be intercepted as E &, std::exception &, or boost::exception &."; I'm not defending the design decision, but they do have some magic that "fixes" things.
– Arne Vogel
Dec 3 '18 at 16:07






"Boost exceptions don't derive from std::exception and it's really annoying." – You're meant to use boost::throw_exception which enables a "normal" catch: "The emitted exception can be intercepted as E &, std::exception &, or boost::exception &."; I'm not defending the design decision, but they do have some magic that "fixes" things.
– Arne Vogel
Dec 3 '18 at 16:07






4




4




@ArneVogel Ah, bollocks. Do you think it's too late to raise a bug at my old company? :D
– Lightness Races in Orbit
Dec 3 '18 at 16:09




@ArneVogel Ah, bollocks. Do you think it's too late to raise a bug at my old company? :D
– Lightness Races in Orbit
Dec 3 '18 at 16:09




3




3




@LightnessRacesinOrbit Depends on how old. It was post-dotcom bust, right? ;-) … @cukier9a7b5: BTW, in addition to ex.what() you can use typeid(ex).name(), which will give you the mangled type name of the most derived object (because std::exception has virtual methods). This can at least be useful for debug logging. In theory, you could even have associative container of handlers (keyed on std::type_index) – but don't overdesign it.
– Arne Vogel
Dec 3 '18 at 16:21




@LightnessRacesinOrbit Depends on how old. It was post-dotcom bust, right? ;-) … @cukier9a7b5: BTW, in addition to ex.what() you can use typeid(ex).name(), which will give you the mangled type name of the most derived object (because std::exception has virtual methods). This can at least be useful for debug logging. In theory, you could even have associative container of handlers (keyed on std::type_index) – but don't overdesign it.
– Arne Vogel
Dec 3 '18 at 16:21


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53593701%2fis-the-stdexception-in-its-current-form-redundant%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

How to change which sound is reproduced for terminal bell?

Title Spacing in Bjornstrup Chapter, Removing Chapter Number From Contents

Can I use Tabulator js library in my java Spring + Thymeleaf project?