This is an automated email from the ASF dual-hosted git repository. pdallig pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/zeppelin.git
The following commit(s) were added to refs/heads/master by this push: new 55a614ad16 [ZEPPELIN-5933] Polish jetty (#4621) 55a614ad16 is described below commit 55a614ad16d220842324f425a159cf41612d76f6 Author: Philipp Dallig <philipp.dal...@gmail.com> AuthorDate: Tue Jun 27 09:52:53 2023 +0200 [ZEPPELIN-5933] Polish jetty (#4621) * Rewrite index.html handling * Polish initialize of the Rest API * Don't break the WebApp Classloader Isolation * Use default SessionHandler * Add simple session tests --- .../apache/zeppelin/server/HtmlAddonResource.java | 173 --------------------- .../apache/zeppelin/server/IndexHtmlServlet.java | 89 +++++++++++ .../apache/zeppelin/server/RestApiApplication.java | 59 +++++++ .../org/apache/zeppelin/server/ZeppelinServer.java | 54 +------ .../apache/zeppelin/rest/SessionRestApiTest.java | 84 ++++++++++ .../zeppelin/server/HtmlAddonResourceTest.java | 71 --------- .../zeppelin/server/IndexHtmlServletTest.java | 111 +++++++++++++ zeppelin-web-angular/WEB-INF/web.xml | 22 +-- zeppelin-web/src/WEB-INF/web.xml | 9 -- 9 files changed, 352 insertions(+), 320 deletions(-) diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/HtmlAddonResource.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/HtmlAddonResource.java deleted file mode 100644 index f504bf2007..0000000000 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/server/HtmlAddonResource.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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.zeppelin.server; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.eclipse.jetty.util.resource.Resource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Resource for enabling html addons in index.html. - */ -public class HtmlAddonResource extends Resource { - - private static final Logger LOGGER = LoggerFactory.getLogger(HtmlAddonResource.class); - - private static final String TAG_BODY_OPENING = "<body"; // ignore bracket here to support potential html attributes - private static final String TAG_BODY_CLOSING = "</body>"; - private static final String TAG_HEAD_CLOSING = "</head>"; - private static final String TAG_HTML_CLOSING = "</html>"; - - public static final String HTML_ADDON_IDENTIFIER = "zeppelin-index-with-addon"; - public static final String INDEX_HTML_PATH = "/index.html"; - - private final Resource indexResource; - private File alteredTempFile = null; - private byte[] alteredContent; - - public HtmlAddonResource(final Resource indexResource, final String bodyAddon, final String headAddon) { - LOGGER.info("Enabling html addons in {}: body='{}' head='{}'", indexResource, bodyAddon, headAddon); - this.indexResource = indexResource; - try { - // read original content from resource - String content = IOUtils.toString(indexResource.getInputStream(), StandardCharsets.UTF_8); - - // process body addon - if (bodyAddon != null) { - if (content.contains(TAG_BODY_CLOSING)) { - content = content.replace(TAG_BODY_CLOSING, bodyAddon + TAG_BODY_CLOSING); - } else if (content.contains(TAG_HTML_CLOSING)) { - content = content.replace(TAG_HTML_CLOSING, bodyAddon + TAG_HTML_CLOSING); - } else { - content = content + bodyAddon; - } - } - - // process head addon - if (headAddon != null) { - if (content.contains(TAG_HEAD_CLOSING)) { - content = content.replace(TAG_HEAD_CLOSING, headAddon + TAG_HEAD_CLOSING); - } else if (content.contains(TAG_BODY_OPENING)) { - content = content.replace(TAG_BODY_OPENING, headAddon + TAG_BODY_OPENING); - } else { - LOGGER.error("Unable to process Head html addon. Could not find proper anchor in index.html."); - } - } - - this.alteredContent = content.getBytes(StandardCharsets.UTF_8); - - // only relevant in development mode: create altered temp file (as zeppelin web archives are addressed via local - // filesystem folders) - if (indexResource.getFile() != null) { - this.alteredTempFile = File.createTempFile(HTML_ADDON_IDENTIFIER, ".html"); - this.alteredTempFile.deleteOnExit(); - FileUtils.writeByteArrayToFile(this.alteredTempFile, this.alteredContent); - } - - } catch (IOException e) { - LOGGER.error("Error initializing html addons.", e); - } - - } - - @Override - public File getFile() throws IOException { - return this.alteredTempFile; - } - - @Override - public InputStream getInputStream() throws IOException { - return new ByteArrayInputStream(this.alteredContent); - } - - @Override - public String getName() { - return indexResource.getName(); - } - - @Override - public boolean isContainedIn(Resource r) throws MalformedURLException { - return indexResource.isContainedIn(r); - } - - @Override - public boolean exists() { - return indexResource.exists(); - } - - @Override - public boolean isDirectory() { - return indexResource.isDirectory(); - } - - @Override - public long lastModified() { - return indexResource.lastModified(); - } - - @Override - public long length() { - return alteredContent.length; - } - - @Override - public URL getURL() { - return indexResource.getURL(); - } - - @Override - public ReadableByteChannel getReadableByteChannel() throws IOException { - return Channels.newChannel(new ByteArrayInputStream(this.alteredContent)); - } - - @Override - public boolean delete() throws SecurityException { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public boolean renameTo(Resource dest) throws SecurityException { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public String[] list() { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public Resource addPath(String path) throws IOException, MalformedURLException { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public void close() { - } - -} diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/IndexHtmlServlet.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/IndexHtmlServlet.java new file mode 100644 index 0000000000..10e7cf9d55 --- /dev/null +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/IndexHtmlServlet.java @@ -0,0 +1,89 @@ +/* + * 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.zeppelin.server; + +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.apache.zeppelin.conf.ZeppelinConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class IndexHtmlServlet extends HttpServlet { + + private static final Logger LOGGER = LoggerFactory.getLogger(IndexHtmlServlet.class); + + /** + * + */ + private static final long serialVersionUID = 1L; + + private static final String TAG_BODY_OPENING = "<body"; // ignore bracket here to support potential html attributes + private static final String TAG_BODY_CLOSING = "</body>"; + private static final String TAG_HEAD_CLOSING = "</head>"; + private static final String TAG_HTML_CLOSING = "</html>"; + + final String bodyAddon; + final String headAddon; + + public IndexHtmlServlet(ZeppelinConfiguration conf) { + this.bodyAddon = conf.getHtmlBodyAddon(); + this.headAddon = conf.getHtmlHeadAddon(); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + try { + URL indexResource = getServletContext().getResource("/index.html"); + String content = IOUtils.toString(indexResource, StandardCharsets.UTF_8); + // read original content from resource + if (bodyAddon != null) { + if (content.contains(TAG_BODY_CLOSING)) { + content = content.replace(TAG_BODY_CLOSING, bodyAddon + TAG_BODY_CLOSING); + } else if (content.contains(TAG_HTML_CLOSING)) { + content = content.replace(TAG_HTML_CLOSING, bodyAddon + TAG_HTML_CLOSING); + } else { + content = content + bodyAddon; + } + } + // process head addon + if (headAddon != null) { + if (content.contains(TAG_HEAD_CLOSING)) { + content = content.replace(TAG_HEAD_CLOSING, headAddon + TAG_HEAD_CLOSING); + } else if (content.contains(TAG_BODY_OPENING)) { + content = content.replace(TAG_BODY_OPENING, headAddon + TAG_BODY_OPENING); + } else { + LOGGER.error( + "Unable to process Head html addon. Could not find proper anchor in index.html."); + } + } + resp.setContentType("text/html"); + resp.setStatus(HttpServletResponse.SC_OK); + resp.getWriter().append(content); + } catch (IOException e) { + LOGGER.error("Error rendering index.html.", e); + } + } +} diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/RestApiApplication.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/RestApiApplication.java new file mode 100644 index 0000000000..56e29e8742 --- /dev/null +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/RestApiApplication.java @@ -0,0 +1,59 @@ +/* + * 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.zeppelin.server; + +import java.util.HashSet; +import java.util.Set; + +import javax.ws.rs.core.Application; + +import org.apache.zeppelin.rest.AdminRestApi; +import org.apache.zeppelin.rest.ClusterRestApi; +import org.apache.zeppelin.rest.ConfigurationsRestApi; +import org.apache.zeppelin.rest.CredentialRestApi; +import org.apache.zeppelin.rest.HeliumRestApi; +import org.apache.zeppelin.rest.InterpreterRestApi; +import org.apache.zeppelin.rest.LoginRestApi; +import org.apache.zeppelin.rest.NotebookRepoRestApi; +import org.apache.zeppelin.rest.NotebookRestApi; +import org.apache.zeppelin.rest.SecurityRestApi; +import org.apache.zeppelin.rest.SessionRestApi; +import org.apache.zeppelin.rest.ZeppelinRestApi; +import org.apache.zeppelin.rest.exception.WebApplicationExceptionMapper; + +public class RestApiApplication extends Application { + @Override + public Set<Class<?>> getClasses() { + Set<Class<?>> s = new HashSet<>(); + s.add(AdminRestApi.class); + s.add(ClusterRestApi.class); + s.add(ConfigurationsRestApi.class); + s.add(CredentialRestApi.class); + s.add(HeliumRestApi.class); + s.add(InterpreterRestApi.class); + s.add(LoginRestApi.class); + s.add(NotebookRepoRestApi.class); + s.add(NotebookRestApi.class); + s.add(SecurityRestApi.class); + s.add(SessionRestApi.class); + s.add(ZeppelinRestApi.class); + + // add ExceptionMapper + s.add(WebApplicationExceptionMapper.class); + return s; + } +} diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java index ae6846d554..86322ebb3b 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java @@ -20,8 +20,6 @@ import com.codahale.metrics.servlets.HealthCheckServlet; import com.codahale.metrics.servlets.PingServlet; import com.google.gson.Gson; -import static org.apache.zeppelin.server.HtmlAddonResource.HTML_ADDON_IDENTIFIER; - import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Tags; @@ -111,11 +109,8 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.ContextHandlerCollection; -import org.eclipse.jetty.server.session.SessionHandler; -import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer; @@ -125,13 +120,12 @@ import org.glassfish.hk2.api.ServiceLocator; import org.glassfish.hk2.api.ServiceLocatorFactory; import org.glassfish.hk2.utilities.ServiceLocatorUtilities; import org.glassfish.hk2.utilities.binding.AbstractBinder; -import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.servlet.ServletProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** Main class of Zeppelin. */ -public class ZeppelinServer extends ResourceConfig { +public class ZeppelinServer { private static final Logger LOG = LoggerFactory.getLogger(ZeppelinServer.class); private static final String WEB_APP_CONTEXT_NEXT = "/next"; @@ -141,8 +135,6 @@ public class ZeppelinServer extends ResourceConfig { public ZeppelinServer(ZeppelinConfiguration conf) { LOG.info("Instantiated ZeppelinServer"); InterpreterOutput.LIMIT = conf.getInt(ConfVars.ZEPPELIN_INTERPRETER_OUTPUT_LIMIT); - - packages("org.apache.zeppelin.rest"); } public static void main(String[] args) throws IOException { @@ -549,9 +541,8 @@ public class ZeppelinServer extends ResourceConfig { final ServletHolder servletHolder = new ServletHolder(new org.glassfish.jersey.servlet.ServletContainer()); - servletHolder.setInitParameter("javax.ws.rs.Application", ZeppelinServer.class.getName()); + servletHolder.setInitParameter("javax.ws.rs.Application", RestApiApplication.class.getName()); servletHolder.setName("rest"); - webapp.setSessionHandler(new SessionHandler()); webapp.addServlet(servletHolder, "/api/*"); String shiroIniPath = conf.getShiroPath(); @@ -584,7 +575,6 @@ public class ZeppelinServer extends ResourceConfig { // Development mode, read from FS // webApp.setDescriptor(warPath+"/WEB-INF/web.xml"); webApp.setResourceBase(warFile.getPath()); - webApp.setParentLoaderPriority(true); } else { // use packaged WAR webApp.setWar(warFile.getAbsolutePath()); @@ -595,7 +585,7 @@ public class ZeppelinServer extends ResourceConfig { webApp.setTempDirectory(warTempDirectory); } // Explicit bind to root - webApp.addServlet(new ServletHolder(setupServlet(webApp, conf)), "/*"); + webApp.addServlet(new ServletHolder(new IndexHtmlServlet(conf)), "/index.html"); contexts.addHandler(webApp); webApp.addFilter(new FilterHolder(CorsFilter.class), "/*", EnumSet.allOf(DispatcherType.class)); @@ -606,44 +596,6 @@ public class ZeppelinServer extends ResourceConfig { return webApp; } - private static DefaultServlet setupServlet( - WebAppContext webApp, - ZeppelinConfiguration conf) { - - // provide DefaultServlet as is in case html addon is not used - if (conf.getHtmlBodyAddon()==null && conf.getHtmlHeadAddon()==null) { - return new DefaultServlet(); - } - - // override ResourceFactory interface part of DefaultServlet for intercepting the static index.html properly. - return new DefaultServlet() { - - private static final long serialVersionUID = 1L; - - @Override - public Resource getResource(String pathInContext) { - - // proceed for everything but '/index.html' - if (!HtmlAddonResource.INDEX_HTML_PATH.equals(pathInContext)) { - return super.getResource(pathInContext); - } - - // create the altered 'index.html' resource and cache it via webapp attributes - if (webApp.getAttribute(HTML_ADDON_IDENTIFIER) == null) { - webApp.setAttribute( - HTML_ADDON_IDENTIFIER, - new HtmlAddonResource( - super.getResource(pathInContext), - conf.getHtmlBodyAddon(), - conf.getHtmlHeadAddon())); - } - - return (Resource) webApp.getAttribute(HTML_ADDON_IDENTIFIER); - } - - }; - } - private static void initWebApp(WebAppContext webApp, ZeppelinConfiguration conf, ServiceLocator sharedServiceLocator, PrometheusMeterRegistry promMetricRegistry) { webApp.addEventListener( new ServletContextListener() { diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SessionRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SessionRestApiTest.java new file mode 100644 index 0000000000..dfca0202dd --- /dev/null +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SessionRestApiTest.java @@ -0,0 +1,84 @@ +/* + * 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.zeppelin.rest; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.util.EntityUtils; +import org.apache.zeppelin.common.SessionInfo; +import org.apache.zeppelin.server.JsonResponse; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import javax.ws.rs.core.Response.Status; + +class SessionRestApiTest extends AbstractTestRestApi { + Gson gson = new Gson(); + + @BeforeAll + static void init() throws Exception { + AbstractTestRestApi.startUp(SessionRestApi.class.getSimpleName()); + } + + @AfterAll + static void destroy() throws Exception { + AbstractTestRestApi.shutDown(); + } + + @Test + void testGetNotAvailableSession() throws IOException { + try (CloseableHttpResponse get = httpGet("/session/testSession")) { + assertEquals(Status.NOT_FOUND.getStatusCode(), get.getStatusLine().getStatusCode()); + } + } + + @Test + void testStartAndStopSession() throws IOException { + String interpreter = "testInterpreter"; + try (CloseableHttpResponse post = httpPost("/session?interpreter=" + interpreter, "")) { + assertEquals(Status.OK.getStatusCode(), post.getStatusLine().getStatusCode()); + Type collectionType = new TypeToken<JsonResponse<SessionInfo>>() { + }.getType(); + JsonResponse<SessionInfo> resp = gson + .fromJson(EntityUtils.toString(post.getEntity(), StandardCharsets.UTF_8), collectionType); + SessionInfo info = resp.getBody(); + + // Get by interpreter name + try (CloseableHttpResponse get = httpGet("/session?interpreter" + interpreter)) { + assertEquals(Status.OK.getStatusCode(), get.getStatusLine().getStatusCode()); + } + + // Get by sessionId + try (CloseableHttpResponse get = httpGet("/session/" + info.getSessionId())) { + assertEquals(Status.OK.getStatusCode(), get.getStatusLine().getStatusCode()); + } + + // Delete session + try (CloseableHttpResponse delete = httpDelete("/session/" + info.getSessionId())) { + assertEquals(Status.OK.getStatusCode(), delete.getStatusLine().getStatusCode()); + } + } + + } +} diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/server/HtmlAddonResourceTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/server/HtmlAddonResourceTest.java deleted file mode 100644 index 65d51d9fe7..0000000000 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/server/HtmlAddonResourceTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.zeppelin.server; - -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.MatcherAssert.assertThat; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.io.IOUtils; -import org.eclipse.jetty.util.resource.Resource; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -class HtmlAddonResourceTest { - - private final static String TEST_BODY_ADDON = "<!-- foo -->"; - private final static String TEST_HEAD_ADDON = "<!-- bar -->"; - - private final static String FILE_PATH_INDEX_HTML_ZEPPELIN_WEB = "../zeppelin-web/dist/index.html"; - private final static String FILE_PATH_INDEX_HTML_ZEPPELIN_WEB_ANGULAR = "../zeppelin-web-angular/dist/zeppelin/index.html"; - - @Test - void testZeppelinWebHtmlAddon() throws IOException { - final Resource addonResource = getHtmlAddonResource(FILE_PATH_INDEX_HTML_ZEPPELIN_WEB); - - final String content = IOUtils.toString(addonResource.getInputStream(), StandardCharsets.UTF_8); - - assertThat(content, containsString(TEST_BODY_ADDON)); - assertThat(content, containsString(TEST_HEAD_ADDON)); - - } - - @Test - @Disabled // ignored due to zeppelin-web-angular not build for core tests - void testZeppelinWebAngularHtmlAddon() throws IOException { - final Resource addonResource = getHtmlAddonResource(FILE_PATH_INDEX_HTML_ZEPPELIN_WEB_ANGULAR); - - final String content = IOUtils.toString(addonResource.getInputStream(), StandardCharsets.UTF_8); - - assertThat(content, containsString(TEST_BODY_ADDON)); - assertThat(content, containsString(TEST_HEAD_ADDON)); - - } - - private Resource getHtmlAddonResource(final String indexHtmlPath) { - return getHtmlAddonResource(indexHtmlPath, TEST_BODY_ADDON, TEST_HEAD_ADDON); - } - - private Resource getHtmlAddonResource(final String indexHtmlPath, final String bodyAddon, final String headAddon) { - final Resource indexResource = Resource.newResource(new File(indexHtmlPath)); - return new HtmlAddonResource(indexResource, TEST_BODY_ADDON, TEST_HEAD_ADDON); - } - -} diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/server/IndexHtmlServletTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/server/IndexHtmlServletTest.java new file mode 100644 index 0000000000..5e8173df96 --- /dev/null +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/server/IndexHtmlServletTest.java @@ -0,0 +1,111 @@ +/* + * 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.zeppelin.server; + +import static org.hamcrest.Matchers.containsString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URL; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.zeppelin.conf.ZeppelinConfiguration; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +class IndexHtmlServletTest { + + private final static String TEST_BODY_ADDON = "<!-- foo -->"; + private final static String TEST_HEAD_ADDON = "<!-- bar -->"; + + private final static String FILE_PATH_INDEX_HTML_ZEPPELIN_WEB = "../zeppelin-web/dist/index.html"; + private final static String FILE_PATH_INDEX_HTML_ZEPPELIN_WEB_ANGULAR = "../zeppelin-web-angular/dist/zeppelin/index.html"; + + + @Test + void testZeppelinWebHtmlAddon() throws IOException, ServletException { + ZeppelinConfiguration conf = mock(ZeppelinConfiguration.class); + when(conf.getHtmlBodyAddon()).thenReturn(TEST_BODY_ADDON); + when(conf.getHtmlHeadAddon()).thenReturn(TEST_HEAD_ADDON); + + ServletConfig sc = mock(ServletConfig.class); + ServletContext ctx = mock(ServletContext.class); + when(ctx.getResource("/index.html")) + .thenReturn(new URL("file:" + FILE_PATH_INDEX_HTML_ZEPPELIN_WEB)); + when(sc.getServletContext()).thenReturn(ctx); + + IndexHtmlServlet servlet = new IndexHtmlServlet(conf); + servlet.init(sc); + + HttpServletResponse mockResponse = mock(HttpServletResponse.class); + HttpServletRequest mockRequest = mock(HttpServletRequest.class); + + // Catch content in ByteArrayOutputStream + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PrintWriter writer = new PrintWriter(out); + when(mockResponse.getWriter()).thenReturn(writer); + + servlet.doGet(mockRequest, mockResponse); + writer.flush(); + // Get Content + String content = new String(out.toString()); + + assertThat(content, containsString(TEST_BODY_ADDON)); + assertThat(content, containsString(TEST_HEAD_ADDON)); + + } + + @Test + @Disabled("ignored due to zeppelin-web-angular not build for core tests") + void testZeppelinWebAngularHtmlAddon() throws IOException, ServletException { + ZeppelinConfiguration conf = mock(ZeppelinConfiguration.class); + when(conf.getHtmlBodyAddon()).thenReturn(TEST_BODY_ADDON); + when(conf.getHtmlHeadAddon()).thenReturn(TEST_HEAD_ADDON); + + ServletConfig sc = mock(ServletConfig.class); + ServletContext ctx = mock(ServletContext.class); + when(ctx.getResource("/index.html")) + .thenReturn(new URL("file:" + FILE_PATH_INDEX_HTML_ZEPPELIN_WEB_ANGULAR)); + when(sc.getServletContext()).thenReturn(ctx); + + IndexHtmlServlet servlet = new IndexHtmlServlet(conf); + servlet.init(sc); + + HttpServletResponse mockResponse = mock(HttpServletResponse.class); + HttpServletRequest mockRequest = mock(HttpServletRequest.class); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PrintWriter writer = new PrintWriter(out); + when(mockResponse.getWriter()).thenReturn(writer); + + servlet.doGet(mockRequest, mockResponse); + writer.flush(); + // Get Content + String content = new String(out.toString()); + + assertThat(content, containsString(TEST_BODY_ADDON)); + assertThat(content, containsString(TEST_HEAD_ADDON)); + + } +} diff --git a/zeppelin-web-angular/WEB-INF/web.xml b/zeppelin-web-angular/WEB-INF/web.xml index f40bf86173..43524f4f90 100644 --- a/zeppelin-web-angular/WEB-INF/web.xml +++ b/zeppelin-web-angular/WEB-INF/web.xml @@ -12,25 +12,15 @@ --> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" - version="3.0"> + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" + version="3.0"> <display-name>zeppelin-web-angular</display-name> - <servlet> - <servlet-name>default</servlet-name> - <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> - <init-param> - <param-name>jersey.config.server.provider.packages</param-name> - <param-value>org.apache.zeppelin.rest</param-value> - </init-param> - <load-on-startup>1</load-on-startup> - </servlet> - - <context-param> - <param-name>configuration</param-name> - <param-value>deployment</param-value> - </context-param> + <context-param> + <param-name>configuration</param-name> + <param-value>deployment</param-value> + </context-param> <session-config> <cookie-config> diff --git a/zeppelin-web/src/WEB-INF/web.xml b/zeppelin-web/src/WEB-INF/web.xml index f7094028fe..179bd02818 100644 --- a/zeppelin-web/src/WEB-INF/web.xml +++ b/zeppelin-web/src/WEB-INF/web.xml @@ -22,15 +22,6 @@ version="3.0"> <display-name>zeppelin-web</display-name> - <servlet> - <servlet-name>restServlet</servlet-name> - <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> - <init-param> - <param-name>jersey.config.server.provider.packages</param-name> - <param-value>org.apache.zeppelin.rest</param-value> - </init-param> - <load-on-startup>1</load-on-startup> - </servlet> <error-page> <error-code>404</error-code>