Create another Singleton object from a Singleton class
It has been some time since I last wrote Java, but I have some general suggestions.

Before focusing on specific details of the code I would recommend you to spend some time on indentation. Having consistent indentation helps reduce the cognitive load when reading your code.

Second I would urge you to consider if a singleton is the appropriate solution for this problem. Using a static class makes testing both the implementation and consuming code difficult. The first being your implementation sharing state between test-cases and you therefore have to spend extra time ensuring you reset the state between each test-case. The latter is due to the consuming code hiding its dependencies. You aren't able to test a consuming class in isolation as you would implicitly also test the cache manager, which in turn also involves the third party code. Furthermore imagine that if the third party implementation relies on I/O operations your tests of the consuming code now also relies on I/O operations. Unit tests especially shouldn't be concerned with I/O such as: file operations, network, databases etc.

To remedy this I would recommend looking into creating an interface, which defines only the required methods of your cache manager. From the provided code an example of an interface could be:

interface ICacheManager 
{
    &lt;T&gt; T getCacheData(String key);

    &lt;T&gt; void setCacheData(String key, T value);

    void closeCacheConnection();
}


Then have your cache manager class implement this interface and use the inversion of control principle in your consuming code to makes its dependency on the cache manager explicit. This is done by requiring an instance of your interface in its constructor (dependency injection). Now in your test-cases you are able to create mock instances of your cache manager when testing consuming code. Using an interface also allows you to provide alternative cache manager implementation in your consuming code without changing a thing. The only requirement being all cache manager implementations adheres to your ICacheManager interface.

The next thing, which caught my eye was how the getConnection() method requires an instance of CacheConfig in its parameter list. Imagine that every time your consuming code needs access to your cache manager (by calling this method) it would also need to know about your cache configurations and have an instance of this. This breaks the separation of concerns principle. You can fix this easily by using the previously proposed solution by using  the inversion of control principle.

If this isn't an option I would create a factory class, which I would ensure is always seeded with an instance of the appropriate cache configurations when starting the application. Then have the factory call the getConnection() method and return the cache manager instance. You would therefore use the factory in your consuming code. You could also seed the cache manager directly with the appropriate cache configurations, but I find that more people understand the principle of the factory pattern and it would therefore make your code easier to understand. 


  Note that this solution still hides the dependency of either the
  factory or the cache manager in consuming code as mentioned previously
  and therefore retains all the mentioned side-effects.


Next I find it enjoyable to see you checking for NULL after entering the synchronized block in the getConnection() method. Not many people remember to do this. Great job! 

You should also include a check to see if the connection has already been closed in the closeCacheConnection() method. Have you checked how the third party code behaves if you try to close the connection twice? Your code should not depend on such things.

In the getConnection() method you write the name of the method in the error log message. I would recommend you to not do that. If the method is renamed and the log message isn't updated the log entries wont correspond with code. As you are already providing the exception a stacktrace accompanies the message and the name of the method would therefore already be available to the logging implementation along with line numbers etc.

Now lastly a minor pain-points of mine. In the getCacheData() and setCacheData() methods you name the variables containing the cache entry as e. This name is often associated with an exception and in the setCacheData() method this is actually the case. While this not being a syntax error I find it easier to define unique names for each variable, which is also descriptive. In this case I would recommend renaming the variables to entry as it's the contents.