From 546a772204719750ddac9b513ad601b37c38fc05 Mon Sep 17 00:00:00 2001 From: Drew Koszewnik Date: Fri, 21 Apr 2017 16:39:29 -0700 Subject: [PATCH 1/9] Added Hollow Explorer --- hollow-diff-ui/build.gradle | 6 +- .../hollow/diff/ui/HollowDiffUIRouter.java | 1 + .../ui/jetty/OptionalDependencyHelper.java | 5 +- .../hollow/history/ui/HollowHistoryUI.java | 2 +- .../ui/jetty/OptionalDependencyHelper.java | 5 +- hollow-explorer-ui/build.gradle | 22 ++ .../hollow/explorer/ui/HollowExplorerUI.java | 92 +++++++ .../ui/jetty/HollowExplorerHandler.java | 43 +++ .../ui/jetty/HollowExplorerUIServer.java | 68 +++++ .../explorer/ui/jetty/JettyBasedUIServer.java | 52 ++++ .../ui/jetty/OptionalDependencyHelper.java | 31 +++ .../hollow/explorer/ui/model/TypeKey.java | 54 ++++ .../explorer/ui/model/TypeOverview.java | 76 ++++++ .../ui/pages/BrowseSelectedTypePage.java | 253 ++++++++++++++++++ .../explorer/ui/pages/HollowExplorerPage.java | 62 +++++ .../explorer/ui/pages/ShowAllTypesPage.java | 107 ++++++++ .../main/resources/browse-selected-type.vm | 116 ++++++++ .../src/main/resources/explorer-footer.vm | 17 ++ .../src/main/resources/explorer-header.vm | 38 +++ .../src/main/resources/show-all-types.vm | 37 +++ hollow-ui-tools/build.gradle | 27 ++ .../netflix/hollow}/ui/HollowUIRouter.java | 2 +- .../AbstractOptionalDependencyHelper.java | 0 .../ui/jetty/OptionalDependencyException.java | 0 settings.gradle | 2 + 25 files changed, 1105 insertions(+), 13 deletions(-) create mode 100644 hollow-explorer-ui/build.gradle create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/HollowExplorerUI.java create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/HollowExplorerHandler.java create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/HollowExplorerUIServer.java create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/JettyBasedUIServer.java create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/OptionalDependencyHelper.java create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/TypeKey.java create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/TypeOverview.java create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSelectedTypePage.java create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/HollowExplorerPage.java create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/ShowAllTypesPage.java create mode 100644 hollow-explorer-ui/src/main/resources/browse-selected-type.vm create mode 100644 hollow-explorer-ui/src/main/resources/explorer-footer.vm create mode 100644 hollow-explorer-ui/src/main/resources/explorer-header.vm create mode 100644 hollow-explorer-ui/src/main/resources/show-all-types.vm create mode 100644 hollow-ui-tools/build.gradle rename {hollow-diff-ui/src/main/java/com/netflix/hollow/diff => hollow-ui-tools/src/main/java/com/netflix/hollow}/ui/HollowUIRouter.java (98%) rename {hollow-diff-ui => hollow-ui-tools}/src/main/java/com/netflix/hollow/ui/jetty/AbstractOptionalDependencyHelper.java (100%) rename {hollow-diff-ui => hollow-ui-tools}/src/main/java/com/netflix/hollow/ui/jetty/OptionalDependencyException.java (100%) diff --git a/hollow-diff-ui/build.gradle b/hollow-diff-ui/build.gradle index 86d229a155..a3b6ef66c6 100644 --- a/hollow-diff-ui/build.gradle +++ b/hollow-diff-ui/build.gradle @@ -9,13 +9,9 @@ sourceSets { dependencies { compile project(':hollow') - compile 'org.apache.velocity:velocity:1.7' - compile 'commons-codec:commons-codec:1.8' - compile 'commons-io:commons-io:2.3' + compile project(':hollow-ui-tools') compile 'com.google.code.gson:gson:2.8.0' - compileOnly 'org.eclipse.jetty:jetty-server:9.4.2.v20170220' - testCompile 'junit:junit:4.11' toolsCompile configurations.compile diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowDiffUIRouter.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowDiffUIRouter.java index dc8570ced6..5d9c4046ce 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowDiffUIRouter.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowDiffUIRouter.java @@ -18,6 +18,7 @@ package com.netflix.hollow.diff.ui; import com.netflix.hollow.tools.diff.HollowDiff; +import com.netflix.hollow.ui.HollowUIRouter; import java.io.IOException; import java.util.HashMap; import java.util.Map; diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/jetty/OptionalDependencyHelper.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/jetty/OptionalDependencyHelper.java index 20a81b6407..84245ce65e 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/jetty/OptionalDependencyHelper.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/jetty/OptionalDependencyHelper.java @@ -17,12 +17,11 @@ */ package com.netflix.hollow.diff.ui.jetty; -import com.netflix.hollow.diff.ui.jetty.HollowDiffUIServer.UIServer; import com.netflix.hollow.ui.jetty.AbstractOptionalDependencyHelper; final class OptionalDependencyHelper extends AbstractOptionalDependencyHelper { - UIServer.Factory uiServerFactory() { - return (UIServer.Factory)newFactory( + HollowDiffUIServer.UIServer.Factory uiServerFactory() { + return (HollowDiffUIServer.UIServer.Factory)newFactory( "com.netflix.hollow.diff.ui.jetty.JettyBasedUIServer$Factory", "org.eclipse.jetty.server.Server", "please add jetty-server (org.eclipse.jetty:jetty-server) to your dependencies"); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/HollowHistoryUI.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/HollowHistoryUI.java index 4de19b3308..66a9115f8f 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/HollowHistoryUI.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/HollowHistoryUI.java @@ -20,7 +20,6 @@ import static com.netflix.hollow.diff.ui.HollowDiffSession.getSession; import com.netflix.hollow.core.index.key.PrimaryKey; -import com.netflix.hollow.diff.ui.HollowUIRouter; import com.netflix.hollow.diffview.DiffViewOutputGenerator; import com.netflix.hollow.diffview.HollowHistoryViewProvider; import com.netflix.hollow.diffview.HollowObjectViewProvider; @@ -36,6 +35,7 @@ import com.netflix.hollow.history.ui.pages.HistoryStateTypeExpandGroupPage; import com.netflix.hollow.history.ui.pages.HistoryStateTypePage; import com.netflix.hollow.tools.history.HollowHistory; +import com.netflix.hollow.ui.HollowUIRouter; import java.io.IOException; import java.util.HashMap; import java.util.Map; diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/jetty/OptionalDependencyHelper.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/jetty/OptionalDependencyHelper.java index 4207fa5cfa..b132609bd0 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/jetty/OptionalDependencyHelper.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/jetty/OptionalDependencyHelper.java @@ -17,12 +17,11 @@ */ package com.netflix.hollow.history.ui.jetty; -import com.netflix.hollow.history.ui.jetty.HollowHistoryUIServer.UIServer; import com.netflix.hollow.ui.jetty.AbstractOptionalDependencyHelper; final class OptionalDependencyHelper extends AbstractOptionalDependencyHelper { - UIServer.Factory historyUIServerFactory() { - return (UIServer.Factory)newFactory( + HollowHistoryUIServer.UIServer.Factory historyUIServerFactory() { + return (HollowHistoryUIServer.UIServer.Factory)newFactory( "com.netflix.hollow.history.ui.jetty.JettyBasedUIServer$Factory", "org.eclipse.jetty.server.Server", "please add jetty-server (org.eclipse.jetty:jetty-server) to your dependencies"); diff --git a/hollow-explorer-ui/build.gradle b/hollow-explorer-ui/build.gradle new file mode 100644 index 0000000000..fdb70fbe4b --- /dev/null +++ b/hollow-explorer-ui/build.gradle @@ -0,0 +1,22 @@ +apply plugin: 'java' + +sourceSets { + tools { + compileClasspath += main.output + runtimeClasspath += main.output + } +} + +dependencies { + compile project(':hollow') + compile project(':hollow-ui-tools') + + testCompile 'junit:junit:4.11' + + toolsCompile configurations.compile + toolsRuntime configurations.runtime +} + +configurations { + toolsCompile.extendsFrom compile +} diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/HollowExplorerUI.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/HollowExplorerUI.java new file mode 100644 index 0000000000..15147a0f01 --- /dev/null +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/HollowExplorerUI.java @@ -0,0 +1,92 @@ +/* + * + * Copyright 2017 Netflix, Inc. + * + * Licensed 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 com.netflix.hollow.explorer.ui; + +import com.netflix.hollow.api.client.HollowClient; + +import com.netflix.hollow.explorer.ui.pages.BrowseSelectedTypePage; +import com.netflix.hollow.core.read.engine.HollowReadStateEngine; +import com.netflix.hollow.explorer.ui.pages.ShowAllTypesPage; +import com.netflix.hollow.ui.HollowUIRouter; +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class HollowExplorerUI extends HollowUIRouter { + + private final HollowClient client; + private final HollowReadStateEngine stateEngine; + + private String headerDisplayString; + + private final ShowAllTypesPage showAllTypesPage; + private final BrowseSelectedTypePage browseTypePage; + + public HollowExplorerUI(String baseUrlPath, HollowClient client) { + this(baseUrlPath, client, null); + } + + public HollowExplorerUI(String baseUrlPath, HollowReadStateEngine stateEngine) { + this(baseUrlPath, null, stateEngine); + } + + private HollowExplorerUI(String baseUrlPath, HollowClient client, HollowReadStateEngine stateEngine) { + super(baseUrlPath); + this.client = client; + this.stateEngine = stateEngine; + + this.showAllTypesPage = new ShowAllTypesPage(this); + this.browseTypePage = new BrowseSelectedTypePage(this); + } + + public boolean handle(String target, HttpServletRequest req, HttpServletResponse resp) throws IOException { + + String pageName = getTargetRootPath(target); + + if("".equals(pageName) || "home".equals(pageName)) { + showAllTypesPage.render(req, resp.getWriter()); + return true; + } else if("type".equals(pageName)) { + browseTypePage.render(req, resp.getWriter()); + return true; + } + + return false; + } + + public long getCurrentStateVersion() { + if(client == null) + return Long.MIN_VALUE; + return client.getCurrentVersionId(); + } + + public HollowReadStateEngine getStateEngine() { + if(client == null) + return stateEngine; + return client.getStateEngine(); + } + + public void setHeaderDisplayString(String str) { + this.headerDisplayString = str; + } + + public String getHeaderDisplayString() { + return headerDisplayString; + } + +} diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/HollowExplorerHandler.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/HollowExplorerHandler.java new file mode 100644 index 0000000000..53e3b2e8d0 --- /dev/null +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/HollowExplorerHandler.java @@ -0,0 +1,43 @@ +/* + * + * Copyright 2017 Netflix, Inc. + * + * Licensed 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 com.netflix.hollow.explorer.ui.jetty; + +import com.netflix.hollow.explorer.ui.HollowExplorerUI; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; + +public class HollowExplorerHandler extends AbstractHandler { + + private final HollowExplorerUI ui; + + public HollowExplorerHandler(HollowExplorerUI ui) { + this.ui = ui; + } + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + if(ui.handle(target, request, response)) + baseRequest.setHandled(true); + } + +} diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/HollowExplorerUIServer.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/HollowExplorerUIServer.java new file mode 100644 index 0000000000..8c50268671 --- /dev/null +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/HollowExplorerUIServer.java @@ -0,0 +1,68 @@ +/* + * + * Copyright 2016 Netflix, Inc. + * + * Licensed 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 com.netflix.hollow.explorer.ui.jetty; + +import com.netflix.hollow.api.client.HollowClient; +import com.netflix.hollow.core.read.engine.HollowReadStateEngine; +import com.netflix.hollow.explorer.ui.HollowExplorerUI; + +public class HollowExplorerUIServer { + private static final UIServer.Factory FACTORY = new OptionalDependencyHelper().explorerUIServerFactory(); + + private final UIServer server; + private final HollowExplorerUI ui; + + public HollowExplorerUIServer(HollowReadStateEngine readEngine, int port) { + this(new HollowExplorerUI("", readEngine), port); + } + + public HollowExplorerUIServer(HollowClient client, int port) { + this(new HollowExplorerUI("", client), port); + } + + public HollowExplorerUIServer(HollowExplorerUI ui, int port) { + this.server = FACTORY.newServer(ui, port); + this.ui = ui; + } + + public void start() throws Exception { + server.start(); + } + + public void stop() throws Exception { + server.stop(); + } + + public void join() throws InterruptedException { + server.join(); + } + + public HollowExplorerUI getUI() { + return ui; + } + + static interface UIServer { + void start() throws Exception; + void stop() throws Exception; + void join() throws InterruptedException; + + static interface Factory { + UIServer newServer(HollowExplorerUI ui, int port); + } + } +} diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/JettyBasedUIServer.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/JettyBasedUIServer.java new file mode 100644 index 0000000000..153c766e5f --- /dev/null +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/JettyBasedUIServer.java @@ -0,0 +1,52 @@ +/* + * + * Copyright 2017 Netflix, Inc. + * + * Licensed 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 com.netflix.hollow.explorer.ui.jetty; + +import com.netflix.hollow.explorer.ui.HollowExplorerUI; +import com.netflix.hollow.explorer.ui.jetty.HollowExplorerUIServer.UIServer; +import org.eclipse.jetty.server.Server; + +final class JettyBasedUIServer implements UIServer { + private final Server server; + private final HollowExplorerHandler handler; + + private JettyBasedUIServer(HollowExplorerUI ui, int port) { + this.server = new Server(port); + this.handler = new HollowExplorerHandler(ui); + } + + public void start() throws Exception { + server.setHandler(handler); + server.start(); + } + + public void stop() throws Exception { + server.stop(); + } + + public void join() throws InterruptedException { + server.join(); + } + + public static final class Factory implements UIServer.Factory { + @Override + public UIServer newServer(HollowExplorerUI ui, int port) { + return new JettyBasedUIServer(ui, port); + } + } +} diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/OptionalDependencyHelper.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/OptionalDependencyHelper.java new file mode 100644 index 0000000000..2f27f4e439 --- /dev/null +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/OptionalDependencyHelper.java @@ -0,0 +1,31 @@ +/* + * + * Copyright 2017 Netflix, Inc. + * + * Licensed 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 com.netflix.hollow.explorer.ui.jetty; + +import com.netflix.hollow.ui.jetty.AbstractOptionalDependencyHelper; + +final class OptionalDependencyHelper extends AbstractOptionalDependencyHelper { + HollowExplorerUIServer.UIServer.Factory explorerUIServerFactory() { + return (HollowExplorerUIServer.UIServer.Factory)newFactory( + "com.netflix.hollow.explorer.ui.jetty.JettyBasedUIServer$Factory", + "org.eclipse.jetty.server.Server", + "please add jetty-server (org.eclipse.jetty:jetty-server) to your dependencies"); + } + + OptionalDependencyHelper() {} +} diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/TypeKey.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/TypeKey.java new file mode 100644 index 0000000000..633f7b7d60 --- /dev/null +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/TypeKey.java @@ -0,0 +1,54 @@ +/* + * + * Copyright 2017 Netflix, Inc. + * + * Licensed 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 com.netflix.hollow.explorer.ui.model; + +public class TypeKey { + + private final int idx; + private final String keyStr; + private final String keyDisplayStr; + private final int ordinal; + + public TypeKey(int idx, int ordinal, String keyStr) { + this(idx, ordinal, keyStr, keyStr); + } + + public TypeKey(int idx, int ordinal, String keyStr, String keyDisplayStr) { + this.idx = idx; + this.ordinal = ordinal; + this.keyStr = keyStr; + this.keyDisplayStr = keyDisplayStr; + } + + public int getIdx() { + return idx; + } + + public int getOrdinal() { + return ordinal; + } + + public String getKey() { + return keyStr; + } + + public String getKeyDisplay() { + return keyDisplayStr; + } + +} diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/TypeOverview.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/TypeOverview.java new file mode 100644 index 0000000000..b2061a3bd9 --- /dev/null +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/TypeOverview.java @@ -0,0 +1,76 @@ +/* + * + * Copyright 2017 Netflix, Inc. + * + * Licensed 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 com.netflix.hollow.explorer.ui.model; + +import java.text.DecimalFormat; + +import java.text.NumberFormat; +import com.netflix.hollow.core.schema.HollowSchema; +import com.netflix.hollow.core.index.key.PrimaryKey; + +public class TypeOverview { + private static final String[] HEAP_SIZE_UNITS = new String[] { "B", "KB", "MB", "GB", "TB" }; + + private final String typeName; + private final int numRecords; + private final long approxHeapFootprint; + private final PrimaryKey primaryKey; + private final HollowSchema schema; + + public TypeOverview(String typeName, int numRecords, long approxHeapFootprint, PrimaryKey primaryKey, HollowSchema schema) { + this.typeName = typeName; + this.numRecords = numRecords; + this.approxHeapFootprint = approxHeapFootprint; + this.primaryKey = primaryKey; + this.schema = schema; + } + + public String getTypeName() { + return typeName; + } + + public int getNumRecordsInt() { + return numRecords; + } + + public String getNumRecords() { + return NumberFormat.getIntegerInstance().format(numRecords); + } + + public long getApproxHeapFootprintLong() { + return approxHeapFootprint; + } + + public String getApproxHeapFootprint() { + return heapFootprintDisplayString(approxHeapFootprint); + } + + public String getPrimaryKey() { + return primaryKey == null ? "" : primaryKey.toString(); + } + + public String getSchema() { + return schema.toString().replace("<", "<").replace(">", ">"); + } + + public static String heapFootprintDisplayString(long approxHeapFootprint) { + if(approxHeapFootprint <= 0) return "0"; + int digitGroups = (int) (Math.log10(approxHeapFootprint)/Math.log10(1024)); + return new DecimalFormat("#,##0.#").format(approxHeapFootprint/Math.pow(1024, digitGroups)) + " " + HEAP_SIZE_UNITS[digitGroups]; + } +} diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSelectedTypePage.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSelectedTypePage.java new file mode 100644 index 0000000000..d8c38a02ff --- /dev/null +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSelectedTypePage.java @@ -0,0 +1,253 @@ +/* + * + * Copyright 2017 Netflix, Inc. + * + * Licensed 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 com.netflix.hollow.explorer.ui.pages; + +import com.netflix.hollow.tools.stringifier.HollowRecordJsonStringifier; + +import com.netflix.hollow.tools.stringifier.HollowRecordStringifier; +import com.netflix.hollow.core.read.engine.HollowTypeStateListener; +import com.netflix.hollow.core.index.HollowPrimaryKeyIndex; +import com.netflix.hollow.explorer.ui.model.TypeKey; +import com.netflix.hollow.core.read.HollowReadFieldUtils; +import com.netflix.hollow.core.read.engine.object.HollowObjectTypeReadState; +import com.netflix.hollow.core.index.key.PrimaryKey; +import com.netflix.hollow.core.read.engine.HollowTypeReadState; +import com.netflix.hollow.core.schema.HollowObjectSchema; +import com.netflix.hollow.core.schema.HollowSchema; +import com.netflix.hollow.core.schema.HollowSchema.SchemaType; +import com.netflix.hollow.explorer.ui.HollowExplorerUI; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import org.apache.velocity.VelocityContext; + +public class BrowseSelectedTypePage extends HollowExplorerPage { + + public BrowseSelectedTypePage(HollowExplorerUI ui) { + super(ui, "browse-selected-type.vm"); + } + + @Override + protected void setUpContext(HttpServletRequest req, VelocityContext ctx) { + + HollowTypeReadState typeState = ui.getStateEngine().getTypeState(req.getParameter("type")); + + HollowPrimaryKeyIndex idx = findPrimaryKeyIndex(typeState); + + int page = req.getParameter("page") == null ? 0 : Integer.parseInt(req.getParameter("page")); + int pageSize = req.getParameter("pageSize") == null ? 20 : Integer.parseInt(req.getParameter("pageSize")); + int startRec = page * pageSize; + + String displayFormat = "json".equals(req.getParameter("display")) ? "json" : "text"; + + BitSet populatedOrdinals = typeState.getPopulatedOrdinals(); + int currentOrdinal = populatedOrdinals.nextSetBit(0); + + for(int i=0;i keys = new ArrayList(pageSize); + + for(int i=0;i 0) + keyBuilder.append(":"); + + keyBuilder.append(HollowReadFieldUtils.fieldValueObject(curState, curOrdinal, fieldPathIndexes[i][fieldPathIndexes[i].length-1])); + } + + return new TypeKey(recordIdx, ordinal, keyBuilder.toString()); + } + + return new TypeKey(recordIdx, ordinal, "", "ORDINAL:"+ordinal); + } + + private PrimaryKey getPrimaryKey(HollowSchema schema) { + if(schema.getSchemaType() == SchemaType.OBJECT) + return ((HollowObjectSchema)schema).getPrimaryKey(); + return null; + } + + private int[][] getFieldPathIndexes(PrimaryKey primaryKey) { + if(primaryKey != null) { + int fieldPathIndexes[][] = new int[primaryKey.numFields()][]; + for(int i=0;i typeOverviews = new ArrayList(); + + for(HollowTypeReadState typeState : ui.getStateEngine().getTypeStates()) { + String typeName = typeState.getSchema().getName(); + int numRecords = typeState.getPopulatedOrdinals() == null ? Integer.MIN_VALUE : typeState.getPopulatedOrdinals().cardinality(); + PrimaryKey primaryKey = typeState.getSchema().getSchemaType() == SchemaType.OBJECT ? ((HollowObjectSchema)typeState.getSchema()).getPrimaryKey() : null; + long approxHeapFootprint = typeState.getApproximateHeapFootprintInBytes(); + HollowSchema schema = typeState.getSchema(); + + typeOverviews.add(new TypeOverview(typeName, numRecords, approxHeapFootprint, primaryKey, schema)); + } + + switch(sort) { + case "typeName": + Collections.sort(typeOverviews, new Comparator() { + public int compare(TypeOverview o1, TypeOverview o2) { + return o1.getTypeName().compareTo(o2.getTypeName()); + } + }); + break; + case "numRecords": + Collections.sort(typeOverviews, new Comparator() { + public int compare(TypeOverview o1, TypeOverview o2) { + return Integer.compare(o2.getNumRecordsInt(), o1.getNumRecordsInt()); + } + }); + break; + case "heapSize": + Collections.sort(typeOverviews, new Comparator() { + public int compare(TypeOverview o1, TypeOverview o2) { + return Long.compare(o2.getApproxHeapFootprintLong(), o1.getApproxHeapFootprintLong()); + } + }); + break; + default: + Collections.sort(typeOverviews, new Comparator() { + public int compare(TypeOverview o1, TypeOverview o2) { + if(!"".equals(o1.getPrimaryKey()) && "".equals(o2.getPrimaryKey())) + return -1; + if("".equals(o1.getPrimaryKey()) && !"".equals(o2.getPrimaryKey())) + return 1; + + return o1.getTypeName().compareTo(o2.getTypeName()); + } + }); + } + + ctx.put("totalHeapFootprint", totalApproximateHeapFootprint(typeOverviews)); + ctx.put("typeOverviews", typeOverviews); + } + + private String totalApproximateHeapFootprint(List allTypes) { + long totalHeapFootprint = 0; + for(TypeOverview type : allTypes) + totalHeapFootprint += type.getApproxHeapFootprintLong(); + return TypeOverview.heapFootprintDisplayString(totalHeapFootprint); + } + + + + + +} diff --git a/hollow-explorer-ui/src/main/resources/browse-selected-type.vm b/hollow-explorer-ui/src/main/resources/browse-selected-type.vm new file mode 100644 index 0000000000..82d2442da3 --- /dev/null +++ b/hollow-explorer-ui/src/main/resources/browse-selected-type.vm @@ -0,0 +1,116 @@ +#** + * Copyright 2017 Netflix, Inc. + * + * Licensed 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. +*# + + + + + + + + #if($selectedRecordData) + + #end + +
+ + + + + + +
+ + + + + + + + + + #foreach($key in $keys) + + + + + #end +
#Key[DownloadableId]
$key.getIdx()$key.getKeyDisplay()
+ +
+ + + + + + + + + #if($selectedRecordData) + + + + + #end + + + + +
Find Key: +
+ + + + + + + +
+
Showing Record:
+ Record Data: + +
+ #if($selectedRecordData) +
+$selectedRecordData
+								
+ #end +
+
+
\ No newline at end of file diff --git a/hollow-explorer-ui/src/main/resources/explorer-footer.vm b/hollow-explorer-ui/src/main/resources/explorer-footer.vm new file mode 100644 index 0000000000..a9ffbd3d35 --- /dev/null +++ b/hollow-explorer-ui/src/main/resources/explorer-footer.vm @@ -0,0 +1,17 @@ +#** + * Copyright 2017 Netflix, Inc. + * + * Licensed 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. +*# + + \ No newline at end of file diff --git a/hollow-explorer-ui/src/main/resources/explorer-header.vm b/hollow-explorer-ui/src/main/resources/explorer-header.vm new file mode 100644 index 0000000000..8156a4a89f --- /dev/null +++ b/hollow-explorer-ui/src/main/resources/explorer-header.vm @@ -0,0 +1,38 @@ +#** + * Copyright 2017 Netflix, Inc. + * + * Licensed 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. +*# + + + + + + + + + + #if($headerDisplayString) + + #end + #if($type) + + #end + #if($stateVersion) + + #end + +
+
\ No newline at end of file diff --git a/hollow-explorer-ui/src/main/resources/show-all-types.vm b/hollow-explorer-ui/src/main/resources/show-all-types.vm new file mode 100644 index 0000000000..efd686f90e --- /dev/null +++ b/hollow-explorer-ui/src/main/resources/show-all-types.vm @@ -0,0 +1,37 @@ +#** + * Copyright 2017 Netflix, Inc. + * + * Licensed 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. +*# + +

Approx. Total Heap Footprint: $totalHeapFootprint

+ + + + + + + + + + + #foreach($typeOverview in $typeOverviews) + + + + + + + #end +
Data TypeRecordsHeap FootprintPrimary KeySchema
$typeOverview.getTypeName() + $typeOverview.getNumRecords()$typeOverview.getApproxHeapFootprint()
$typeOverview.getPrimaryKey()
$typeOverview.getSchema()
diff --git a/hollow-ui-tools/build.gradle b/hollow-ui-tools/build.gradle new file mode 100644 index 0000000000..052b33056a --- /dev/null +++ b/hollow-ui-tools/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'java' + +sourceSets { + tools { + compileClasspath += main.output + runtimeClasspath += main.output + } +} + +dependencies { + compile project(':hollow') + compile 'org.apache.velocity:velocity:1.7' + compile 'commons-codec:commons-codec:1.8' + compile 'commons-io:commons-io:2.3' + compile 'com.google.code.gson:gson:2.8.0' + + compileOnly 'org.eclipse.jetty:jetty-server:9.4.3.v20170317' + + testCompile 'junit:junit:4.11' + + toolsCompile configurations.compile + toolsRuntime configurations.runtime +} + +configurations { + toolsCompile.extendsFrom compile +} diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowUIRouter.java b/hollow-ui-tools/src/main/java/com/netflix/hollow/ui/HollowUIRouter.java similarity index 98% rename from hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowUIRouter.java rename to hollow-ui-tools/src/main/java/com/netflix/hollow/ui/HollowUIRouter.java index 2bbc8475d0..e2c7d478ca 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowUIRouter.java +++ b/hollow-ui-tools/src/main/java/com/netflix/hollow/ui/HollowUIRouter.java @@ -15,7 +15,7 @@ * limitations under the License. * */ -package com.netflix.hollow.diff.ui; +package com.netflix.hollow.ui; import java.io.InputStream; import javax.servlet.http.HttpServletRequest; diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/ui/jetty/AbstractOptionalDependencyHelper.java b/hollow-ui-tools/src/main/java/com/netflix/hollow/ui/jetty/AbstractOptionalDependencyHelper.java similarity index 100% rename from hollow-diff-ui/src/main/java/com/netflix/hollow/ui/jetty/AbstractOptionalDependencyHelper.java rename to hollow-ui-tools/src/main/java/com/netflix/hollow/ui/jetty/AbstractOptionalDependencyHelper.java diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/ui/jetty/OptionalDependencyException.java b/hollow-ui-tools/src/main/java/com/netflix/hollow/ui/jetty/OptionalDependencyException.java similarity index 100% rename from hollow-diff-ui/src/main/java/com/netflix/hollow/ui/jetty/OptionalDependencyException.java rename to hollow-ui-tools/src/main/java/com/netflix/hollow/ui/jetty/OptionalDependencyException.java diff --git a/settings.gradle b/settings.gradle index c6a5071f8e..9ad7728e47 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,8 @@ rootProject.name = 'hollow-root' include 'hollow' +include 'hollow-ui-tools' include 'hollow-diff-ui' +include 'hollow-explorer-ui' include 'hollow-jsonadapter' include 'hollow-zenoadapter' From 8faa9ddc3e5e95270a8a10003516df5033b7f6e9 Mon Sep 17 00:00:00 2001 From: Drew Koszewnik Date: Fri, 21 Apr 2017 16:44:45 -0700 Subject: [PATCH 2/9] Adding compileOnly dependency on jetty --- hollow-diff-ui/build.gradle | 2 ++ hollow-explorer-ui/build.gradle | 2 ++ 2 files changed, 4 insertions(+) diff --git a/hollow-diff-ui/build.gradle b/hollow-diff-ui/build.gradle index a3b6ef66c6..6554f3a83c 100644 --- a/hollow-diff-ui/build.gradle +++ b/hollow-diff-ui/build.gradle @@ -12,6 +12,8 @@ dependencies { compile project(':hollow-ui-tools') compile 'com.google.code.gson:gson:2.8.0' + compileOnly 'org.eclipse.jetty:jetty-server:9.4.3.v20170317' + testCompile 'junit:junit:4.11' toolsCompile configurations.compile diff --git a/hollow-explorer-ui/build.gradle b/hollow-explorer-ui/build.gradle index fdb70fbe4b..a8e1c632b9 100644 --- a/hollow-explorer-ui/build.gradle +++ b/hollow-explorer-ui/build.gradle @@ -11,6 +11,8 @@ dependencies { compile project(':hollow') compile project(':hollow-ui-tools') + compileOnly 'org.eclipse.jetty:jetty-server:9.4.3.v20170317' + testCompile 'junit:junit:4.11' toolsCompile configurations.compile From 2a9e3438d1c28758c64f5c44dcc05ca5e89346e4 Mon Sep 17 00:00:00 2001 From: Drew Koszewnik Date: Fri, 21 Apr 2017 17:47:54 -0700 Subject: [PATCH 3/9] Added HollowFieldMatchQuery --- .../hollow/core/memory/ThreadSafeBitSet.java | 9 +- .../tools/query/HollowFieldMatchQuery.java | 155 ++++++++++++++++++ .../core/util/ThreadSafeBitSetTest.java | 2 +- 3 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java diff --git a/hollow/src/main/java/com/netflix/hollow/core/memory/ThreadSafeBitSet.java b/hollow/src/main/java/com/netflix/hollow/core/memory/ThreadSafeBitSet.java index 9dd90e80c9..aace0d0cb9 100644 --- a/hollow/src/main/java/com/netflix/hollow/core/memory/ThreadSafeBitSet.java +++ b/hollow/src/main/java/com/netflix/hollow/core/memory/ThreadSafeBitSet.java @@ -43,15 +43,22 @@ public ThreadSafeBitSet() { } public ThreadSafeBitSet(int log2SegmentSizeInBits) { + this(log2SegmentSizeInBits, 0); + } + + public ThreadSafeBitSet(int log2SegmentSizeInBits, int numBitsToPreallocate) { if(log2SegmentSizeInBits < 6) throw new IllegalArgumentException("Cannot specify fewer than 64 bits in each segment!"); this.log2SegmentSize = log2SegmentSizeInBits; this.numLongsPerSegment = (1 << (log2SegmentSizeInBits - 6)); this.segmentMask = numLongsPerSegment - 1; + + long numBitsPerSegment = numLongsPerSegment * 64; + int numSegmentsToPreallocate = numBitsToPreallocate == 0 ? 1 : (int)(((numBitsToPreallocate - 1) / numBitsPerSegment) + 1); segments = new AtomicReference(); - segments.set(new ThreadSafeBitSetSegments(1, numLongsPerSegment)); + segments.set(new ThreadSafeBitSetSegments(numSegmentsToPreallocate, numLongsPerSegment)); } public void set(int position) { diff --git a/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java b/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java new file mode 100644 index 0000000000..be0ed6a41e --- /dev/null +++ b/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java @@ -0,0 +1,155 @@ +/* + * + * Copyright 2017 Netflix, Inc. + * + * Licensed 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 com.netflix.hollow.tools.query; + +import com.netflix.hollow.core.memory.ThreadSafeBitSet; + +import java.util.HashMap; +import com.netflix.hollow.core.schema.HollowObjectSchema.FieldType; +import com.netflix.hollow.core.read.HollowReadFieldUtils; +import com.netflix.hollow.core.read.engine.object.HollowObjectTypeReadState; +import com.netflix.hollow.core.schema.HollowObjectSchema; +import com.netflix.hollow.core.schema.HollowSchema.SchemaType; +import com.netflix.hollow.core.read.engine.HollowTypeReadState; +import com.netflix.hollow.core.read.engine.HollowReadStateEngine; +import java.util.BitSet; +import java.util.Map; + +public class HollowFieldMatchQuery { + + private final HollowReadStateEngine readEngine; + + public HollowFieldMatchQuery(HollowReadStateEngine readEngine) { + this.readEngine = readEngine; + } + + public Map findMatchingRecords(String fieldName, String fieldValue) { + Map matches = new HashMap(); + + for(HollowTypeReadState typeState : readEngine.getTypeStates()) { + + if(typeState.getSchema().getSchemaType() == SchemaType.OBJECT) { + HollowObjectSchema schema = (HollowObjectSchema)typeState.getSchema(); + + for(int i=0;i 0) + matches.put(typeState.getSchema().getName(), typeQueryMatches); + } + } + } + + } + + return matches; + } + + private ThreadSafeBitSet queryType(HollowObjectTypeReadState typeState, int fieldPosition, Object queryValue) { + BitSet populatedOrdinals = typeState.getPopulatedOrdinals(); + ThreadSafeBitSet typeQueryMatches = new ThreadSafeBitSet(14, populatedOrdinals.length()); + + int ordinal = populatedOrdinals.nextSetBit(0); + while(ordinal != -1) { + if(HollowReadFieldUtils.fieldValueEquals(typeState, ordinal, fieldPosition, queryValue)) + typeQueryMatches.set(ordinal); + ordinal = populatedOrdinals.nextSetBit(ordinal+1); + } + return typeQueryMatches; + } + + private ThreadSafeBitSet attemptReferenceExpansionQuery(HollowObjectTypeReadState typeState, int fieldIdx, String fieldValue) { + HollowTypeReadState referencedTypeState = typeState.getSchema().getReferencedTypeState(fieldIdx); + + if(referencedTypeState.getSchema().getSchemaType() == SchemaType.OBJECT) { + HollowObjectTypeReadState refObjTypeState = (HollowObjectTypeReadState)referencedTypeState; + HollowObjectSchema refSchema = refObjTypeState.getSchema(); + + if(refSchema.numFields() == 1) { + if(refSchema.getFieldType(0) == FieldType.REFERENCE) { + ThreadSafeBitSet refQueryMatches = attemptReferenceExpansionQuery(refObjTypeState, 0, fieldValue); + if(refQueryMatches != null) + return queryBasedOnMatchedReferences(typeState, fieldIdx, refQueryMatches); + } else { + Object queryValue = castQueryValue(fieldValue, refSchema.getFieldType(0)); + + if(queryValue != null) { + ThreadSafeBitSet refQueryMatches = queryType(refObjTypeState, 0, queryValue); + if(refQueryMatches.cardinality() > 0) + return queryBasedOnMatchedReferences(typeState, fieldIdx, refQueryMatches); + } + } + } + } + + return null; + } + + private ThreadSafeBitSet queryBasedOnMatchedReferences(HollowObjectTypeReadState typeState, int referenceFieldPosition, ThreadSafeBitSet matchedReferences) { + BitSet populatedOrdinals = typeState.getPopulatedOrdinals(); + ThreadSafeBitSet typeQueryMatches = new ThreadSafeBitSet(14, populatedOrdinals.length()); + + int ordinal = populatedOrdinals.nextSetBit(0); + while(ordinal != -1) { + int refOrdinal = typeState.readOrdinal(ordinal, referenceFieldPosition); + if(refOrdinal != -1 && matchedReferences.get(refOrdinal)) + typeQueryMatches.set(ordinal); + ordinal = populatedOrdinals.nextSetBit(ordinal+1); + } + return typeQueryMatches; + } + + private Object castQueryValue(String fieldValue, FieldType fieldType) { + + try { + switch(fieldType) { + case BOOLEAN: + return Boolean.valueOf(fieldValue); + case DOUBLE: + return Double.parseDouble(fieldValue); + case FLOAT: + return Float.parseFloat(fieldValue); + case INT: + return Integer.parseInt(fieldValue); + case LONG: + return Long.parseLong(fieldValue); + case STRING: + return fieldValue; + default: + return null; + } + } catch(Exception e) { + return null; + } + } + + +} diff --git a/hollow/src/test/java/com/netflix/hollow/core/util/ThreadSafeBitSetTest.java b/hollow/src/test/java/com/netflix/hollow/core/util/ThreadSafeBitSetTest.java index 7e1e8186b8..3b3547ebe7 100644 --- a/hollow/src/test/java/com/netflix/hollow/core/util/ThreadSafeBitSetTest.java +++ b/hollow/src/test/java/com/netflix/hollow/core/util/ThreadSafeBitSetTest.java @@ -27,7 +27,7 @@ public class ThreadSafeBitSetTest { @Test public void testEquality() { ThreadSafeBitSet set1 = new ThreadSafeBitSet(); - ThreadSafeBitSet set2 = new ThreadSafeBitSet(); + ThreadSafeBitSet set2 = new ThreadSafeBitSet(14, 16385); set1.set(100); From 4e666b8d0918bc2772d694513ff55a9e8443ff7f Mon Sep 17 00:00:00 2001 From: Drew Koszewnik Date: Fri, 21 Apr 2017 18:45:21 -0700 Subject: [PATCH 4/9] Bugfix: HollowSchemaSorter might have thrown ArrayIndexOutOfBoundsException when referenced types were filtered --- .../netflix/hollow/core/schema/HollowSchemaSorter.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hollow/src/main/java/com/netflix/hollow/core/schema/HollowSchemaSorter.java b/hollow/src/main/java/com/netflix/hollow/core/schema/HollowSchemaSorter.java index 1b78a9d1ea..71152cb8a2 100644 --- a/hollow/src/main/java/com/netflix/hollow/core/schema/HollowSchemaSorter.java +++ b/hollow/src/main/java/com/netflix/hollow/core/schema/HollowSchemaSorter.java @@ -74,7 +74,13 @@ public DependencyIndex() { } public boolean hasMoreTypes() { - return !dependencyIndex.isEmpty(); + for(Map.Entry> entry : dependencyIndex.entrySet()) { + if(entry.getValue().isEmpty()) { + return true; + } + } + + return false; } public String getNextType() { From 504faa956e47c39c2034d15a398185f627b39408 Mon Sep 17 00:00:00 2001 From: Drew Koszewnik Date: Mon, 24 Apr 2017 10:34:48 -0700 Subject: [PATCH 5/9] HollowFieldMatchQuery: experimenting with parallelization --- .../tools/query/HollowFieldMatchQuery.java | 184 +++++++++++++++--- 1 file changed, 157 insertions(+), 27 deletions(-) diff --git a/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java b/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java index be0ed6a41e..b285e41197 100644 --- a/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java +++ b/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java @@ -18,17 +18,19 @@ package com.netflix.hollow.tools.query; import com.netflix.hollow.core.memory.ThreadSafeBitSet; - -import java.util.HashMap; -import com.netflix.hollow.core.schema.HollowObjectSchema.FieldType; import com.netflix.hollow.core.read.HollowReadFieldUtils; +import com.netflix.hollow.core.read.engine.HollowReadStateEngine; +import com.netflix.hollow.core.read.engine.HollowTypeReadState; import com.netflix.hollow.core.read.engine.object.HollowObjectTypeReadState; import com.netflix.hollow.core.schema.HollowObjectSchema; +import com.netflix.hollow.core.schema.HollowObjectSchema.FieldType; import com.netflix.hollow.core.schema.HollowSchema.SchemaType; -import com.netflix.hollow.core.read.engine.HollowTypeReadState; -import com.netflix.hollow.core.read.engine.HollowReadStateEngine; +import com.netflix.hollow.core.util.SimultaneousExecutor; import java.util.BitSet; +import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; public class HollowFieldMatchQuery { @@ -38,8 +40,8 @@ public HollowFieldMatchQuery(HollowReadStateEngine readEngine) { this.readEngine = readEngine; } - public Map findMatchingRecords(String fieldName, String fieldValue) { - Map matches = new HashMap(); + public Map findMatchingRecords(String fieldName, String fieldValue) { + Map matches = new HashMap(); for(HollowTypeReadState typeState : readEngine.getTypeStates()) { @@ -50,15 +52,15 @@ public Map findMatchingRecords(String fieldName, Strin if(schema.getFieldName(i).equals(fieldName)) { HollowObjectTypeReadState objState = (HollowObjectTypeReadState)typeState; - ThreadSafeBitSet typeQueryMatches = null; + BitSet typeQueryMatches = null; if(schema.getFieldType(i) == FieldType.REFERENCE) { - typeQueryMatches = attemptReferenceExpansionQuery(objState, i, fieldValue); + typeQueryMatches = attemptReferenceTraversalQuery(objState, i, fieldValue); } else { Object queryValue = castQueryValue(fieldValue, schema.getFieldType(i)); if(queryValue != null) { - typeQueryMatches = queryType(objState, i, queryValue); + typeQueryMatches = queryBasedOnValueMatches(objState, i, queryValue); } } @@ -73,9 +75,50 @@ public Map findMatchingRecords(String fieldName, Strin return matches; } - private ThreadSafeBitSet queryType(HollowObjectTypeReadState typeState, int fieldPosition, Object queryValue) { + private BitSet attemptReferenceTraversalQuery(HollowObjectTypeReadState typeState, int fieldIdx, String fieldValue) { + HollowTypeReadState referencedTypeState = typeState.getSchema().getReferencedTypeState(fieldIdx); + + if(referencedTypeState.getSchema().getSchemaType() == SchemaType.OBJECT) { + HollowObjectTypeReadState refObjTypeState = (HollowObjectTypeReadState)referencedTypeState; + HollowObjectSchema refSchema = refObjTypeState.getSchema(); + + if(refSchema.numFields() == 1) { + if(refSchema.getFieldType(0) == FieldType.REFERENCE) { + BitSet refQueryMatches = attemptReferenceTraversalQuery(refObjTypeState, 0, fieldValue); + if(refQueryMatches != null) + return queryBasedOnMatchedReferences(typeState, fieldIdx, refQueryMatches); + } else { + Object queryValue = castQueryValue(fieldValue, refSchema.getFieldType(0)); + + if(queryValue != null) { + BitSet refQueryMatches = queryBasedOnValueMatches(refObjTypeState, 0, queryValue); + if(refQueryMatches.cardinality() > 0) + return queryBasedOnMatchedReferences(typeState, fieldIdx, refQueryMatches); + } + } + } + } + + return null; + } + + private BitSet queryBasedOnMatchedReferences(HollowObjectTypeReadState typeState, int referenceFieldPosition, BitSet matchedReferences) { BitSet populatedOrdinals = typeState.getPopulatedOrdinals(); - ThreadSafeBitSet typeQueryMatches = new ThreadSafeBitSet(14, populatedOrdinals.length()); + BitSet typeQueryMatches = new BitSet(populatedOrdinals.length()); + + int ordinal = populatedOrdinals.nextSetBit(0); + while(ordinal != -1) { + int refOrdinal = typeState.readOrdinal(ordinal, referenceFieldPosition); + if(refOrdinal != -1 && matchedReferences.get(refOrdinal)) + typeQueryMatches.set(ordinal); + ordinal = populatedOrdinals.nextSetBit(ordinal+1); + } + return typeQueryMatches; + } + + private BitSet queryBasedOnValueMatches(HollowObjectTypeReadState typeState, int fieldPosition, Object queryValue) { + BitSet populatedOrdinals = typeState.getPopulatedOrdinals(); + BitSet typeQueryMatches = new BitSet(populatedOrdinals.length()); int ordinal = populatedOrdinals.nextSetBit(0); while(ordinal != -1) { @@ -86,7 +129,45 @@ private ThreadSafeBitSet queryType(HollowObjectTypeReadState typeState, int fiel return typeQueryMatches; } - private ThreadSafeBitSet attemptReferenceExpansionQuery(HollowObjectTypeReadState typeState, int fieldIdx, String fieldValue) { + public Map findMatchingRecords(String fieldName, String fieldValue, SimultaneousExecutor executor) { + Map matches = new HashMap(); + + try { + for(HollowTypeReadState typeState : readEngine.getTypeStates()) { + + if(typeState.getSchema().getSchemaType() == SchemaType.OBJECT) { + HollowObjectSchema schema = (HollowObjectSchema)typeState.getSchema(); + + for(int i=0;i 0) + matches.put(typeState.getSchema().getName(), typeQueryMatches); + } + } + } + } + } catch(Exception e) { + throw new RuntimeException(e); + } + + return matches; + } + + private ThreadSafeBitSet attemptReferenceTraversalQuery(HollowObjectTypeReadState typeState, int fieldIdx, String fieldValue, SimultaneousExecutor executor) throws InterruptedException, ExecutionException { HollowTypeReadState referencedTypeState = typeState.getSchema().getReferencedTypeState(fieldIdx); if(referencedTypeState.getSchema().getSchemaType() == SchemaType.OBJECT) { @@ -95,16 +176,16 @@ private ThreadSafeBitSet attemptReferenceExpansionQuery(HollowObjectTypeReadStat if(refSchema.numFields() == 1) { if(refSchema.getFieldType(0) == FieldType.REFERENCE) { - ThreadSafeBitSet refQueryMatches = attemptReferenceExpansionQuery(refObjTypeState, 0, fieldValue); + ThreadSafeBitSet refQueryMatches = attemptReferenceTraversalQuery(refObjTypeState, 0, fieldValue, executor); if(refQueryMatches != null) - return queryBasedOnMatchedReferences(typeState, fieldIdx, refQueryMatches); + return queryBasedOnMatchedReferences(typeState, fieldIdx, refQueryMatches, executor); } else { Object queryValue = castQueryValue(fieldValue, refSchema.getFieldType(0)); if(queryValue != null) { - ThreadSafeBitSet refQueryMatches = queryType(refObjTypeState, 0, queryValue); + ThreadSafeBitSet refQueryMatches = queryBasedOnValueMatches(refObjTypeState, 0, queryValue, executor); if(refQueryMatches.cardinality() > 0) - return queryBasedOnMatchedReferences(typeState, fieldIdx, refQueryMatches); + return queryBasedOnMatchedReferences(typeState, fieldIdx, refQueryMatches, executor); } } } @@ -113,17 +194,67 @@ private ThreadSafeBitSet attemptReferenceExpansionQuery(HollowObjectTypeReadStat return null; } - private ThreadSafeBitSet queryBasedOnMatchedReferences(HollowObjectTypeReadState typeState, int referenceFieldPosition, ThreadSafeBitSet matchedReferences) { - BitSet populatedOrdinals = typeState.getPopulatedOrdinals(); - ThreadSafeBitSet typeQueryMatches = new ThreadSafeBitSet(14, populatedOrdinals.length()); + private ThreadSafeBitSet queryBasedOnMatchedReferences(final HollowObjectTypeReadState typeState, final int referenceFieldPosition, final ThreadSafeBitSet matchedReferences, SimultaneousExecutor executor) throws InterruptedException, ExecutionException { + final BitSet populatedOrdinals = typeState.getPopulatedOrdinals(); + final ThreadSafeBitSet typeQueryMatches = new ThreadSafeBitSet(14, populatedOrdinals.length()); - int ordinal = populatedOrdinals.nextSetBit(0); - while(ordinal != -1) { - int refOrdinal = typeState.readOrdinal(ordinal, referenceFieldPosition); - if(refOrdinal != -1 && matchedReferences.get(refOrdinal)) - typeQueryMatches.set(ordinal); - ordinal = populatedOrdinals.nextSetBit(ordinal+1); + final AtomicInteger atomicOrdinal = new AtomicInteger(0); + final int bitSetLength = populatedOrdinals.length(); + + for(int i=0;i Date: Mon, 24 Apr 2017 10:35:55 -0700 Subject: [PATCH 6/9] HollowFieldMatchQuery: removing premature optimization --- .../tools/query/HollowFieldMatchQuery.java | 133 ------------------ 1 file changed, 133 deletions(-) diff --git a/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java b/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java index b285e41197..03f8ad4f0b 100644 --- a/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java +++ b/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java @@ -17,7 +17,6 @@ */ package com.netflix.hollow.tools.query; -import com.netflix.hollow.core.memory.ThreadSafeBitSet; import com.netflix.hollow.core.read.HollowReadFieldUtils; import com.netflix.hollow.core.read.engine.HollowReadStateEngine; import com.netflix.hollow.core.read.engine.HollowTypeReadState; @@ -25,12 +24,9 @@ import com.netflix.hollow.core.schema.HollowObjectSchema; import com.netflix.hollow.core.schema.HollowObjectSchema.FieldType; import com.netflix.hollow.core.schema.HollowSchema.SchemaType; -import com.netflix.hollow.core.util.SimultaneousExecutor; import java.util.BitSet; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicInteger; public class HollowFieldMatchQuery { @@ -129,135 +125,6 @@ private BitSet queryBasedOnValueMatches(HollowObjectTypeReadState typeState, int return typeQueryMatches; } - public Map findMatchingRecords(String fieldName, String fieldValue, SimultaneousExecutor executor) { - Map matches = new HashMap(); - - try { - for(HollowTypeReadState typeState : readEngine.getTypeStates()) { - - if(typeState.getSchema().getSchemaType() == SchemaType.OBJECT) { - HollowObjectSchema schema = (HollowObjectSchema)typeState.getSchema(); - - for(int i=0;i 0) - matches.put(typeState.getSchema().getName(), typeQueryMatches); - } - } - } - } - } catch(Exception e) { - throw new RuntimeException(e); - } - - return matches; - } - - private ThreadSafeBitSet attemptReferenceTraversalQuery(HollowObjectTypeReadState typeState, int fieldIdx, String fieldValue, SimultaneousExecutor executor) throws InterruptedException, ExecutionException { - HollowTypeReadState referencedTypeState = typeState.getSchema().getReferencedTypeState(fieldIdx); - - if(referencedTypeState.getSchema().getSchemaType() == SchemaType.OBJECT) { - HollowObjectTypeReadState refObjTypeState = (HollowObjectTypeReadState)referencedTypeState; - HollowObjectSchema refSchema = refObjTypeState.getSchema(); - - if(refSchema.numFields() == 1) { - if(refSchema.getFieldType(0) == FieldType.REFERENCE) { - ThreadSafeBitSet refQueryMatches = attemptReferenceTraversalQuery(refObjTypeState, 0, fieldValue, executor); - if(refQueryMatches != null) - return queryBasedOnMatchedReferences(typeState, fieldIdx, refQueryMatches, executor); - } else { - Object queryValue = castQueryValue(fieldValue, refSchema.getFieldType(0)); - - if(queryValue != null) { - ThreadSafeBitSet refQueryMatches = queryBasedOnValueMatches(refObjTypeState, 0, queryValue, executor); - if(refQueryMatches.cardinality() > 0) - return queryBasedOnMatchedReferences(typeState, fieldIdx, refQueryMatches, executor); - } - } - } - } - - return null; - } - - private ThreadSafeBitSet queryBasedOnMatchedReferences(final HollowObjectTypeReadState typeState, final int referenceFieldPosition, final ThreadSafeBitSet matchedReferences, SimultaneousExecutor executor) throws InterruptedException, ExecutionException { - final BitSet populatedOrdinals = typeState.getPopulatedOrdinals(); - final ThreadSafeBitSet typeQueryMatches = new ThreadSafeBitSet(14, populatedOrdinals.length()); - - final AtomicInteger atomicOrdinal = new AtomicInteger(0); - final int bitSetLength = populatedOrdinals.length(); - - for(int i=0;i Date: Mon, 24 Apr 2017 11:10:42 -0700 Subject: [PATCH 7/9] Moved HollowDiffSession into hollow-ui-tools as HollowUISession --- .../netflix/hollow/diff/ui/HollowDiffUI.java | 2 +- .../hollow/diff/ui/pages/DiffFieldPage.java | 4 +-- .../hollow/diff/ui/pages/DiffObjectPage.java | 4 +-- .../diff/ui/pages/DiffOverviewPage.java | 4 +-- .../hollow/diff/ui/pages/DiffPage.java | 12 ++++---- .../hollow/diff/ui/pages/DiffTypePage.java | 4 +-- .../diffview/DiffViewOutputGenerator.java | 4 +-- .../diffview/HollowDiffViewProvider.java | 10 +++---- .../diffview/HollowHistoryViewProvider.java | 10 +++---- .../diffview/HollowObjectViewProvider.java | 5 ++-- .../hollow/history/ui/HollowHistoryUI.java | 3 +- .../ui/pages/HistoricalObjectDiffPage.java | 4 +-- .../history/ui/pages/HistoryOverviewPage.java | 4 +-- .../hollow/history/ui/pages/HistoryPage.java | 6 ++-- .../history/ui/pages/HistoryQueryPage.java | 4 +-- .../history/ui/pages/HistoryStatePage.java | 4 +-- .../HistoryStateTypeExpandGroupPage.java | 4 +-- .../ui/pages/HistoryStateTypePage.java | 8 ++--- .../netflix/hollow/ui/HollowUISession.java | 30 +++++++------------ 19 files changed, 58 insertions(+), 68 deletions(-) rename hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowDiffSession.java => hollow-ui-tools/src/main/java/com/netflix/hollow/ui/HollowUISession.java (73%) diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowDiffUI.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowDiffUI.java index 2721b917d3..9b7e0c8668 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowDiffUI.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowDiffUI.java @@ -17,7 +17,7 @@ */ package com.netflix.hollow.diff.ui; -import static com.netflix.hollow.diff.ui.HollowDiffSession.getSession; +import static com.netflix.hollow.ui.HollowUISession.getSession; import com.netflix.hollow.core.index.key.PrimaryKey; import com.netflix.hollow.diff.ui.pages.DiffFieldPage; diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffFieldPage.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffFieldPage.java index 89cc17f409..1c8e3f5ed9 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffFieldPage.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffFieldPage.java @@ -17,12 +17,12 @@ */ package com.netflix.hollow.diff.ui.pages; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.diff.ui.HollowDiffUI; import com.netflix.hollow.diff.ui.model.HollowDiffUIBreadcrumbs; import com.netflix.hollow.diff.ui.model.HollowObjectPairDiffScore; import com.netflix.hollow.tools.diff.HollowTypeDiff; import com.netflix.hollow.tools.diff.count.HollowFieldDiff; +import com.netflix.hollow.ui.HollowUISession; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -36,7 +36,7 @@ public DiffFieldPage(HollowDiffUI diffUI) { } @Override - protected void setUpContext(HttpServletRequest req, HollowDiffSession session, VelocityContext ctx) { + protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { String typeName = req.getParameter("type"); HollowTypeDiff typeDiff = getTypeDiff(typeName); int fieldIdx = Integer.parseInt(req.getParameter("fieldIdx")); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffObjectPage.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffObjectPage.java index 9959270b8f..3c6ca3219f 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffObjectPage.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffObjectPage.java @@ -17,12 +17,12 @@ */ package com.netflix.hollow.diff.ui.pages; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.diff.ui.HollowDiffUI; import com.netflix.hollow.diff.ui.model.HollowDiffUIBreadcrumbs; import com.netflix.hollow.diffview.HollowDiffHtmlKickstarter; import com.netflix.hollow.diffview.HollowObjectView; import com.netflix.hollow.tools.diff.HollowTypeDiff; +import com.netflix.hollow.ui.HollowUISession; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -35,7 +35,7 @@ public DiffObjectPage(HollowDiffUI diffUI) { } @Override - protected void setUpContext(HttpServletRequest req, HollowDiffSession session, VelocityContext ctx) { + protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { String type = req.getParameter("type"); int fromOrdinal = Integer.parseInt(req.getParameter("fromOrdinal")); int toOrdinal = Integer.parseInt(req.getParameter("toOrdinal")); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffOverviewPage.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffOverviewPage.java index 617769a6ea..b4da038be7 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffOverviewPage.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffOverviewPage.java @@ -17,10 +17,10 @@ */ package com.netflix.hollow.diff.ui.pages; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.diff.ui.HollowDiffUI; import com.netflix.hollow.diff.ui.model.HollowDiffOverviewTypeEntry; import com.netflix.hollow.tools.diff.HollowTypeDiff; +import com.netflix.hollow.ui.HollowUISession; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -35,7 +35,7 @@ public DiffOverviewPage(HollowDiffUI diffUI) { } @Override - protected void setUpContext(HttpServletRequest req, HollowDiffSession session, VelocityContext ctx) { + protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { String sortBy = param(req, session, "overview", "sortBy", "diffs"); ctx.put("typeOverviewEntries", getTypeEntries(sortBy)); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffPage.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffPage.java index 5a1debc61e..92dc01d547 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffPage.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffPage.java @@ -17,11 +17,11 @@ */ package com.netflix.hollow.diff.ui.pages; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.diff.ui.HollowDiffUI; import com.netflix.hollow.diff.ui.model.HollowHeaderEntry; import com.netflix.hollow.tools.diff.HollowDiff; import com.netflix.hollow.tools.diff.HollowTypeDiff; +import com.netflix.hollow.ui.HollowUISession; import java.io.Writer; import java.util.ArrayList; import java.util.HashSet; @@ -50,7 +50,7 @@ public DiffPage(HollowDiffUI diffUI, String templateName) { this.footerTemplate = diffUI.getVelocity().getTemplate("diff-footer.vm"); } - public void render(HttpServletRequest req, HollowDiffSession session, Writer writer) { + public void render(HttpServletRequest req, HollowUISession session, Writer writer) { processCookies(req); VelocityContext ctx = new VelocityContext(); @@ -89,21 +89,21 @@ private void processCookies(HttpServletRequest request) { } } - protected abstract void setUpContext(HttpServletRequest req, HollowDiffSession session, VelocityContext ctx); + protected abstract void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx); protected HollowDiff getDiff() { return diffUI.getDiff(); } - protected int intParam(HttpServletRequest req, HollowDiffSession session, String ctx, String paramName, int defaultValue) { + protected int intParam(HttpServletRequest req, HollowUISession session, String ctx, String paramName, int defaultValue) { return Integer.parseInt(param(req, session, ctx, paramName, String.valueOf(defaultValue))); } - protected boolean boolParam(HttpServletRequest req, HollowDiffSession session, String ctx, String paramName, boolean defaultValue) { + protected boolean boolParam(HttpServletRequest req, HollowUISession session, String ctx, String paramName, boolean defaultValue) { return Boolean.parseBoolean(param(req, session, ctx, paramName, String.valueOf(defaultValue))); } - protected String param(HttpServletRequest req, HollowDiffSession session, String ctx, String paramName, String defaultValue) { + protected String param(HttpServletRequest req, HollowUISession session, String ctx, String paramName, String defaultValue) { String sessionParamName = ctx + "_" + paramName; String reqParam = req.getParameter(paramName); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffTypePage.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffTypePage.java index db50abc77c..5825991a59 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffTypePage.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/pages/DiffTypePage.java @@ -19,7 +19,6 @@ import com.netflix.hollow.core.read.engine.object.HollowObjectTypeReadState; import com.netflix.hollow.core.util.IntList; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.diff.ui.HollowDiffUI; import com.netflix.hollow.diff.ui.model.HollowDiffUIBreadcrumbs; import com.netflix.hollow.diff.ui.model.HollowFieldDiffScore; @@ -27,6 +26,7 @@ import com.netflix.hollow.diff.ui.model.HollowUnmatchedObject; import com.netflix.hollow.tools.diff.HollowTypeDiff; import com.netflix.hollow.tools.diff.count.HollowFieldDiff; +import com.netflix.hollow.ui.HollowUISession; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -48,7 +48,7 @@ public DiffTypePage(HollowDiffUI diffUI) { } @Override - protected void setUpContext(HttpServletRequest req, HollowDiffSession session, VelocityContext ctx) { + protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { String typeName = req.getParameter("type"); HollowTypeDiff typeDiff = getTypeDiff(typeName); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/DiffViewOutputGenerator.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/DiffViewOutputGenerator.java index a028377e51..ba0841951a 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/DiffViewOutputGenerator.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/DiffViewOutputGenerator.java @@ -17,7 +17,7 @@ */ package com.netflix.hollow.diffview; -import com.netflix.hollow.diff.ui.HollowDiffSession; +import com.netflix.hollow.ui.HollowUISession; import java.io.IOException; import java.io.Writer; import javax.servlet.http.HttpServletRequest; @@ -50,7 +50,7 @@ public void uncollapseRow(HttpServletRequest req, HttpServletResponse resp) thro } private HollowDiffViewRow findRow(HttpServletRequest req, HttpServletResponse resp) { - HollowDiffSession session = HollowDiffSession.getSession(req, resp); + HollowUISession session = HollowUISession.getSession(req, resp); HollowObjectView objectView = viewProvider.getObjectView(req, session); int rowPath[] = getRowPath(req.getParameter("row")); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/HollowDiffViewProvider.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/HollowDiffViewProvider.java index a1b8e0155a..287df5bce4 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/HollowDiffViewProvider.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/HollowDiffViewProvider.java @@ -17,8 +17,8 @@ */ package com.netflix.hollow.diffview; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.diff.ui.HollowDiffUI; +import com.netflix.hollow.ui.HollowUISession; import javax.servlet.http.HttpServletRequest; public class HollowDiffViewProvider implements HollowObjectViewProvider { @@ -30,7 +30,7 @@ public HollowDiffViewProvider(HollowDiffUI diffUI) { } @Override - public HollowDiffView getObjectView(HttpServletRequest req, HollowDiffSession session) { + public HollowDiffView getObjectView(HttpServletRequest req, HollowUISession session) { String type = req.getParameter("type"); int fromOrdinal = Integer.parseInt(req.getParameter("fromOrdinal")); int toOrdinal = Integer.parseInt(req.getParameter("toOrdinal")); @@ -39,8 +39,8 @@ public HollowDiffView getObjectView(HttpServletRequest req, HollowDiffSession se return objectView; } - private HollowDiffView getObjectView(HollowDiffSession session, String type, int fromOrdinal, int toOrdinal) { - HollowDiffView objectView = (HollowDiffView) session.getObjectView(); + private HollowDiffView getObjectView(HollowUISession session, String type, int fromOrdinal, int toOrdinal) { + HollowDiffView objectView = (HollowDiffView) session.getAttribute("hollow-diff-view"); if(objectView != null && objectView.getType().equals(type) @@ -52,7 +52,7 @@ private HollowDiffView getObjectView(HollowDiffSession session, String type, int HollowDiffViewRow rootRow = new HollowObjectDiffViewGenerator(diffUI.getDiff().getFromStateEngine(), diffUI.getDiff().getToStateEngine(), diffUI, type, fromOrdinal, toOrdinal).getHollowDiffViewRows(); objectView = new HollowDiffView(type, fromOrdinal, toOrdinal, rootRow, diffUI.getExactRecordMatcher()); objectView.resetView(); - session.setObjectView(objectView); + session.setAttribute("hollow-diff-view", objectView); return objectView; } diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/HollowHistoryViewProvider.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/HollowHistoryViewProvider.java index 8b508ca18d..8922c701b2 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/HollowHistoryViewProvider.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/HollowHistoryViewProvider.java @@ -17,10 +17,10 @@ */ package com.netflix.hollow.diffview; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.history.ui.HollowHistoryUI; import com.netflix.hollow.tools.history.HollowHistoricalState; import com.netflix.hollow.tools.history.keyindex.HollowHistoricalStateTypeKeyOrdinalMapping; +import com.netflix.hollow.ui.HollowUISession; import javax.servlet.http.HttpServletRequest; public class HollowHistoryViewProvider implements HollowObjectViewProvider { @@ -32,7 +32,7 @@ public HollowHistoryViewProvider(HollowHistoryUI historyUI) { } @Override - public HollowHistoryView getObjectView(HttpServletRequest req, HollowDiffSession session) { + public HollowHistoryView getObjectView(HttpServletRequest req, HollowUISession session) { long version = Long.parseLong(req.getParameter("version")); String type = req.getParameter("type"); int keyOrdinal = Integer.parseInt(req.getParameter("keyOrdinal")); @@ -41,8 +41,8 @@ public HollowHistoryView getObjectView(HttpServletRequest req, HollowDiffSession return objectView; } - private HollowHistoryView getObjectView(HollowDiffSession session, long version, String type, int keyOrdinal) { - HollowHistoryView objectView = (HollowHistoryView) session.getObjectView(); + private HollowHistoryView getObjectView(HollowUISession session, long version, String type, int keyOrdinal) { + HollowHistoryView objectView = (HollowHistoryView) session.getAttribute("hollow-history-view"); if(objectView != null && objectView.getHistoricalVersion() == version @@ -59,7 +59,7 @@ private HollowHistoryView getObjectView(HollowDiffSession session, long version, HollowDiffViewRow rootRow = new HollowObjectDiffViewGenerator(historicalState.getDataAccess(), historicalState.getDataAccess(), historyUI, type, fromOrdinal, toOrdinal).getHollowDiffViewRows(); objectView = new HollowHistoryView(version, type, keyOrdinal, rootRow, historyUI.getExactRecordMatcher()); objectView.resetView(); - session.setObjectView(objectView); + session.setAttribute("hollow-history-view", objectView); return objectView; } diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/HollowObjectViewProvider.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/HollowObjectViewProvider.java index 9e6bfa1007..39c0cf83d6 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/HollowObjectViewProvider.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/diffview/HollowObjectViewProvider.java @@ -17,10 +17,11 @@ */ package com.netflix.hollow.diffview; -import com.netflix.hollow.diff.ui.HollowDiffSession; +import com.netflix.hollow.ui.HollowUISession; + import javax.servlet.http.HttpServletRequest; public interface HollowObjectViewProvider { - public HollowObjectView getObjectView(HttpServletRequest req, HollowDiffSession session); + public HollowObjectView getObjectView(HttpServletRequest req, HollowUISession session); } diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/HollowHistoryUI.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/HollowHistoryUI.java index 66a9115f8f..db81e44b30 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/HollowHistoryUI.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/HollowHistoryUI.java @@ -17,8 +17,7 @@ */ package com.netflix.hollow.history.ui; -import static com.netflix.hollow.diff.ui.HollowDiffSession.getSession; - +import static com.netflix.hollow.ui.HollowUISession.getSession; import com.netflix.hollow.core.index.key.PrimaryKey; import com.netflix.hollow.diffview.DiffViewOutputGenerator; import com.netflix.hollow.diffview.HollowHistoryViewProvider; diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoricalObjectDiffPage.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoricalObjectDiffPage.java index b6329f642e..13c62667ca 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoricalObjectDiffPage.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoricalObjectDiffPage.java @@ -17,7 +17,6 @@ */ package com.netflix.hollow.history.ui.pages; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.diffview.HollowDiffHtmlKickstarter; import com.netflix.hollow.diffview.HollowObjectView; import com.netflix.hollow.history.ui.HollowHistoryUI; @@ -26,6 +25,7 @@ import com.netflix.hollow.tools.history.HollowHistoricalState; import com.netflix.hollow.tools.history.HollowHistory; import com.netflix.hollow.tools.history.keyindex.HollowHistoricalStateTypeKeyOrdinalMapping; +import com.netflix.hollow.ui.HollowUISession; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -38,7 +38,7 @@ public HistoricalObjectDiffPage(HollowHistoryUI ui) { } @Override - protected void setUpContext(HttpServletRequest req, HollowDiffSession session, VelocityContext ctx) { + protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { long version = Long.parseLong(req.getParameter("version")); String type = req.getParameter("type"); int keyOrdinal = Integer.parseInt(req.getParameter("keyOrdinal")); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryOverviewPage.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryOverviewPage.java index 9a50394b59..d6877d3f18 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryOverviewPage.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryOverviewPage.java @@ -18,12 +18,12 @@ package com.netflix.hollow.history.ui.pages; import com.google.gson.Gson; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.history.ui.HollowHistoryUI; import com.netflix.hollow.history.ui.VersionTimestampConverter; import com.netflix.hollow.history.ui.model.HistoryOverviewRow; import com.netflix.hollow.tools.history.HollowHistoricalState; import com.netflix.hollow.tools.history.keyindex.HollowHistoricalStateTypeKeyOrdinalMapping; +import com.netflix.hollow.ui.HollowUISession; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; @@ -41,7 +41,7 @@ public HistoryOverviewPage(HollowHistoryUI ui) { } @Override - protected void setUpContext(HttpServletRequest req, HollowDiffSession session, VelocityContext ctx) { + protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { List rows = getHistoryOverview(); ctx.put("overviewDisplayHeaders", ui.getOverviewDisplayHeaders()); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryPage.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryPage.java index 33179240e9..dcaa107463 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryPage.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryPage.java @@ -17,10 +17,10 @@ */ package com.netflix.hollow.history.ui.pages; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.diff.ui.model.HollowHeaderEntry; import com.netflix.hollow.history.ui.HollowHistoryUI; import com.netflix.hollow.tools.history.HollowHistoricalState; +import com.netflix.hollow.ui.HollowUISession; import java.io.Writer; import java.util.ArrayList; import java.util.HashSet; @@ -45,7 +45,7 @@ public HistoryPage(HollowHistoryUI ui, String templateName) { this.footerTemplate = ui.getVelocityEngine().getTemplate("history-footer.vm"); } - public void render(HttpServletRequest req, HollowDiffSession session, Writer writer) { + public void render(HttpServletRequest req, HollowUISession session, Writer writer) { VelocityContext ctx = new VelocityContext(); ctx.put("showHomeLink", !(this instanceof HistoryOverviewPage)); @@ -60,7 +60,7 @@ public void render(HttpServletRequest req, HollowDiffSession session, Writer wri footerTemplate.merge(ctx, writer); } - protected abstract void setUpContext(HttpServletRequest req, HollowDiffSession session, VelocityContext ctx); + protected abstract void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx); protected List getHeaderEntries(HollowHistoricalState state) { Map fromTags = state.getHeaderEntries(); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryQueryPage.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryQueryPage.java index 52f4b5d446..c019411adc 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryQueryPage.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryQueryPage.java @@ -18,13 +18,13 @@ package com.netflix.hollow.history.ui.pages; import com.netflix.hollow.core.util.IntList; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.history.ui.HollowHistoryUI; import com.netflix.hollow.history.ui.VersionTimestampConverter; import com.netflix.hollow.history.ui.model.HistoryStateQueryMatches; import com.netflix.hollow.tools.history.HollowHistoricalState; import com.netflix.hollow.tools.history.HollowHistory; import com.netflix.hollow.tools.history.keyindex.HollowHistoryTypeKeyIndex; +import com.netflix.hollow.ui.HollowUISession; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -39,7 +39,7 @@ public HistoryQueryPage(HollowHistoryUI ui) { } @Override - protected void setUpContext(HttpServletRequest req, HollowDiffSession session, VelocityContext ctx) { + protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { HollowHistory history = ui.getHistory(); String query = req.getParameter("query"); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryStatePage.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryStatePage.java index 3b3188a736..b4b89a1986 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryStatePage.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryStatePage.java @@ -18,12 +18,12 @@ package com.netflix.hollow.history.ui.pages; import com.google.gson.Gson; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.diff.ui.model.HollowHeaderEntry; import com.netflix.hollow.history.ui.HollowHistoryUI; import com.netflix.hollow.history.ui.model.HistoryStateTypeChangeSummary; import com.netflix.hollow.tools.history.HollowHistoricalState; import com.netflix.hollow.tools.history.keyindex.HollowHistoricalStateTypeKeyOrdinalMapping; +import com.netflix.hollow.ui.HollowUISession; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; @@ -45,7 +45,7 @@ public HistoryStatePage(HollowHistoryUI ui) { } @Override - protected void setUpContext(HttpServletRequest req, HollowDiffSession session, VelocityContext ctx) { + protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { HollowHistoricalState historicalState = ui.getHistory().getHistoricalState(Long.parseLong(req.getParameter("version"))); long nextStateVersion = getNextStateVersion(historicalState); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryStateTypeExpandGroupPage.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryStateTypeExpandGroupPage.java index a716548cbc..5275745b2d 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryStateTypeExpandGroupPage.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryStateTypeExpandGroupPage.java @@ -17,9 +17,9 @@ */ package com.netflix.hollow.history.ui.pages; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.history.ui.HollowHistoryUI; import com.netflix.hollow.history.ui.model.HistoryStateTypeChanges; +import com.netflix.hollow.ui.HollowUISession; import javax.servlet.http.HttpServletRequest; import org.apache.velocity.VelocityContext; @@ -30,7 +30,7 @@ public HistoryStateTypeExpandGroupPage(HollowHistoryUI ui) { } @Override - protected void setUpContext(HttpServletRequest req, HollowDiffSession session, VelocityContext ctx) { + protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { HistoryStateTypeChanges typeChange = HistoryStateTypePage.getStateTypeChanges(req, session, ui); String expandGroupId = req.getParameter("expandGroupId"); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryStateTypePage.java b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryStateTypePage.java index bfd3837cd3..6d064b592c 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryStateTypePage.java +++ b/hollow-diff-ui/src/main/java/com/netflix/hollow/history/ui/pages/HistoryStateTypePage.java @@ -18,13 +18,13 @@ package com.netflix.hollow.history.ui.pages; import com.google.gson.Gson; -import com.netflix.hollow.diff.ui.HollowDiffSession; import com.netflix.hollow.history.ui.HollowHistoryUI; import com.netflix.hollow.history.ui.model.HistoryStateTypeChanges; import com.netflix.hollow.history.ui.model.RecordDiff; import com.netflix.hollow.history.ui.model.RecordDiffTreeNode; import com.netflix.hollow.history.ui.naming.HollowHistoryRecordNamer; import com.netflix.hollow.tools.history.HollowHistoricalState; +import com.netflix.hollow.ui.HollowUISession; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; @@ -45,7 +45,7 @@ public HistoryStateTypePage(HollowHistoryUI ui) { } @Override - protected void setUpContext(HttpServletRequest req, HollowDiffSession session, VelocityContext ctx) { + protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { long version = Long.parseLong(req.getParameter("version")); HistoryStateTypeChanges typeChange = getStateTypeChanges(req, session, ui); HollowHistoricalState historicalState = ui.getHistory().getHistoricalState(version); @@ -59,7 +59,7 @@ protected void setUpContext(HttpServletRequest req, HollowDiffSession session, V ctx.put("groupByOptions", groupByOptions); } - public void sendJson(HttpServletRequest request, HollowDiffSession session, HttpServletResponse response) { + public void sendJson(HttpServletRequest request, HollowUISession session, HttpServletResponse response) { long version = Long.parseLong(request.getParameter("version")); HistoryStateTypeChanges typeChange = getStateTypeChanges(request, session, ui); HollowHistoricalState historicalState = ui.getHistory().getHistoricalState(version); @@ -160,7 +160,7 @@ public void sendJson(HttpServletRequest request, HollowDiffSession session, Http } - public static HistoryStateTypeChanges getStateTypeChanges(HttpServletRequest req, HollowDiffSession session, HollowHistoryUI ui) { + public static HistoryStateTypeChanges getStateTypeChanges(HttpServletRequest req, HollowUISession session, HollowHistoryUI ui) { HistoryStateTypeChanges typeChanges = (HistoryStateTypeChanges) session.getAttribute(STATE_TYPE_CHANGES_SESSION_ATTRIBUTE_NAME); long version = Long.parseLong(req.getParameter("version")); String type = req.getParameter("type"); diff --git a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowDiffSession.java b/hollow-ui-tools/src/main/java/com/netflix/hollow/ui/HollowUISession.java similarity index 73% rename from hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowDiffSession.java rename to hollow-ui-tools/src/main/java/com/netflix/hollow/ui/HollowUISession.java index 578c19fcdb..6a95965013 100644 --- a/hollow-diff-ui/src/main/java/com/netflix/hollow/diff/ui/HollowDiffSession.java +++ b/hollow-ui-tools/src/main/java/com/netflix/hollow/ui/HollowUISession.java @@ -15,9 +15,8 @@ * limitations under the License. * */ -package com.netflix.hollow.diff.ui; +package com.netflix.hollow.ui; -import com.netflix.hollow.diffview.HollowObjectView; import java.util.Iterator; import java.util.Map; import java.util.Random; @@ -27,15 +26,14 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Response; -public class HollowDiffSession { +public class HollowUISession { private static final long SESSION_ABANDONMENT_MILLIS = 60 * 60 * 1000; private final Map sessionParams; - private HollowObjectView currentObjectView; private long lastAccessed; - public HollowDiffSession() { + public HollowUISession() { this.sessionParams = new ConcurrentHashMap(); } @@ -47,21 +45,13 @@ public Object getAttribute(String param) { return sessionParams.get(param); } - public void setObjectView(HollowObjectView view) { - this.currentObjectView = view; - } - - public HollowObjectView getObjectView() { - return currentObjectView; - } - public void updateLastAccessed() { lastAccessed = System.currentTimeMillis(); } - private static final ConcurrentHashMap sessions = new ConcurrentHashMap(); + private static final ConcurrentHashMap sessions = new ConcurrentHashMap(); - public static HollowDiffSession getSession(HttpServletRequest req, HttpServletResponse resp) { + public static HollowUISession getSession(HttpServletRequest req, HttpServletResponse resp) { Long sessionId = null; if(req.getCookies() != null) { @@ -79,10 +69,10 @@ public static HollowDiffSession getSession(HttpServletRequest req, HttpServletRe resp.addCookie(cookie); } - HollowDiffSession session = sessions.get(sessionId); + HollowUISession session = sessions.get(sessionId); if(session == null) { - session = new HollowDiffSession(); - HollowDiffSession existingSession = sessions.putIfAbsent(sessionId, session); + session = new HollowUISession(); + HollowUISession existingSession = sessions.putIfAbsent(sessionId, session); if(existingSession != null) session = existingSession; } @@ -93,9 +83,9 @@ public static HollowDiffSession getSession(HttpServletRequest req, HttpServletRe static { Thread sessionCleanupThread = new Thread(new Runnable() { public void run() { - Iterator> iter = sessions.entrySet().iterator(); + Iterator> iter = sessions.entrySet().iterator(); while(iter.hasNext()) { - Map.Entry entry = iter.next(); + Map.Entry entry = iter.next(); if(entry.getValue().lastAccessed + SESSION_ABANDONMENT_MILLIS < System.currentTimeMillis()) iter.remove(); } From 104dc98685d9d9be64026d4a39abf986d42bdebb Mon Sep 17 00:00:00 2001 From: Drew Koszewnik Date: Mon, 24 Apr 2017 13:07:06 -0700 Subject: [PATCH 8/9] Added browse schema page --- .../hollow/explorer/ui/HollowExplorerUI.java | 17 ++++- .../explorer/ui/model/SchemaDisplay.java | 75 +++++++++++++++++++ .../explorer/ui/model/SchemaDisplayField.java | 68 +++++++++++++++++ .../explorer/ui/pages/BrowseSchemaPage.java | 54 +++++++++++++ .../ui/pages/BrowseSelectedTypePage.java | 16 ++-- .../explorer/ui/pages/HollowExplorerPage.java | 8 +- .../explorer/ui/pages/ShowAllTypesPage.java | 3 +- .../src/main/resources/browse-schema.vm | 54 +++++++++++++ .../src/main/resources/explorer-header.vm | 7 +- 9 files changed, 283 insertions(+), 19 deletions(-) create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplay.java create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplayField.java create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSchemaPage.java create mode 100644 hollow-explorer-ui/src/main/resources/browse-schema.vm diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/HollowExplorerUI.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/HollowExplorerUI.java index 15147a0f01..f3f99268ff 100644 --- a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/HollowExplorerUI.java +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/HollowExplorerUI.java @@ -17,12 +17,14 @@ */ package com.netflix.hollow.explorer.ui; -import com.netflix.hollow.api.client.HollowClient; +import com.netflix.hollow.explorer.ui.pages.BrowseSchemaPage; -import com.netflix.hollow.explorer.ui.pages.BrowseSelectedTypePage; +import com.netflix.hollow.api.client.HollowClient; import com.netflix.hollow.core.read.engine.HollowReadStateEngine; +import com.netflix.hollow.explorer.ui.pages.BrowseSelectedTypePage; import com.netflix.hollow.explorer.ui.pages.ShowAllTypesPage; import com.netflix.hollow.ui.HollowUIRouter; +import com.netflix.hollow.ui.HollowUISession; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -36,6 +38,7 @@ public class HollowExplorerUI extends HollowUIRouter { private final ShowAllTypesPage showAllTypesPage; private final BrowseSelectedTypePage browseTypePage; + private final BrowseSchemaPage browseSchemaPage; public HollowExplorerUI(String baseUrlPath, HollowClient client) { this(baseUrlPath, client, null); @@ -52,17 +55,23 @@ private HollowExplorerUI(String baseUrlPath, HollowClient client, HollowReadStat this.showAllTypesPage = new ShowAllTypesPage(this); this.browseTypePage = new BrowseSelectedTypePage(this); + this.browseSchemaPage = new BrowseSchemaPage(this); } public boolean handle(String target, HttpServletRequest req, HttpServletResponse resp) throws IOException { String pageName = getTargetRootPath(target); + HollowUISession session = HollowUISession.getSession(req, resp); + if("".equals(pageName) || "home".equals(pageName)) { - showAllTypesPage.render(req, resp.getWriter()); + showAllTypesPage.render(req, session, resp.getWriter()); return true; } else if("type".equals(pageName)) { - browseTypePage.render(req, resp.getWriter()); + browseTypePage.render(req, session, resp.getWriter()); + return true; + } else if("schema".equals(pageName)) { + browseSchemaPage.render(req, session, resp.getWriter()); return true; } diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplay.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplay.java new file mode 100644 index 0000000000..354efa440d --- /dev/null +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplay.java @@ -0,0 +1,75 @@ +package com.netflix.hollow.explorer.ui.model; + +import com.netflix.hollow.core.schema.HollowMapSchema; +import com.netflix.hollow.core.schema.HollowCollectionSchema; +import com.netflix.hollow.core.schema.HollowObjectSchema; +import java.util.ArrayList; +import java.util.List; +import com.netflix.hollow.core.schema.HollowSchema; + +public class SchemaDisplay { + + private final HollowSchema schema; + private final List displayFields; + + private boolean isExpanded; + + public SchemaDisplay(HollowSchema schema) { + this.schema = schema; + this.displayFields = createDisplayFields(); + this.isExpanded = false; + } + + private List createDisplayFields() { + List displayFields = new ArrayList(); + + switch(schema.getSchemaType()) { + case OBJECT: + HollowObjectSchema objSchema = (HollowObjectSchema)schema; + + for(int i=0;i getFields() { + return displayFields; + } + + public void setExpanded(boolean isExpanded) { + this.isExpanded = isExpanded; + } + + public boolean isExpanded() { + return isExpanded; + } + +} diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplayField.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplayField.java new file mode 100644 index 0000000000..a7d20a8f39 --- /dev/null +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplayField.java @@ -0,0 +1,68 @@ +package com.netflix.hollow.explorer.ui.model; + +import com.netflix.hollow.core.schema.HollowCollectionSchema; +import com.netflix.hollow.core.schema.HollowMapSchema; +import com.netflix.hollow.core.schema.HollowObjectSchema; +import com.netflix.hollow.core.schema.HollowObjectSchema.FieldType; +import com.netflix.hollow.core.schema.HollowSchema.SchemaType; + +public class SchemaDisplayField { + + private final String fieldName; + private final FieldType fieldType; + private final boolean isSearchable; + + private final SchemaDisplay referencedType; + + public SchemaDisplayField(HollowCollectionSchema parentSchema) { + this.fieldName = "element"; + this.fieldType = FieldType.REFERENCE; + this.isSearchable = false; + this.referencedType = new SchemaDisplay(parentSchema.getElementTypeState().getSchema()); + } + + public SchemaDisplayField(HollowMapSchema parentSchema, int fieldNumber) { + this.fieldName = fieldNumber == 0 ? "key" : "value"; + this.fieldType = FieldType.REFERENCE; + this.isSearchable = false; + this.referencedType = fieldNumber == 0 ? new SchemaDisplay(parentSchema.getKeyTypeState().getSchema()) : new SchemaDisplay(parentSchema.getValueTypeState().getSchema()); + } + + public SchemaDisplayField(HollowObjectSchema parentSchema, int fieldNumber) { + this.fieldName = parentSchema.getFieldName(fieldNumber); + this.fieldType = parentSchema.getFieldType(fieldNumber); + this.isSearchable = isSearchable(parentSchema, fieldNumber); + this.referencedType = fieldType == FieldType.REFERENCE ? new SchemaDisplay(parentSchema.getReferencedTypeState(fieldNumber).getSchema()) : null; + } + + private boolean isSearchable(HollowObjectSchema schema, int fieldNumber) { + if(schema.getFieldType(fieldNumber) == FieldType.REFERENCE) { + if(schema.getReferencedTypeState(fieldNumber).getSchema().getSchemaType() != SchemaType.OBJECT) + return false; + HollowObjectSchema refObjSchema = (HollowObjectSchema)schema.getReferencedTypeState(fieldNumber).getSchema(); + if(refObjSchema.numFields() != 1) + return false; + + return isSearchable(refObjSchema, 0); + } + + return true; + } + + public String getFieldName() { + return fieldName; + } + + public FieldType getFieldType() { + return fieldType; + } + + public boolean isSearchable() { + return isSearchable; + } + + public SchemaDisplay getReferencedType() { + return referencedType; + } + +} diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSchemaPage.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSchemaPage.java new file mode 100644 index 0000000000..26aea2314e --- /dev/null +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSchemaPage.java @@ -0,0 +1,54 @@ +package com.netflix.hollow.explorer.ui.pages; + +import com.netflix.hollow.explorer.ui.model.SchemaDisplayField; + +import com.netflix.hollow.explorer.ui.HollowExplorerUI; +import com.netflix.hollow.explorer.ui.model.SchemaDisplay; +import com.netflix.hollow.ui.HollowUISession; +import javax.servlet.http.HttpServletRequest; +import org.apache.velocity.VelocityContext; + +public class BrowseSchemaPage extends HollowExplorerPage { + + public BrowseSchemaPage(HollowExplorerUI ui) { + super(ui, "browse-schema.vm"); + } + + @Override + protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { + String type = req.getParameter("type"); + String expand = req.getParameter("expand"); + String collapse = req.getParameter("collapse"); + + SchemaDisplay schemaDisplay = (SchemaDisplay) session.getAttribute("schema-display-" + type); + + if(schemaDisplay == null) { + schemaDisplay = new SchemaDisplay(ui.getStateEngine().getSchema(type)); + schemaDisplay.setExpanded(true); + } + + if(expand != null) expandOrCollapse(schemaDisplay, expand.split("\\."), 1, true); + if(collapse != null) expandOrCollapse(schemaDisplay, collapse.split("\\."), 1, false); + + session.setAttribute("schema-display-" + type, schemaDisplay); + + ctx.put("schemaDisplay", schemaDisplay); + ctx.put("type", type); + } + + private void expandOrCollapse(SchemaDisplay display, String fieldPaths[], int cursor, boolean isExpand) { + if(display == null) + return; + + if(cursor >= fieldPaths.length) { + display.setExpanded(isExpand); + return; + } + + for(SchemaDisplayField field : display.getFields()) { + if(field.getFieldName().equals(fieldPaths[cursor])) + expandOrCollapse(field.getReferencedType(), fieldPaths, cursor+1, isExpand); + } + } + +} diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSelectedTypePage.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSelectedTypePage.java index d8c38a02ff..82c7af277a 100644 --- a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSelectedTypePage.java +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSelectedTypePage.java @@ -17,20 +17,20 @@ */ package com.netflix.hollow.explorer.ui.pages; -import com.netflix.hollow.tools.stringifier.HollowRecordJsonStringifier; - -import com.netflix.hollow.tools.stringifier.HollowRecordStringifier; -import com.netflix.hollow.core.read.engine.HollowTypeStateListener; import com.netflix.hollow.core.index.HollowPrimaryKeyIndex; -import com.netflix.hollow.explorer.ui.model.TypeKey; -import com.netflix.hollow.core.read.HollowReadFieldUtils; -import com.netflix.hollow.core.read.engine.object.HollowObjectTypeReadState; import com.netflix.hollow.core.index.key.PrimaryKey; +import com.netflix.hollow.core.read.HollowReadFieldUtils; import com.netflix.hollow.core.read.engine.HollowTypeReadState; +import com.netflix.hollow.core.read.engine.HollowTypeStateListener; +import com.netflix.hollow.core.read.engine.object.HollowObjectTypeReadState; import com.netflix.hollow.core.schema.HollowObjectSchema; import com.netflix.hollow.core.schema.HollowSchema; import com.netflix.hollow.core.schema.HollowSchema.SchemaType; import com.netflix.hollow.explorer.ui.HollowExplorerUI; +import com.netflix.hollow.explorer.ui.model.TypeKey; +import com.netflix.hollow.tools.stringifier.HollowRecordJsonStringifier; +import com.netflix.hollow.tools.stringifier.HollowRecordStringifier; +import com.netflix.hollow.ui.HollowUISession; import java.util.ArrayList; import java.util.BitSet; import java.util.List; @@ -44,7 +44,7 @@ public BrowseSelectedTypePage(HollowExplorerUI ui) { } @Override - protected void setUpContext(HttpServletRequest req, VelocityContext ctx) { + protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { HollowTypeReadState typeState = ui.getStateEngine().getTypeState(req.getParameter("type")); diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/HollowExplorerPage.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/HollowExplorerPage.java index d3ccaa794c..b071026ed3 100644 --- a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/HollowExplorerPage.java +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/HollowExplorerPage.java @@ -17,6 +17,8 @@ */ package com.netflix.hollow.explorer.ui.pages; +import com.netflix.hollow.ui.HollowUISession; + import com.netflix.hollow.explorer.ui.HollowExplorerUI; import java.io.Writer; import javax.servlet.http.HttpServletRequest; @@ -39,7 +41,7 @@ public HollowExplorerPage(HollowExplorerUI ui, String templateName) { this.footerTemplate = ui.getVelocityEngine().getTemplate("explorer-footer.vm"); } - public void render(HttpServletRequest req, Writer writer) { + public void render(HttpServletRequest req, HollowUISession session, Writer writer) { VelocityContext ctx = new VelocityContext(); if(ui.getHeaderDisplayString() != null) @@ -50,13 +52,13 @@ public void render(HttpServletRequest req, Writer writer) { ctx.put("basePath", ui.getBaseURLPath()); - setUpContext(req, ctx); + setUpContext(req, session, ctx); headerTemplate.merge(ctx, writer); template.merge(ctx, writer); footerTemplate.merge(ctx, writer); } - protected abstract void setUpContext(HttpServletRequest req, VelocityContext ctx); + protected abstract void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx); } diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/ShowAllTypesPage.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/ShowAllTypesPage.java index fc0f9584f4..7320a073d0 100644 --- a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/ShowAllTypesPage.java +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/ShowAllTypesPage.java @@ -24,6 +24,7 @@ import com.netflix.hollow.core.schema.HollowSchema.SchemaType; import com.netflix.hollow.explorer.ui.HollowExplorerUI; import com.netflix.hollow.explorer.ui.model.TypeOverview; +import com.netflix.hollow.ui.HollowUISession; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -38,7 +39,7 @@ public ShowAllTypesPage(HollowExplorerUI ui) { } @Override - protected void setUpContext(HttpServletRequest req, VelocityContext ctx) { + protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { String sort = req.getParameter("sort") == null ? "primaryKey" : req.getParameter("sort"); diff --git a/hollow-explorer-ui/src/main/resources/browse-schema.vm b/hollow-explorer-ui/src/main/resources/browse-schema.vm new file mode 100644 index 0000000000..0d64c908fb --- /dev/null +++ b/hollow-explorer-ui/src/main/resources/browse-schema.vm @@ -0,0 +1,54 @@ +#** + * Copyright 2017 Netflix, Inc. + * + * Licensed 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. +*# + +TYPE:    $schemaDisplay.getSchema().getName() + +#showSchema($schemaDisplay "") + +#macro(showSchema $schemaDisplay $fp) + + #set($typeName = "$schemaDisplay.getTypeName()") + +
    + #foreach($field in $schemaDisplay.getFields()) +
  • + + #if($field.getReferencedType()) + #if($field.getReferencedType().isExpanded()) + $field.getFieldName() + #else + $field.getFieldName() + #end + + ($field.getReferencedType().getTypeName() - $field.getReferencedType().getSchema().getSchemaType()) + #else + $field.getFieldName() ($field.getFieldType()) + #end + + #if($field.isSearchable()) + query + #end + + + #if($field.getReferencedType() && $field.getReferencedType().isExpanded()) + #set($childFp = "$fp.$field.getFieldName()") + #showSchema($field.getReferencedType() $childFp) + #end +
  • + #end +
+ +#end diff --git a/hollow-explorer-ui/src/main/resources/explorer-header.vm b/hollow-explorer-ui/src/main/resources/explorer-header.vm index 8156a4a89f..ba06cc89fd 100644 --- a/hollow-explorer-ui/src/main/resources/explorer-header.vm +++ b/hollow-explorer-ui/src/main/resources/explorer-header.vm @@ -27,12 +27,13 @@ #if($headerDisplayString) [ $headerDisplayString ] #end - #if($type) - [ Type: $type ] - #end #if($stateVersion) [ State Version: $stateVersion ] #end + #if($type) + [ Type: $type ] + [ Browse Data | Browse Schema ] + #end
\ No newline at end of file From 1ac76706255779543aa23dd35509de6dbf684ddc Mon Sep 17 00:00:00 2001 From: Drew Koszewnik Date: Mon, 24 Apr 2017 14:59:36 -0700 Subject: [PATCH 9/9] Hollow Explorer: Added query ability --- .../hollow/explorer/ui/HollowExplorerUI.java | 9 +- .../ui/jetty/HollowExplorerUIServer.java | 2 +- .../hollow/explorer/ui/model/QueryResult.java | 128 ++++++++++++++++++ .../explorer/ui/model/SchemaDisplay.java | 17 +++ .../explorer/ui/model/SchemaDisplayField.java | 17 +++ .../explorer/ui/pages/BrowseSchemaPage.java | 17 +++ .../ui/pages/BrowseSelectedTypePage.java | 31 +++-- .../hollow/explorer/ui/pages/QueryPage.java | 101 ++++++++++++++ .../main/resources/browse-selected-type.vm | 5 + .../src/main/resources/explorer-header.vm | 1 + .../src/main/resources/query.vm | 55 ++++++++ .../netflix/hollow/ui/HollowUISession.java | 4 + .../tools/query/HollowFieldMatchQuery.java | 56 +++++--- 13 files changed, 409 insertions(+), 34 deletions(-) create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/QueryResult.java create mode 100644 hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/QueryPage.java create mode 100644 hollow-explorer-ui/src/main/resources/query.vm diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/HollowExplorerUI.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/HollowExplorerUI.java index f3f99268ff..295c0ecb52 100644 --- a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/HollowExplorerUI.java +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/HollowExplorerUI.java @@ -17,11 +17,11 @@ */ package com.netflix.hollow.explorer.ui; -import com.netflix.hollow.explorer.ui.pages.BrowseSchemaPage; - import com.netflix.hollow.api.client.HollowClient; import com.netflix.hollow.core.read.engine.HollowReadStateEngine; +import com.netflix.hollow.explorer.ui.pages.BrowseSchemaPage; import com.netflix.hollow.explorer.ui.pages.BrowseSelectedTypePage; +import com.netflix.hollow.explorer.ui.pages.QueryPage; import com.netflix.hollow.explorer.ui.pages.ShowAllTypesPage; import com.netflix.hollow.ui.HollowUIRouter; import com.netflix.hollow.ui.HollowUISession; @@ -39,6 +39,7 @@ public class HollowExplorerUI extends HollowUIRouter { private final ShowAllTypesPage showAllTypesPage; private final BrowseSelectedTypePage browseTypePage; private final BrowseSchemaPage browseSchemaPage; + private final QueryPage queryPage; public HollowExplorerUI(String baseUrlPath, HollowClient client) { this(baseUrlPath, client, null); @@ -56,6 +57,7 @@ private HollowExplorerUI(String baseUrlPath, HollowClient client, HollowReadStat this.showAllTypesPage = new ShowAllTypesPage(this); this.browseTypePage = new BrowseSelectedTypePage(this); this.browseSchemaPage = new BrowseSchemaPage(this); + this.queryPage = new QueryPage(this); } public boolean handle(String target, HttpServletRequest req, HttpServletResponse resp) throws IOException { @@ -73,6 +75,9 @@ public boolean handle(String target, HttpServletRequest req, HttpServletResponse } else if("schema".equals(pageName)) { browseSchemaPage.render(req, session, resp.getWriter()); return true; + } else if("query".equals(pageName)) { + queryPage.render(req, session, resp.getWriter()); + return true; } return false; diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/HollowExplorerUIServer.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/HollowExplorerUIServer.java index 8c50268671..afd8cfae14 100644 --- a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/HollowExplorerUIServer.java +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/jetty/HollowExplorerUIServer.java @@ -1,6 +1,6 @@ /* * - * Copyright 2016 Netflix, Inc. + * Copyright 2017 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/QueryResult.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/QueryResult.java new file mode 100644 index 0000000000..313b864e45 --- /dev/null +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/QueryResult.java @@ -0,0 +1,128 @@ +/* + * + * Copyright 2017 Netflix, Inc. + * + * Licensed 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 com.netflix.hollow.explorer.ui.model; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +public class QueryResult { + + private final List queryClauses; + + private final Map queryMatches; + + public QueryResult(QueryClause clause, Map matches) { + this.queryClauses = new ArrayList(); + this.queryMatches = matches; + this.queryClauses.add(clause); + } + + public List getQueryClauses() { + return queryClauses; + } + + public String getQueryDisplayString() { + StringBuilder builder = new StringBuilder(); + + for(int i=0;i 0) + builder.append(" AND "); + builder.append(queryClauses.get(i)); + } + + return builder.toString(); + } + + public Map getQueryMatches() { + return queryMatches; + } + + public List getTypeMatches() { + List list = new ArrayList(); + + for(Map.Entry entry : queryMatches.entrySet()) { + int numTypeMatches = entry.getValue().cardinality(); + if(numTypeMatches > 0) + list.add(new QueryTypeMatches(entry.getKey(), numTypeMatches)); + } + + Collections.sort(list, new Comparator() { + public int compare(QueryTypeMatches o1, QueryTypeMatches o2) { + return Integer.compare(o2.getNumMatches(), o1.getNumMatches()); + } + }); + + return list; + } + + public static class QueryClause { + private final String type; + private final String field; + private final String value; + + public QueryClause(String type, String field, String value) { + this.type = type; + this.field = field; + this.value = value; + } + + public String getType() { + return type; + } + public String getField() { + return field; + } + public String getValue() { + return value; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + + if(type != null) + builder.append(type).append("."); + + builder.append(field).append("=\"").append(value).append("\""); + + return builder.toString(); + } + } + + public static class QueryTypeMatches { + private final String typeName; + private final int numMatches; + + public QueryTypeMatches(String typeName, int numMatches) { + this.typeName = typeName; + this.numMatches = numMatches; + } + + public String getTypeName() { + return typeName; + } + public int getNumMatches() { + return numMatches; + } + } + +} diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplay.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplay.java index 354efa440d..5a78042919 100644 --- a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplay.java +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplay.java @@ -1,3 +1,20 @@ +/* + * + * Copyright 2017 Netflix, Inc. + * + * Licensed 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 com.netflix.hollow.explorer.ui.model; import com.netflix.hollow.core.schema.HollowMapSchema; diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplayField.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplayField.java index a7d20a8f39..e213e99aa9 100644 --- a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplayField.java +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/model/SchemaDisplayField.java @@ -1,3 +1,20 @@ +/* + * + * Copyright 2017 Netflix, Inc. + * + * Licensed 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 com.netflix.hollow.explorer.ui.model; import com.netflix.hollow.core.schema.HollowCollectionSchema; diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSchemaPage.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSchemaPage.java index 26aea2314e..467f231d85 100644 --- a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSchemaPage.java +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSchemaPage.java @@ -1,3 +1,20 @@ +/* + * + * Copyright 2017 Netflix, Inc. + * + * Licensed 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 com.netflix.hollow.explorer.ui.pages; import com.netflix.hollow.explorer.ui.model.SchemaDisplayField; diff --git a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSelectedTypePage.java b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSelectedTypePage.java index 82c7af277a..942de5f4d8 100644 --- a/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSelectedTypePage.java +++ b/hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSelectedTypePage.java @@ -17,6 +17,8 @@ */ package com.netflix.hollow.explorer.ui.pages; +import com.netflix.hollow.explorer.ui.model.QueryResult; + import com.netflix.hollow.core.index.HollowPrimaryKeyIndex; import com.netflix.hollow.core.index.key.PrimaryKey; import com.netflix.hollow.core.read.HollowReadFieldUtils; @@ -45,7 +47,6 @@ public BrowseSelectedTypePage(HollowExplorerUI ui) { @Override protected void setUpContext(HttpServletRequest req, HollowUISession session, VelocityContext ctx) { - HollowTypeReadState typeState = ui.getStateEngine().getTypeState(req.getParameter("type")); HollowPrimaryKeyIndex idx = findPrimaryKeyIndex(typeState); @@ -56,11 +57,23 @@ protected void setUpContext(HttpServletRequest req, HollowUISession session, Vel String displayFormat = "json".equals(req.getParameter("display")) ? "json" : "text"; - BitSet populatedOrdinals = typeState.getPopulatedOrdinals(); - int currentOrdinal = populatedOrdinals.nextSetBit(0); + BitSet selectedOrdinals = typeState.getPopulatedOrdinals(); + + if("true".equals(req.getParameter("clearQuery"))) + session.clearAttribute("query-result"); + + if(session.getAttribute("query-result") != null) { + QueryResult queryResult = (QueryResult) session.getAttribute("query-result"); + selectedOrdinals = queryResult.getQueryMatches().get(typeState.getSchema().getName()); + if(selectedOrdinals == null) + selectedOrdinals = new BitSet(); + + ctx.put("filteredByQuery", queryResult.getQueryDisplayString()); + } + int currentOrdinal = selectedOrdinals.nextSetBit(0); for(int i=0;i allTypes = new ArrayList(); + for(HollowSchema schema : ui.getStateEngine().getSchemas()) + allTypes.add(schema.getName()); + Collections.sort(allTypes); + + if(field != null && queryValue != null) { + HollowFieldMatchQuery query = new HollowFieldMatchQuery(ui.getStateEngine()); + Map queryMatches = type != null ? query.findMatchingRecords(type, field, queryValue) : query.findMatchingRecords(field, queryValue); + TransitiveSetTraverser.addReferencingOutsideClosure(ui.getStateEngine(), queryMatches); + + QueryClause queryClause = new QueryClause(type, field, queryValue); + + QueryResult result = (QueryResult) session.getAttribute("query-result"); + if(result == null) { + result = new QueryResult(queryClause, queryMatches); + session.setAttribute("query-result", result); + } else { + result.getQueryClauses().add(queryClause); + booleanAndQueryMatches(result.getQueryMatches(), queryMatches); + } + + type = null; + field = null; + queryValue = null; + } + + ctx.put("allTypes", allTypes); + ctx.put("selectedType", type); + ctx.put("selectedField", field); + ctx.put("queryValue", queryValue); + ctx.put("queryResult", session.getAttribute("query-result")); + } + + private void booleanAndQueryMatches(Map existingQueryMatches, Map newQueryMatches) { + Iterator> iter = existingQueryMatches.entrySet().iterator(); + while(iter.hasNext()) { + Map.Entry existingEntry = iter.next(); + BitSet newTypeMatches = newQueryMatches.get(existingEntry.getKey()); + if(newTypeMatches != null) { + existingEntry.getValue().and(newTypeMatches); + } else { + iter.remove(); + } + } + } + +} diff --git a/hollow-explorer-ui/src/main/resources/browse-selected-type.vm b/hollow-explorer-ui/src/main/resources/browse-selected-type.vm index 82d2442da3..3bb2164645 100644 --- a/hollow-explorer-ui/src/main/resources/browse-selected-type.vm +++ b/hollow-explorer-ui/src/main/resources/browse-selected-type.vm @@ -14,6 +14,11 @@ * limitations under the License. *# + +#if($filteredByQuery) +NOTE: Results are filtered by query $filteredByQuery. clear +#end + diff --git a/hollow-explorer-ui/src/main/resources/explorer-header.vm b/hollow-explorer-ui/src/main/resources/explorer-header.vm index ba06cc89fd..d51783f0b5 100644 --- a/hollow-explorer-ui/src/main/resources/explorer-header.vm +++ b/hollow-explorer-ui/src/main/resources/explorer-header.vm @@ -34,6 +34,7 @@ #end +

\ No newline at end of file diff --git a/hollow-explorer-ui/src/main/resources/query.vm b/hollow-explorer-ui/src/main/resources/query.vm new file mode 100644 index 0000000000..6ba0de6c09 --- /dev/null +++ b/hollow-explorer-ui/src/main/resources/query.vm @@ -0,0 +1,55 @@ +#** + * Copyright 2017 Netflix, Inc. + * + * Licensed 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. +*# + +#if($queryResult) + Augment Current Query: +#else + Search: +#end + +
+ + + + + + + +
+ +#if($queryResult) + +Current Query: $queryResult.getQueryDisplayString() clear + +

+ +RESULTS: + +

    + #foreach($typeMatches in $queryResult.getTypeMatches()) +
  • + + $typeMatches.getTypeName(): $typeMatches.getNumMatches() records match + +
  • + #end +
+#end \ No newline at end of file diff --git a/hollow-ui-tools/src/main/java/com/netflix/hollow/ui/HollowUISession.java b/hollow-ui-tools/src/main/java/com/netflix/hollow/ui/HollowUISession.java index 6a95965013..21e6d537a3 100644 --- a/hollow-ui-tools/src/main/java/com/netflix/hollow/ui/HollowUISession.java +++ b/hollow-ui-tools/src/main/java/com/netflix/hollow/ui/HollowUISession.java @@ -37,6 +37,10 @@ public HollowUISession() { this.sessionParams = new ConcurrentHashMap(); } + public void clearAttribute(String param) { + sessionParams.remove(param); + } + public void setAttribute(String param, Object value) { sessionParams.put(param, value); } diff --git a/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java b/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java index 03f8ad4f0b..38f2149321 100644 --- a/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java +++ b/hollow/src/main/java/com/netflix/hollow/tools/query/HollowFieldMatchQuery.java @@ -40,35 +40,47 @@ public Map findMatchingRecords(String fieldName, String fieldVal Map matches = new HashMap(); for(HollowTypeReadState typeState : readEngine.getTypeStates()) { + augmentMatchingRecords(typeState, fieldName, fieldValue, matches); + } + + return matches; + } + + public Map findMatchingRecords(String typeName, String fieldName, String fieldValue) { + Map matches = new HashMap(); + + HollowTypeReadState typeState = readEngine.getTypeState(typeName); + if(typeState != null) + augmentMatchingRecords(typeState, fieldName, fieldValue, matches); + + return matches; + } + + private void augmentMatchingRecords(HollowTypeReadState typeState, String fieldName, String fieldValue, Map matches) { + if(typeState.getSchema().getSchemaType() == SchemaType.OBJECT) { + HollowObjectSchema schema = (HollowObjectSchema)typeState.getSchema(); - if(typeState.getSchema().getSchemaType() == SchemaType.OBJECT) { - HollowObjectSchema schema = (HollowObjectSchema)typeState.getSchema(); - - for(int i=0;i 0) - matches.put(typeState.getSchema().getName(), typeQueryMatches); } + + if(typeQueryMatches != null && typeQueryMatches.cardinality() > 0) + matches.put(typeState.getSchema().getName(), typeQueryMatches); } } - } - - return matches; } private BitSet attemptReferenceTraversalQuery(HollowObjectTypeReadState typeState, int fieldIdx, String fieldValue) {