In a business environment we end up needing different kinds of data across applications so that it makes sense to store them in a shared location. For instance you may have a set of applications that share the same set of users, and we need authorization information for each of them listing what roles they have so we can know what they need to access. That kind of thing goes into an LDAP data store, you can think of it as a hierarchical database optimized for fast read access.
All sorts of things can go in these datastores, it's normal for an application server to stash connection pools in them, for instance. A lot of these, like users, roles, and connection pools, are vital things you need to do your job.
JNDI is the standard Java API for accessing these LDAP datastores.
The nasty thing about the service locator design pattern is that the client code doing the lookup has to know too much about the thing it is querying (mainly, where to get it from), and having that lookup hard-coded in the client makes the code inflexible and hard to test. But if we use dependency injection (whether it's CDI, Spring, whatever) we can have the framework inject the value we want into the code, while the JNDI lookups are handled within the framework code and not in the application. That means you can use JNDI without your application code having to use the service locator pattern.