Repository: ambari Updated Branches: refs/heads/trunk 52422ca16 -> 439a0a40d
AMBARI-7938 - Views: ability to use current ambari authorization + session Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/439a0a40 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/439a0a40 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/439a0a40 Branch: refs/heads/trunk Commit: 439a0a40d5bffdfa38059deb212d3658ea79adf1 Parents: 52422ca Author: tbeerbower <[email protected]> Authored: Thu Oct 23 19:45:27 2014 -0400 Committer: tbeerbower <[email protected]> Committed: Fri Oct 24 13:21:18 2014 -0400 ---------------------------------------------------------------------- .../controller/AmbariManagementController.java | 10 ++ .../ambari/server/controller/AmbariServer.java | 4 + .../server/controller/AmbariSessionManager.java | 73 +++++++++++++ .../controller/internal/URLStreamProvider.java | 108 ++++++++++++++----- .../server/view/ViewAmbariStreamProvider.java | 108 +++++++++++++++++++ .../ambari/server/view/ViewContextImpl.java | 100 +++++------------ .../apache/ambari/server/view/ViewRegistry.java | 45 ++++++++ .../server/view/ViewURLStreamProvider.java | 78 ++++++++++++++ .../internal/URLStreamProviderTest.java | 83 ++++++++++++++ .../view/ViewAmbariStreamProviderTest.java | 73 +++++++++++++ .../ambari/server/view/ViewContextImplTest.java | 45 ++++---- .../server/view/ViewURLStreamProviderTest.java | 80 ++++++++++++++ .../ambari/view/AmbariStreamProvider.java | 45 ++++++++ .../org/apache/ambari/view/ViewContext.java | 8 +- 14 files changed, 737 insertions(+), 123 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java index c37ee79..5af88a8 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java @@ -49,6 +49,16 @@ import java.util.Set; */ public interface AmbariManagementController { + /** + * Get an Ambari endpoint URI for the given path. + * + * @param path the path (e.g. /api/v1/users) + * + * @return the Ambari endpoint URI + */ + public String getAmbariServerURI(String path); + + // ----- Create ----------------------------------------------------------- /** http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java index 4a064a2..cfb73bc 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java @@ -114,6 +114,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.request.RequestContextListener; import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.filter.DelegatingFilterProxy; @@ -276,6 +277,9 @@ public class AmbariServer { root.addFilter(new FilterHolder(new MethodOverrideFilter()), "/api/*", 1); root.addFilter(new FilterHolder(new MethodOverrideFilter()), "/proxy/*", 1); + // register listener to capture request context + root.addEventListener(new RequestContextListener()); + agentroot.addFilter(new FilterHolder(injector.getInstance(AmbariPersistFilter.class)), "/agent/*", 1); agentroot.addFilter(SecurityFilter.class, "/*", 1); http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariSessionManager.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariSessionManager.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariSessionManager.java new file mode 100644 index 0000000..e6dd07f --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariSessionManager.java @@ -0,0 +1,73 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.controller; + +import org.eclipse.jetty.server.SessionManager; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +/** + * Class to expose current session information. + */ +public class AmbariSessionManager { + + /** + * Session manager. + */ + @Inject + SessionManager sessionManager; + + + // ----- AmbariSessionManager ---------------------------------------------- + + /** + * Get the session id associated with the current thread-bound request. + * + * @return the current session id; null if no request is associated with the current thread + */ + public String getCurrentSessionId() { + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + + if (requestAttributes instanceof ServletRequestAttributes) { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes; + + HttpServletRequest request = servletRequestAttributes.getRequest(); + if (request != null) { + HttpSession session = request.getSession(true); + + return session.getId(); + } + } + return null; + } + + /** + * The session cookie id (i.e. AMBARISESSIONID). + * + * @return the session cookie + */ + public String getSessionCookie() { + return sessionManager.getSessionCookie(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/URLStreamProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/URLStreamProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/URLStreamProvider.java index 1b57c84..156c736 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/URLStreamProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/URLStreamProvider.java @@ -24,8 +24,9 @@ import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; -import java.net.URLConnection; import java.security.KeyStore; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -46,7 +47,7 @@ import org.apache.http.HttpStatus; */ public class URLStreamProvider implements StreamProvider { - private static final String COOKIE = "Cookie"; + public static final String COOKIE = "Cookie"; private static final String WWW_AUTHENTICATE = "WWW-Authenticate"; private static final String NEGOTIATE = "Negotiate"; private static Log LOG = LogFactory.getLog(URLStreamProvider.class); @@ -57,7 +58,10 @@ public class URLStreamProvider implements StreamProvider { private final String password; private final String type; private volatile SSLSocketFactory sslSocketFactory = null; - private AppCookieManager appCookieManager; + private AppCookieManager appCookieManager = null; + + + // ----- Constructors ------------------------------------------------------ /** * Provide the connection timeout for the underlying connection. @@ -88,18 +92,39 @@ public class URLStreamProvider implements StreamProvider { this.connTimeout = connectionTimeout; this.readTimeout = readTimeout; - this.path = path; // truststroe path - this.password = password; // truststore password - this.type = type; // truststroe type - appCookieManager = new AppCookieManager(); + this.path = path; // truststroe path + this.password = password; // truststore password + this.type = type; // truststroe type } - + + // ----- StreamProvider ---------------------------------------------------- + @Override public InputStream readFrom(String spec, String requestMethod, String params) throws IOException { return processURL(spec, requestMethod, params, null).getInputStream(); } + @Override + public InputStream readFrom(String spec) throws IOException { + return readFrom(spec, "GET", null); + } + + + // ----- URLStreamProvider ------------------------------------------------- + + /** + * Get a URL connection from the given spec. + * + * @param spec the String to parse as a URL + * @param requestMethod the HTTP method (GET,POST,PUT,etc.). + * @param params the body of the request; may be null + * @param headers the headers of the request; may be null + * + * @return a URL connection + * + * @throws IOException if the URL connection can not be established + */ public HttpURLConnection processURL(String spec, String requestMethod, Object params, Map<String, List<String>> headers) throws IOException { if (LOG.isDebugEnabled()) { @@ -107,13 +132,25 @@ public class URLStreamProvider implements StreamProvider { } HttpURLConnection connection = spec.startsWith("https") ? - (HttpURLConnection)getSSLConnection(spec) - : (HttpURLConnection)getConnection(spec); + getSSLConnection(spec) : getConnection(spec); + + AppCookieManager appCookieManager = getAppCookieManager(); String appCookie = appCookieManager.getCachedAppCookie(spec); if (appCookie != null) { LOG.debug("Using cached app cookie for URL:" + spec); - connection.setRequestProperty(COOKIE, appCookie); + + // allow for additional passed in cookies + if (headers == null || headers.isEmpty()) { + headers = Collections.singletonMap(COOKIE, Collections.singletonList(appCookie)); + } else { + headers = new HashMap<String, List<String>>(headers); + + List<String> cookieList = headers.get(COOKIE); + String cookies = cookieList.isEmpty() ? null : cookieList.get(0); + + headers.put(COOKIE, Collections.singletonList(appendCookie(cookies, appCookie))); + } } connection.setConnectTimeout(connTimeout); connection.setReadTimeout(readTimeout); @@ -145,10 +182,8 @@ public class URLStreamProvider implements StreamProvider { } if (wwwAuthHeader != null && wwwAuthHeader.trim().startsWith(NEGOTIATE)) { - //connection.getInputStream().close(); connection = spec.startsWith("https") ? - (HttpURLConnection)getSSLConnection(spec) - : (HttpURLConnection)getConnection(spec); + getSSLConnection(spec) : getConnection(spec); appCookie = appCookieManager.getAppCookie(spec, true); connection.setRequestProperty(COOKIE, appCookie); connection.setConnectTimeout(connTimeout); @@ -168,23 +203,44 @@ public class URLStreamProvider implements StreamProvider { return connection; } } - - @Override - public InputStream readFrom(String spec) throws IOException { - - return readFrom(spec, "GET", null); - + + /** + * Get the associated app cookie manager. + * + * @return the app cookie manager + */ + public synchronized AppCookieManager getAppCookieManager() { + if (appCookieManager == null) { + appCookieManager = new AppCookieManager(); + } + return appCookieManager; + } + + /** + * Utility method to append a new cookie value to an existing list of cookies. + * + * @param cookies the semicolon separated list of cookie values + * @param newCookie a new cookie value to be appended. + * + * @return the new list of cookie values + */ + public static String appendCookie(String cookies, String newCookie) { + if (cookies == null || cookies.length() == 0) { + return newCookie; + } + return cookies + "; " + newCookie; } - + + // ----- helper methods ---------------------------------------------------- // Get a connection - private URLConnection getConnection(String spec) throws IOException { - return new URL(spec).openConnection(); + protected HttpURLConnection getConnection(String spec) throws IOException { + return (HttpURLConnection) new URL(spec).openConnection(); } // Get an ssl connection - private HttpsURLConnection getSSLConnection(String spec) throws IOException { + protected HttpsURLConnection getSSLConnection(String spec) throws IOException { if (sslSocketFactory == null) { synchronized (this) { @@ -218,8 +274,4 @@ public class URLStreamProvider implements StreamProvider { return connection; } - - public AppCookieManager getAppCookieManager() { - return appCookieManager; - } } http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-server/src/main/java/org/apache/ambari/server/view/ViewAmbariStreamProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewAmbariStreamProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewAmbariStreamProvider.java new file mode 100644 index 0000000..d8448b8 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewAmbariStreamProvider.java @@ -0,0 +1,108 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.view; + +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.AmbariSessionManager; +import org.apache.ambari.server.controller.internal.URLStreamProvider; +import org.apache.ambari.view.AmbariStreamProvider; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Provider of an input stream for a request to the Ambari server. + */ +public class ViewAmbariStreamProvider implements AmbariStreamProvider { + /** + * Internal stream provider. + */ + private final URLStreamProvider streamProvider; + + /** + * The Ambari session manager. + */ + private final AmbariSessionManager ambariSessionManager; + + /** + * The Ambari management controller. + */ + private final AmbariManagementController controller; + + + // ----- Constructor ----------------------------------------------------- + + /** + * Construct a view Ambari stream provider. + * + * @param streamProvider the underlying stream provider + * @param ambariSessionManager the Ambari session manager + * @param controller the Ambari configuration + * + * @throws IllegalStateException if the Ambari stream provider can not be created + */ + protected ViewAmbariStreamProvider(URLStreamProvider streamProvider, AmbariSessionManager ambariSessionManager, + AmbariManagementController controller) { + this.streamProvider = streamProvider; + this.ambariSessionManager = ambariSessionManager; + this.controller = controller; + } + + + // ----- AmbariStreamProvider ----------------------------------------------- + + @Override + public InputStream readFrom(String path, String requestMethod, String params, Map<String, String> headers, + boolean useAmbariSession) + throws IOException { + + // add the Ambari session cookie to the given headers + if (useAmbariSession) { + String sessionId = ambariSessionManager.getCurrentSessionId(); + if (sessionId != null) { + + String ambariSessionCookie = ambariSessionManager.getSessionCookie() + "=" + sessionId; + + if (headers == null || headers.isEmpty()) { + headers = Collections.singletonMap(URLStreamProvider.COOKIE, ambariSessionCookie); + } else { + headers = new HashMap<String, String>(headers); + + String cookies = headers.get(URLStreamProvider.COOKIE); + + headers.put(URLStreamProvider.COOKIE, URLStreamProvider.appendCookie(cookies, ambariSessionCookie)); + } + } + } + + // adapt the headers for the internal URLStreamProvider signature + Map<String, List<String>> headerMap = new HashMap<String, List<String>>(); + for (Map.Entry<String, String> entry : headers.entrySet()) { + headerMap.put(entry.getKey(), Collections.singletonList(entry.getValue())); + } + + return streamProvider.processURL(controller.getAmbariServerURI(path.startsWith("/") ? path : "/" + path), + requestMethod, params, headerMap).getInputStream(); + } +} + http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java index 7fea0d5..dd7417a 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java @@ -20,7 +20,6 @@ package org.apache.ambari.server.view; import com.google.inject.Guice; import com.google.inject.Injector; -import org.apache.ambari.server.configuration.ComponentSSLConfiguration; import org.apache.ambari.server.orm.entities.PermissionEntity; import org.apache.ambari.server.orm.entities.ViewEntity; import org.apache.ambari.server.orm.entities.ViewInstanceEntity; @@ -29,15 +28,14 @@ import org.apache.ambari.server.view.configuration.ViewConfig; import org.apache.ambari.server.view.events.EventImpl; import org.apache.ambari.server.view.persistence.DataStoreImpl; import org.apache.ambari.server.view.persistence.DataStoreModule; +import org.apache.ambari.view.AmbariStreamProvider; import org.apache.ambari.view.DataStore; +import org.apache.ambari.view.ImpersonatorSetting; import org.apache.ambari.view.MaskException; import org.apache.ambari.view.Masker; import org.apache.ambari.view.ResourceProvider; import org.apache.ambari.view.SecurityException; import org.apache.ambari.view.SystemException; -import org.apache.ambari.view.URLStreamProvider; -import org.apache.ambari.server.controller.internal.AppCookieManager; -import org.apache.ambari.view.ImpersonatorSetting; import org.apache.ambari.view.ViewContext; import org.apache.ambari.view.ViewController; import org.apache.ambari.view.ViewDefinition; @@ -51,15 +49,11 @@ import org.apache.velocity.VelocityContext; import org.apache.velocity.app.Velocity; import org.apache.velocity.exception.ParseErrorException; -import java.io.IOException; -import java.io.InputStream; import java.io.StringWriter; import java.io.Writer; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -89,9 +83,14 @@ public class ViewContextImpl implements ViewContext, ViewController { private final ViewRegistry viewRegistry; /** - * The available stream provider. + * The URL stream provider. + */ + private ViewURLStreamProvider streamProvider; + + /** + * The Ambari stream provider. */ - private final ViewURLStreamProvider streamProvider; + private ViewAmbariStreamProvider ambariStreamProvider; /** * The data store. @@ -131,7 +130,6 @@ public class ViewContextImpl implements ViewContext, ViewController { this.viewEntity = viewEntity; this.viewInstanceEntity = viewInstanceEntity; this.viewRegistry = viewRegistry; - this.streamProvider = ViewURLStreamProvider.getProvider(); this.masker = getMasker(viewEntity.getClassLoader(), viewEntity.getConfiguration()); this.velocityContext = initVelocityContext(); } @@ -275,11 +273,20 @@ public class ViewContextImpl implements ViewContext, ViewController { } @Override - public URLStreamProvider getURLStreamProvider() { + public org.apache.ambari.view.URLStreamProvider getURLStreamProvider() { + ensureURLStreamProvider(); return streamProvider; } @Override + public synchronized AmbariStreamProvider getAmbariStreamProvider() { + if (ambariStreamProvider == null) { + ambariStreamProvider = viewRegistry.createAmbariStreamProvider(); + } + return ambariStreamProvider; + } + + @Override public synchronized DataStore getDataStore() { if (viewInstanceEntity != null) { if (dataStore == null) { @@ -311,7 +318,8 @@ public class ViewContextImpl implements ViewContext, ViewController { @Override public HttpImpersonatorImpl getHttpImpersonator() { - return new HttpImpersonatorImpl(this, this.streamProvider.getAppCookieManager()); + ensureURLStreamProvider(); + return new HttpImpersonatorImpl(this, streamProvider.getAppCookieManager()); } @Override @@ -319,6 +327,7 @@ public class ViewContextImpl implements ViewContext, ViewController { return new ImpersonatorSettingImpl(this); } + // ----- ViewController ---------------------------------------------------- @Override @@ -352,6 +361,13 @@ public class ViewContextImpl implements ViewContext, ViewController { // ----- helper methods ---------------------------------------------------- + // ensure that the URL stream provider has been created + private synchronized void ensureURLStreamProvider() { + if (streamProvider == null) { + streamProvider = viewRegistry.createURLStreamProvider(); + } + } + // check for an associated instance private void checkInstance() { if (viewInstanceEntity == null) { @@ -413,64 +429,6 @@ public class ViewContextImpl implements ViewContext, ViewController { return context; } - // ----- Inner class : ViewURLStreamProvider ------------------------------- - - /** - * Wrapper around internal URL stream provider. - */ - protected static class ViewURLStreamProvider implements URLStreamProvider { - private static final int DEFAULT_REQUEST_CONNECT_TIMEOUT = 5000; - private static final int DEFAULT_REQUEST_READ_TIMEOUT = 10000; - - /** - * Internal stream provider. - */ - private final org.apache.ambari.server.controller.internal.URLStreamProvider streamProvider; - - // ----- Constructor ----------------------------------------------------- - - protected ViewURLStreamProvider(org.apache.ambari.server.controller.internal.URLStreamProvider streamProvider) { - this.streamProvider = streamProvider; - } - - - // ----- URLStreamProvider ----------------------------------------------- - - @Override - public InputStream readFrom(String spec, String requestMethod, String params, Map<String, String> headers) - throws IOException { - // adapt to org.apache.ambari.server.controller.internal.URLStreamProvider processURL signature - Map<String, List<String>> headerMap = new HashMap<String, List<String>>(); - for (Map.Entry<String, String> entry : headers.entrySet()) { - headerMap.put(entry.getKey(), Collections.singletonList(entry.getValue())); - } - return streamProvider.processURL(spec, requestMethod, params, headerMap).getInputStream(); - } - - - // ----- helper methods -------------------------------------------------- - - /** - * Factory method. - * - * @return a new URL stream provider. - */ - protected static ViewURLStreamProvider getProvider() { - ComponentSSLConfiguration configuration = ComponentSSLConfiguration.instance(); - org.apache.ambari.server.controller.internal.URLStreamProvider streamProvider = - new org.apache.ambari.server.controller.internal.URLStreamProvider( - DEFAULT_REQUEST_CONNECT_TIMEOUT, DEFAULT_REQUEST_READ_TIMEOUT, - configuration.getTruststorePath(), - configuration.getTruststorePassword(), - configuration.getTruststoreType()); - return new ViewURLStreamProvider(streamProvider); - } - - protected AppCookieManager getAppCookieManager() { - return streamProvider.getAppCookieManager(); - } - } - // ----- Inner class : ParameterResolver ------------------------------- /** http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java index 1f5db9a..c4da8b4 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java @@ -27,7 +27,10 @@ import org.apache.ambari.server.api.resources.SubResourceDefinition; import org.apache.ambari.server.api.resources.ViewExternalSubResourceDefinition; import org.apache.ambari.server.api.services.ViewExternalSubResourceService; import org.apache.ambari.server.api.services.ViewSubResourceService; +import org.apache.ambari.server.configuration.ComponentSSLConfiguration; import org.apache.ambari.server.configuration.Configuration; +import org.apache.ambari.server.controller.AmbariServer; +import org.apache.ambari.server.controller.AmbariSessionManager; import org.apache.ambari.server.controller.spi.Resource; import org.apache.ambari.server.controller.spi.ResourceProvider; import org.apache.ambari.server.orm.dao.MemberDAO; @@ -102,6 +105,8 @@ public class ViewRegistry { private static final String EXTRACTED_ARCHIVES_DIR = "work"; private static final String EXTRACT_COMMAND = "extract"; private static final String ALL_VIEWS_REG_EXP = ".*"; + protected static final int DEFAULT_REQUEST_CONNECT_TIMEOUT = 5000; + protected static final int DEFAULT_REQUEST_READ_TIMEOUT = 10000; /** * Thread pool @@ -219,6 +224,12 @@ public class ViewRegistry { @Inject ViewArchiveUtility archiveUtility; + /** + * The Ambari session manager. + */ + @Inject + AmbariSessionManager ambariSessionManager; + // ----- ViewRegistry ------------------------------------------------------ @@ -1370,4 +1381,38 @@ public class ViewRegistry { } return executorService; } + + /** + * Factory method to create a view URL stream provider. + * + * @return a new view URL stream provider + */ + protected ViewURLStreamProvider createURLStreamProvider() { + ComponentSSLConfiguration configuration1 = ComponentSSLConfiguration.instance(); + org.apache.ambari.server.controller.internal.URLStreamProvider streamProvider = + new org.apache.ambari.server.controller.internal.URLStreamProvider( + DEFAULT_REQUEST_CONNECT_TIMEOUT, + DEFAULT_REQUEST_READ_TIMEOUT, + configuration1.getTruststorePath(), + configuration1.getTruststorePassword(), + configuration1.getTruststoreType()); + return new ViewURLStreamProvider(streamProvider); + } + + /** + * Factory method to create a view Ambari stream provider. + * + * @return a new view Ambari stream provider + */ + protected ViewAmbariStreamProvider createAmbariStreamProvider() { + ComponentSSLConfiguration configuration1 = ComponentSSLConfiguration.instance(); + org.apache.ambari.server.controller.internal.URLStreamProvider streamProvider = + new org.apache.ambari.server.controller.internal.URLStreamProvider( + DEFAULT_REQUEST_CONNECT_TIMEOUT, + DEFAULT_REQUEST_READ_TIMEOUT, + configuration1.getTruststorePath(), + configuration1.getTruststorePassword(), + configuration1.getTruststoreType()); + return new ViewAmbariStreamProvider(streamProvider, ambariSessionManager, AmbariServer.getController()); + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-server/src/main/java/org/apache/ambari/server/view/ViewURLStreamProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewURLStreamProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewURLStreamProvider.java new file mode 100644 index 0000000..87e2753 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewURLStreamProvider.java @@ -0,0 +1,78 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.view; + +import org.apache.ambari.server.controller.internal.AppCookieManager; +import org.apache.ambari.server.controller.internal.URLStreamProvider; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Wrapper around an internal URL stream provider. + */ +public class ViewURLStreamProvider implements org.apache.ambari.view.URLStreamProvider { + /** + * Internal stream provider. + */ + private final URLStreamProvider streamProvider; + + + // ----- Constructor ----------------------------------------------------- + + /** + * Construct a view URL stream provider. + * + * @param streamProvider the underlying stream provider + */ + protected ViewURLStreamProvider(URLStreamProvider streamProvider) { + this.streamProvider = streamProvider; + } + + + // ----- URLStreamProvider ----------------------------------------------- + + @Override + public InputStream readFrom(String spec, String requestMethod, String params, Map<String, String> headers) + throws IOException { + // adapt the headers to the internal URLStreamProvider processURL signature + Map<String, List<String>> headerMap = new HashMap<String, List<String>>(); + for (Map.Entry<String, String> entry : headers.entrySet()) { + headerMap.put(entry.getKey(), Collections.singletonList(entry.getValue())); + } + return streamProvider.processURL(spec, requestMethod, params, headerMap).getInputStream(); + } + + + // ----- helper methods -------------------------------------------------- + + /** + * Get the associated app cookie manager + * + * @return get the app cookie manager + */ + protected AppCookieManager getAppCookieManager() { + return streamProvider.getAppCookieManager(); + } +} + http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/URLStreamProviderTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/URLStreamProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/URLStreamProviderTest.java new file mode 100644 index 0000000..ebed4db --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/URLStreamProviderTest.java @@ -0,0 +1,83 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.controller.internal; + +import org.junit.Assert; +import org.junit.Test; + +import java.net.HttpURLConnection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.easymock.EasyMock.createNiceMock; +import static org.easymock.EasyMock.createMockBuilder; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; + +/** + * URLStreamProvider tests. + */ +public class URLStreamProviderTest { + + @Test + public void testProcessURL() throws Exception { + HttpURLConnection connection = createNiceMock(HttpURLConnection.class); + AppCookieManager appCookieManager = createNiceMock(AppCookieManager.class); + + URLStreamProvider urlStreamProvider = createMockBuilder(URLStreamProvider.class). + withConstructor(Integer.TYPE, Integer.TYPE, String.class, String.class, String.class). + withArgs(1000, 1000, "path", "password", "type"). + addMockedMethod("getAppCookieManager"). + addMockedMethod("getConnection", String.class). + createMock(); + + expect(urlStreamProvider.getAppCookieManager()).andReturn(appCookieManager).anyTimes(); + expect(urlStreamProvider.getConnection("spec")).andReturn(connection); + + Map<String, List<String>> headerMap = new HashMap<String, List<String>>(); + headerMap.put("Header1", Collections.singletonList("value")); + headerMap.put("Cookie", Collections.singletonList("FOO=bar")); + + expect(appCookieManager.getCachedAppCookie("spec")).andReturn("APPCOOKIE=abcdef"); + + connection.setConnectTimeout(1000); + connection.setReadTimeout(1000); + connection.setRequestMethod("GET"); + + connection.setRequestProperty("Header1", "value"); + connection.setRequestProperty("Cookie", "FOO=bar; APPCOOKIE=abcdef"); + + replay(urlStreamProvider, connection, appCookieManager); + + Assert.assertEquals(connection, urlStreamProvider.processURL("spec", "GET", null, headerMap)); + + verify(urlStreamProvider, connection, appCookieManager); + } + + @Test + public void testAppendCookie() throws Exception { + Assert.assertEquals("newCookie", URLStreamProvider.appendCookie(null, "newCookie")); + Assert.assertEquals("newCookie", URLStreamProvider.appendCookie("", "newCookie")); + Assert.assertEquals("oldCookie; newCookie", URLStreamProvider.appendCookie("oldCookie", "newCookie")); + Assert.assertEquals("oldCookie1; oldCookie2; newCookie", URLStreamProvider.appendCookie("oldCookie1; oldCookie2", "newCookie")); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-server/src/test/java/org/apache/ambari/server/view/ViewAmbariStreamProviderTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewAmbariStreamProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewAmbariStreamProviderTest.java new file mode 100644 index 0000000..94f5e86 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewAmbariStreamProviderTest.java @@ -0,0 +1,73 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.view; + +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.AmbariSessionManager; +import org.apache.ambari.server.controller.internal.URLStreamProvider; +import org.junit.Assert; +import org.junit.Test; + +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.easymock.EasyMock.createNiceMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; + +public class ViewAmbariStreamProviderTest { + + @Test + public void testReadFrom() throws Exception { + URLStreamProvider streamProvider = createNiceMock(URLStreamProvider.class); + AmbariSessionManager sessionManager = createNiceMock(AmbariSessionManager.class); + AmbariManagementController controller = createNiceMock(AmbariManagementController.class); + + HttpURLConnection urlConnection = createNiceMock(HttpURLConnection.class); + InputStream inputStream = createNiceMock(InputStream.class); + + Map<String, String> headers = new HashMap<String, String>(); + headers.put("header", "headerValue"); + headers.put("Cookie", "FOO=bar"); + + Map<String, List<String>> headerMap = new HashMap<String, List<String>>(); + headerMap.put("header", Collections.singletonList("headerValue")); + headerMap.put("Cookie", Collections.singletonList("FOO=bar; AMBARISESSIONID=abcdefg")); + + expect(sessionManager.getCurrentSessionId()).andReturn("abcdefg"); + expect(sessionManager.getSessionCookie()).andReturn("AMBARISESSIONID"); + + expect(controller.getAmbariServerURI("/spec")).andReturn("http://c6401.ambari.apache.org:8080/spec"); + expect(streamProvider.processURL("http://c6401.ambari.apache.org:8080/spec", "requestMethod", "params", headerMap)).andReturn(urlConnection); + expect(urlConnection.getInputStream()).andReturn(inputStream); + + replay(streamProvider, sessionManager, controller, urlConnection, inputStream); + + ViewAmbariStreamProvider viewAmbariStreamProvider = new ViewAmbariStreamProvider(streamProvider, sessionManager, controller); + + Assert.assertEquals(inputStream, viewAmbariStreamProvider.readFrom("spec", "requestMethod", "params", headers, true)); + + verify(streamProvider, sessionManager, urlConnection, inputStream); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-server/src/test/java/org/apache/ambari/server/view/ViewContextImplTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewContextImplTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewContextImplTest.java index 6bddf08..519d010 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewContextImplTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewContextImplTest.java @@ -18,7 +18,6 @@ package org.apache.ambari.server.view; -import org.apache.ambari.server.controller.internal.URLStreamProvider; import org.apache.ambari.server.controller.spi.Resource; import org.apache.ambari.server.orm.entities.ViewEntity; import org.apache.ambari.server.orm.entities.ViewEntityTest; @@ -30,11 +29,6 @@ import org.apache.ambari.view.ResourceProvider; import org.junit.Assert; import org.junit.Test; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; import java.util.Map; import static org.easymock.EasyMock.createMockBuilder; @@ -165,40 +159,45 @@ public class ViewContextImplTest { ViewEntity viewDefinition = ViewEntityTest.getViewEntity(); ViewInstanceEntity viewInstanceDefinition = new ViewInstanceEntity(viewDefinition, instanceConfig); ViewRegistry viewRegistry = createNiceMock(ViewRegistry.class); + ViewURLStreamProvider urlStreamProvider = createNiceMock(ViewURLStreamProvider.class); ResourceProvider provider = createNiceMock(ResourceProvider.class); Resource.Type type = new Resource.Type("MY_VIEW/myType"); viewInstanceDefinition.addResourceProvider(type, provider); + expect(viewRegistry.createURLStreamProvider()).andReturn(urlStreamProvider); + + replay(viewRegistry); + ViewContextImpl viewContext = new ViewContextImpl(viewInstanceDefinition, viewRegistry); - Assert.assertNotNull(viewContext.getURLStreamProvider()); + Assert.assertEquals(urlStreamProvider, viewContext.getURLStreamProvider()); + + verify(viewRegistry); } @Test - public void testViewURLStreamProvider() throws Exception { - - URLStreamProvider streamProvider = createNiceMock(URLStreamProvider.class); - HttpURLConnection urlConnection = createNiceMock(HttpURLConnection.class); - InputStream inputStream = createNiceMock(InputStream.class); + public void testGetAmbariStreamProvider() throws Exception { + InstanceConfig instanceConfig = InstanceConfigTest.getInstanceConfigs().get(0); + ViewEntity viewDefinition = ViewEntityTest.getViewEntity(); + ViewInstanceEntity viewInstanceDefinition = new ViewInstanceEntity(viewDefinition, instanceConfig); + ViewRegistry viewRegistry = createNiceMock(ViewRegistry.class); + ViewAmbariStreamProvider ambariStreamProvider = createNiceMock(ViewAmbariStreamProvider.class); - Map<String, String> headers = new HashMap<String, String>(); - headers.put("header", "headerValue"); + ResourceProvider provider = createNiceMock(ResourceProvider.class); + Resource.Type type = new Resource.Type("MY_VIEW/myType"); - Map<String, List<String>> headerMap = new HashMap<String, List<String>>(); - headerMap.put("header", Collections.singletonList("headerValue")); + viewInstanceDefinition.addResourceProvider(type, provider); - expect(streamProvider.processURL("spec", "requestMethod", "params", headerMap)).andReturn(urlConnection); - expect(urlConnection.getInputStream()).andReturn(inputStream); + expect(viewRegistry.createAmbariStreamProvider()).andReturn(ambariStreamProvider); - replay(streamProvider, urlConnection, inputStream); + replay(viewRegistry); - ViewContextImpl.ViewURLStreamProvider viewURLStreamProvider = - new ViewContextImpl.ViewURLStreamProvider(streamProvider); + ViewContextImpl viewContext = new ViewContextImpl(viewInstanceDefinition, viewRegistry); - Assert.assertEquals(inputStream, viewURLStreamProvider.readFrom("spec", "requestMethod", "params", headers)); + Assert.assertEquals(ambariStreamProvider, viewContext.getAmbariStreamProvider()); - verify(streamProvider, urlConnection, inputStream); + verify(viewRegistry); } } http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-server/src/test/java/org/apache/ambari/server/view/ViewURLStreamProviderTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewURLStreamProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewURLStreamProviderTest.java new file mode 100644 index 0000000..3241e46 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewURLStreamProviderTest.java @@ -0,0 +1,80 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.view; + +import org.apache.ambari.server.controller.internal.AppCookieManager; +import org.apache.ambari.server.controller.internal.URLStreamProvider; +import org.junit.Assert; +import org.junit.Test; + +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.easymock.EasyMock.createNiceMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; + +public class ViewURLStreamProviderTest { + + @Test + public void testReadFrom() throws Exception { + + URLStreamProvider streamProvider = createNiceMock(URLStreamProvider.class); + HttpURLConnection urlConnection = createNiceMock(HttpURLConnection.class); + InputStream inputStream = createNiceMock(InputStream.class); + + Map<String, String> headers = new HashMap<String, String>(); + headers.put("header", "headerValue"); + + Map<String, List<String>> headerMap = new HashMap<String, List<String>>(); + headerMap.put("header", Collections.singletonList("headerValue")); + + expect(streamProvider.processURL("spec", "requestMethod", "params", headerMap)).andReturn(urlConnection); + expect(urlConnection.getInputStream()).andReturn(inputStream); + + replay(streamProvider, urlConnection, inputStream); + + ViewURLStreamProvider viewURLStreamProvider = new ViewURLStreamProvider(streamProvider); + + Assert.assertEquals(inputStream, viewURLStreamProvider.readFrom("spec", "requestMethod", "params", headers)); + + verify(streamProvider, urlConnection, inputStream); + } + + @Test + public void testGetAppCookieManager() throws Exception { + URLStreamProvider streamProvider = createNiceMock(URLStreamProvider.class); + AppCookieManager appCookieManager = createNiceMock(AppCookieManager.class); + + expect(streamProvider.getAppCookieManager()).andReturn(appCookieManager); + + replay(streamProvider, appCookieManager); + + ViewURLStreamProvider viewURLStreamProvider = new ViewURLStreamProvider(streamProvider); + + Assert.assertEquals(appCookieManager, viewURLStreamProvider.getAppCookieManager()); + + verify(streamProvider, appCookieManager); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-views/src/main/java/org/apache/ambari/view/AmbariStreamProvider.java ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/java/org/apache/ambari/view/AmbariStreamProvider.java b/ambari-views/src/main/java/org/apache/ambari/view/AmbariStreamProvider.java new file mode 100644 index 0000000..0f8367a --- /dev/null +++ b/ambari-views/src/main/java/org/apache/ambari/view/AmbariStreamProvider.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.view; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +/** + * Provider of an input stream for a request to the Ambari server. + */ +public interface AmbariStreamProvider { + /** + * Read from the input stream specified by the given path on the Ambari server. + * + * @param path the String to parse as an Ambari endpoint (e.g. /api/v1/users) + * @param requestMethod the HTTP method (GET,POST,PUT,etc.). + * @param params the body of the request; may be null + * @param headers the headers of the request; may be null + * @param useAmbariSession indicates that the current Ambari session cookie should be set for the request + * + * @return the input stream + * + * @throws java.io.IOException if an error occurred connecting to the server + */ + public InputStream readFrom(String path, String requestMethod, String params, + Map<String, String> headers, boolean useAmbariSession) + throws IOException; +} http://git-wip-us.apache.org/repos/asf/ambari/blob/439a0a40/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java b/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java index 97385d3..1897a0c 100644 --- a/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java +++ b/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java @@ -20,7 +20,6 @@ package org.apache.ambari.view; import java.util.Collection; import java.util.Map; -import org.apache.ambari.view.HttpImpersonator; /** * Context object available to the view components to provide access to @@ -149,6 +148,13 @@ public interface ViewContext { public URLStreamProvider getURLStreamProvider(); /** + * Get an Ambari stream provider. + * + * @return an Ambari stream provider + */ + public AmbariStreamProvider getAmbariStreamProvider(); + + /** * Get a data store for view persistence entities. * * @return a data store; null if no instance is associated
