http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/webapp/scripts/odf-client.js
----------------------------------------------------------------------
diff --git a/odf/odf-web/src/main/webapp/scripts/odf-client.js 
b/odf/odf-web/src/main/webapp/scripts/odf-client.js
new file mode 100755
index 0000000..de64367
--- /dev/null
+++ b/odf/odf-web/src/main/webapp/scripts/odf-client.js
@@ -0,0 +1,1087 @@
+/**
+ *
+ * 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.
+ */
+require("bootstrap/dist/css/bootstrap.min.css");
+
+var $ = require("jquery");
+var bootstrap = require("bootstrap");
+
+var React = require("react");
+var ReactDOM = require("react-dom");
+var LinkedStateMixin = require('react-addons-linked-state-mixin');
+var ReactBootstrap = require("react-bootstrap");
+
+var Nav = ReactBootstrap.Nav;
+var NavItem = ReactBootstrap.NavItem;
+var Navbar = ReactBootstrap.Navbar;
+var NavDropdown = ReactBootstrap.NavDropdown;
+var Button = ReactBootstrap.Button;
+var Grid = ReactBootstrap.Grid;
+var Row = ReactBootstrap.Row;
+var Col = ReactBootstrap.Col;
+var Table = ReactBootstrap.Table;
+var Modal = ReactBootstrap.Modal;
+var Alert = ReactBootstrap.Alert;
+var Panel = ReactBootstrap.Panel;
+var Label = ReactBootstrap.Label;
+var Input = ReactBootstrap.Input;
+var Jumbotron = ReactBootstrap.Jumbotron;
+var Image = ReactBootstrap.Image;
+var Dropdown = ReactBootstrap.Dropdown;
+var DropdownButton = ReactBootstrap.DropdownButton;
+var CustomMenu = ReactBootstrap.CustomMenu;
+var MenuItem = ReactBootstrap.MenuItem;
+var Tooltip = ReactBootstrap.Tooltip;
+var OverlayTrigger = ReactBootstrap.OverlayTrigger;
+var Glyphicon = ReactBootstrap.Glyphicon;
+
+var ODFGlobals = require("./odf-globals.js");
+var OdfAnalysisRequest = require("./odf-analysis-request.js");
+var NewAnalysisRequestButton = OdfAnalysisRequest.NewCreateAnnotationsButton;
+var ODFBrowser = require("./odf-metadata-browser.js");
+var Utils = require("./odf-utils.js");
+var AtlasHelper = Utils.AtlasHelper;
+var AJAXCleanupMixin = require("./odf-mixins.js");
+var UISpec = require("./odf-ui-spec.js");
+
+
+var knownAnnotations = {
+       "Default": [{value : "annotationType", style : "primary", label: 
"Unknown"}],
+       "ColumnAnalysisColumnAnnotation" : [{value: 
"jsonProperties.inferredDataClass.className", style: "danger" , label: "Class 
name"}, {value: "jsonProperties.inferredDataType.type", style: "info", label 
:"Datatype"}],
+       "DataQualityColumnAnnotation": [{style: "warning", value: 
"jsonProperties.qualityScore" , label: "Data quality score"}],
+    "MatcherAnnotation": [{style: "success", value: 
"jsonProperties.termAssignments", label: "Matching terms"}]
+};
+
+////////////////////////////////////////////////////////////////
+// toplevel navigation bar
+
+const constants_ODFNavBar = {
+  odfDataLakePage: "navKeyDataLakePage",
+  odfTermPage: "navKeyTermPage"
+}
+
+var ODFNavBar = React.createClass({
+   render: function() {
+       return (
+         <Navbar inverse>
+           <Navbar.Header>
+             <Navbar.Brand>
+               <b>Shop for Data Application, powered by Open Discovery 
Framework</b>
+             </Navbar.Brand>
+             <Navbar.Toggle />
+           </Navbar.Header>
+           <Navbar.Collapse>
+             <Nav pullRight activeKey={this.props.activeKey} 
onSelect={this.props.selectCallback}>
+               <NavItem eventKey={constants_ODFNavBar.odfDataLakePage} 
href="#">Data Lake Browser</NavItem>
+               <NavItem eventKey={constants_ODFNavBar.odfTermPage} 
href="#">Glossary</NavItem>
+             </Nav>
+           </Navbar.Collapse>
+         </Navbar>
+       );
+   }
+});
+
+var ODFAnnotationLegend = React.createClass({
+
+       render : function(){
+               var items = [];
+               $.each(knownAnnotations, function(key, val){
+                       $.each(val, function(key2, item){
+                               items.push(<Label key={key + "_" + key2} 
bsStyle={item.style}>{item.label}</Label>);
+                       });
+               });
+
+               return <div>{items}</div>;
+       }
+
+});
+
+var ODFAnnotationMarker = React.createClass({
+
+       render : function(){
+               var annotationKey = "Default";
+               var annotationLabels = [];
+               if(this.props.annotation && 
knownAnnotations[this.props.annotation.annotationType]){
+                       annotationKey = this.props.annotation.annotationType;
+                       var tooltip = <Tooltip 
id={this.props.annotation.annotationType}>{this.props.annotation.annotationType}<br/>{this.props.annotation.summary}</Tooltip>
+                       $.each(knownAnnotations[annotationKey], function(key, 
val){
+                               var style = val.style;
+                               var value = 
ODFGlobals.getPathValue(this.props.annotation, val.value);
+                               if (annotationKey === "MatcherAnnotation") {
+                                       value = value[0].matchingString; // if 
no abbreviation matches this will be the term; ideally it should be based on 
the OMBusinessTerm reference
+                               }
+                               else if(value && !isNaN(value)){
+                                       value = Math.round(value*100) + " %";
+                               }
+                               annotationLabels.push(<OverlayTrigger key={key} 
placement="top" overlay={tooltip}><Label style={{margin: "5px"}} 
bsStyle={style}>{value}</Label></OverlayTrigger>);
+                       }.bind(this));
+               }else{
+                       var tooltip = <Tooltip 
id={this.props.annotation.annotationType}>{this.props.annotation.annotationType}<br/>{this.props.annotation.summary}</Tooltip>
+                       annotationLabels.push(<OverlayTrigger 
key="unknownAnnotation" placement="top" overlay={tooltip}><Label 
style={{margin: "5px"}} 
bsStyle={knownAnnotations[annotationKey][0].style}>{this.props.annotation.annotationType}</Label></OverlayTrigger>);
+               }
+
+               return <div style={this.props.style}>{annotationLabels}</div>;
+       }
+});
+
+
+var AnnotationsColumn = React.createClass({
+       mixins : [AJAXCleanupMixin],
+
+       getInitialState : function(){
+               return {annotations: []};
+       },
+
+       componentDidMount : function() {
+               if(this.props.annotations){
+                       this.setState({loadedAnnotations : 
this.props.annotations});
+                       return;
+               }
+
+               if(this.props.annotationReferences){
+                       
this.loadColumnAnnotations(this.props.annotationReferences);
+               }
+       },
+
+       componentWillReceiveProps : function(nextProps){
+               if(!this.isMounted()){
+                       return;
+               }
+
+               if(nextProps.annotations){
+                       this.setState({loadedAnnotations : 
nextProps.annotations});
+                       return;
+               }
+       },
+
+       render : function(){
+               if(this.state){
+                       var annotations = this.state.loadedAnnotations;
+                       if(!annotations || annotations.length > 0 && 
annotations[0].repositoryId){
+                               return <noscript/>;
+                       }
+
+                       var processedTypes = [];
+                       var colAnnotations = [];
+                       $.each(annotations, function(key, val){
+                               if(processedTypes.indexOf(val.annotationType) 
== -1){
+                                       processedTypes.push(val.annotationType);
+                                       var style = {float: "left"};
+                                       if(key % 6 == 0){
+                                               style = {clear: "both"};
+                                       }
+
+                                       var summary = (val.summary ? 
val.summary : "");
+                                       
colAnnotations.push(<ODFAnnotationMarker style={style} key={key} 
annotation={val}/>);
+                               }
+                       });
+
+                       return <div>{colAnnotations}</div>;
+               }
+               return <noscript/>;
+       }
+
+});
+
+var QualityScoreFilter = React.createClass({
+
+       getInitialState : function(){
+               return {key: "All", val : "0", showMenu : false};
+       },
+
+       onSelect : function(obj, key){
+
+               if(obj.target.tagName != "INPUT"){
+                       this.setState({key: key});
+                       var equation = "All";
+                       if(key != "All"){
+                               if(this.refs.numberInput.getValue().trim() == 
""){
+                                       return;
+                               }
+                               equation = key + 
this.refs.numberInput.getValue();
+                       }
+                       this.props.onFilter(equation);
+               }
+       },
+
+       textChange : function(event){
+               var equation = "All";
+               if(this.state.key != "All"){
+                       if(this.refs.numberInput.getValue().trim() == ""){
+                               return;
+                       }
+                       equation = this.state.key + 
this.refs.numberInput.getValue();
+               }
+               this.props.onFilter(equation);
+       },
+
+       render : function(){
+               var items = [];
+               var values = ["<", "<=", "==", ">=", ">", "!=", "All"];
+               $.each(values, function(key, val){
+                       items.push(<MenuItem onSelect={this.onSelect} id={val} 
key={key} eventKey={val}>{val}</MenuItem>)
+               }.bind(this));
+
+               var menu = <div bsRole="menu" className={"dropdown-menu"}>
+                       <h5 style={{float: "left", marginLeft: "15px"}}><Label 
ref="typeLabel">{this.state.key}</Label></h5>
+                       <Input style={{width: "100px"}} ref="numberInput" 
onChange={this.textChange} type="number" defaultValue="1"/>
+                       {items}
+               </div>;
+
+               return <div style={this.props.style}  >
+                       <Dropdown id="quality score select" 
onSelect={this.onSelect} open={this.state.showMenu} onToggle={function(){}}>
+                               <Button bsRole="toggle" 
onClick={function(){this.setState({showMenu: 
!this.state.showMenu})}.bind(this)}>Qualityscore filter</Button>
+                               {menu}
+                       </Dropdown>
+               </div>;
+       }
+});
+
+var DataClassFilter = React.createClass({
+
+       defaultClasses : ["US Zip", "Credit Card"],
+
+       render : function(){
+               var items = [];
+               var classes = (this.props.dataClasses ? 
this.props.dataClasses.slice() : this.defaultClasses);
+               classes.push("All");
+               $.each(classes, function(key, val){
+                       items.push(<MenuItem id={val} key={key} 
eventKey={val}>{val}</MenuItem>)
+               });
+
+               return <div style={this.props.style}>
+                       <DropdownButton id="Data class filter" 
onSelect={function(obj, key){this.props.onFilter(key)}.bind(this)} title="Data 
Class filter">
+                               {items}
+                       </DropdownButton>
+               </div>;
+       }
+});
+
+
+var FilterMenu = React.createClass({
+
+       getInitialState : function(){
+               return {showMenu : false, dataClassFilter: "All", 
qualityScoreFilter: "All"};
+       },
+
+       onQualityScoreFilter: function(param){
+               this.setState({qualityScoreFilter: param});
+               if(this.props.onFilter){
+                       this.props.onFilter({dataClassFilter: 
this.state.dataClassFilter, qualityScoreFilter: param});
+               }
+       },
+
+       onDataClassFilter : function(param){
+               this.setState({dataClassFilter: param});
+               if(this.props.onFilter){
+                       this.props.onFilter({dataClassFilter: param, 
qualityScoreFilter: this.state.qualityScoreFilter});
+               }
+       },
+
+       render : function(){
+               var menu = <div bsRole="menu" className={"dropdown-menu"}>
+                       <QualityScoreFilter 
onFilter={this.onQualityScoreFilter}/>
+                       <br />
+                       <DataClassFilter dataClasses={this.props.dataClasses} 
onFilter={this.onDataClassFilter}  />
+               </div>;
+
+               return <div style={this.props.style}  >
+                       <Dropdown id="filter menu" open={this.state.showMenu} 
onToggle={function(){}}>
+                               <Button bsRole="toggle" 
onClick={function(){this.setState({showMenu: 
!this.state.showMenu})}.bind(this)}>Filter annotations</Button>
+                               {menu}
+                       </Dropdown>
+               </div>;
+       }
+
+});
+
+
+var SelectCheckbox = React.createClass({
+
+       getInitialState : function(){
+               return {selected : this.props.asset.isSelected};
+       },
+
+       componentWillReceiveProps : function(nextProps){
+               if(!this.isMounted()){
+                       return;
+               }
+               if(nextProps.asset.reference.id != 
this.props.asset.reference.id){
+                       this.setState({selected : nextProps.asset.isSelected});
+               }
+       },
+
+       onChange : function(selected){
+               if(this.props.onChange){
+                       this.props.onChange(selected);
+               }
+               this.setState({selected : selected});
+       },
+
+       render : function(){
+               return <div><Input style={{marginTop: "-6px"}} type="checkbox" 
label=" " checked={this.state.selected} onChange={function(e){
+                       this.onChange($(e.target).prop("checked"));
+               }.bind(this)}/></div>;
+       }
+
+});
+
+var ODFDataLakePage = React.createClass({
+
+       columnAnnotations : {},
+
+       getInitialState : function(){
+               return {
+                       ajaxAborts : [],
+                       sourceLoading: false,
+                       columns: [],
+                       dataClasses: [],
+                       qualityScoreFilter: "All",
+                       dataClassFilter: "All",
+                       importFeedback: {msg: null, style: "primary"}
+               };
+       },
+
+       componentDidMount : function() {
+               this.loadSources();
+       },
+
+       loadSources : function(){
+               this.searchAtlasMetadata("from RelationalDataSet", 
function(data){
+                        $.each(data, function(key, source){
+                                source.isSelected = false;
+                         });
+                       this.setState({filteredSources: data, sources: data});
+               }.bind(this));
+       },
+
+       searchAtlasMetadata : function(query, successCallback, errorCallback) {
+               var url = ODFGlobals.metadataUrl + "/search?" + $.param({query: 
query});
+               $.ajax({
+                       url: url,
+                       dataType: 'json',
+                       type: 'GET',
+                       success: function(data) {
+                               successCallback(data);
+                       },
+                       error: function(xhr, status, err) {
+                               console.error(url, status, err.toString());
+                               var msg = "Error while loading metadata: " + 
err.toString();
+                               if(errorCallback){
+                                       errorCallback(msg);
+                               }
+                       }
+               });
+        },
+
+       load : function(assetRef){
+               $.each(this.state.ajaxAborts, function(key, abort){
+                       if(abort && abort.call){
+                               abort.call();
+                       }
+               });
+               this.setState({ajaxAborts : []});
+
+               var req = AtlasHelper.loadAtlasAsset(assetRef, function(data){
+                       var source = data;
+                       var refresh = false;
+                       if(this.state == null || this.state.selectedTable == 
null || this.state.selectedTable.reference.id != source.reference.id){
+                               console.log("set state source " + new 
Date().toLocaleString());
+                               this.setState({selectedTable: source});
+                               if(source.annotations == null){
+                                       source.annotations = [];
+                               }
+                               if(source.columns == null){
+                                       source.columns = [];
+                               }
+                       }else{
+                               source.annotations = 
this.state.selectedTable.annotations;
+                               refresh = true;
+                       }
+
+                       this.loadSourceAnnotations(source, refresh);
+                       this.loadColumns(source, refresh);
+               }.bind(this), function(){
+
+               });
+       },
+
+       loadSourceAnnotations : function(source, refresh){
+               if(!refresh || !source.loadedAnnotations){
+                       source.loadedAnnotations = [];
+               }
+        var reqs = AtlasHelper.loadMostRecentAnnotations(source.reference, 
function(annotationList){
+            if (refresh) {
+               var newAnnotations = [];
+               if(source.loadedAnnotations.length > 0){
+                       $.each(annotationList, function(key, val){
+                               
if(!this.atlasAssetArrayContains(source.loadedAnnotations, val)){
+                                       newAnnotations.push(val);
+                               }
+                       }.bind(this));
+               }else{
+                       newAnnotations = annotationList;
+               }
+                source.loadedAnnotations = newAnnotations;
+            }else{
+               source.loadedAnnotations = annotationList;
+            }
+            console.log("set state source anns " + new 
Date().toLocaleString());
+            this.setState({selectedTable: source});
+        }.bind(this), function(){
+
+        });
+
+        var ajaxAborts = [];
+               $.each(reqs, function(key, req){
+                       ajaxAborts.push(req.abort);
+               }.bind(this))
+               this.setState({ajaxAborts : ajaxAborts});
+       },
+
+       atlasAssetArrayContains : function(array, obj){
+               for(var no = 0; no < array.length; no++){
+                       var val = array[no];
+                       if(val && val.reference && obj && obj.reference && 
val.reference.id == obj.reference.id){
+                               return true;
+                       }
+               }
+               return false;
+       },
+
+       loadColumns : function(dataSet, refresh){
+               var columns = [];
+               if(refresh){
+                       columns = this.state.columns;
+               }
+               var reqs = AtlasHelper.loadRelationalDataSet(dataSet, 
function(result){
+                       var foundAnnotations = false;
+                       if(!refresh){
+                               $.each(result, function(key, col){
+                                       if(col.annotations && 
col.annotations.length > 0){
+                                               foundAnnotations = true;
+                                       }
+                                       if(col.isSelected == null || 
col.isSelected == undefined){
+                                               col.isSelected = false;
+                                       }
+                                       columns.push(col);
+                               });
+                       }else{
+                               //if result size is different, reset completely
+                               if(result.length != columns.length){
+                                       columns = [];
+                               }
+                               //if the old array contains any column that is 
not in the new columns, reset completely
+                               $.each(columns, function(key, col){
+                                       
if(!this.atlasAssetArrayContains(result, col)){
+                                               columns = [];
+                                       }
+                               }.bind(this));
+                               $.each(result, function(key, col){
+                                       //only add new columns
+                                       
if(!this.atlasAssetArrayContains(columns, col)){
+                                               columns.push(col);
+                                       }
+                                       if(col.annotations && 
col.annotations.length > 0){
+                                               for(var no = 0; no < 
columns.length; no++){
+                                                       if(columns[no] == null 
|| columns[no] == undefined){
+                                                               col.isSelected 
= false;
+                                                       }
+                                                       
if(columns[no].reference.id == col.reference.id){
+                                                               
columns[no].annotations = col.annotations;
+                                                               break;
+                                                       }
+                                               }
+                                               foundAnnotations = true;
+                                       }
+                               }.bind(this));
+                       }
+
+                       if(!foundAnnotations){
+                               if(!Utils.arraysEqual(this.state.columns, 
columns)){
+                                       console.log("set state columns " + new 
Date().toLocaleString());
+                                       this.setState({currentlyLoading : 
false, columns: columns, filteredColumns: columns});
+                               }else{
+                                       console.log("columns same, no 
annotations, dont update");
+                               }
+                       }else{
+                               this.loadColumnAnnotations(columns, refresh);
+                       }
+               }.bind(this), function(){
+
+               });
+
+        var ajaxAborts = [];
+               $.each(reqs, function(key, req){
+                       ajaxAborts.push(req.abort);
+               }.bind(this))
+               this.setState({ajaxAborts : ajaxAborts});
+       },
+
+       loadColumnAnnotations : function(columns, refresh){
+               var annotationRefs = [];
+               $.each(columns, function(key, col){
+                       if(!refresh || !col.loadedAnnotations){
+                               col.loadedAnnotations = [];
+                       }
+               });
+
+               var requests = [];
+               var annotationsChanged = false;
+               var dataClasses = [];
+               $.each(columns, function(key, column){
+                       var req = 
AtlasHelper.loadMostRecentAnnotations(column.reference, function(annotations){
+                               $.each(annotations, function(key, annotation){
+                                       
if(!this.atlasAssetArrayContains(column.loadedAnnotations, annotation)){
+                                               annotationsChanged = true;
+                                               
column.loadedAnnotations.push(annotation);
+                                       }
+                                       if(annotation &&
+                                                       
annotation.inferredDataClass && 
dataClasses.indexOf(annotation.inferredDataClass.className) == -1){
+                                               
dataClasses.push(annotation.inferredDataClass.className);
+                                       }
+                               }.bind(this));
+                       }.bind(this));
+                       requests.push(req);
+               }.bind(this));
+
+               $.when.apply(undefined, requests).done(function(){
+                       if(annotationsChanged){
+                               console.log("set state column anns " + new 
Date().toLocaleString());
+                               this.setState({currentlyLoading : false, 
columns: columns, filteredColumns: columns, dataClasses: dataClasses});
+                       }else{
+                               if(!Utils.arraysEqual(this.state.columns, 
columns)){
+                                       console.log("set state column anns " + 
new Date().toLocaleString());
+                                       this.setState({currentlyLoading : 
false, columns: columns, filteredColumns: columns});
+                               }else{
+                                       console.log("columns same, annotations 
same, dont update");
+                               }
+                       }
+               }.bind(this));
+
+        var ajaxAborts = [];
+               $.each(requests, function(key, req){
+                       ajaxAborts.push(req.abort);
+               }.bind(this));
+               this.setState({ajaxAborts : ajaxAborts});
+       },
+
+       storeColumnAnnotation : function(columnId, annotation){
+               if(!this.columnAnnotations[columnId]){
+                       this.columnAnnotations[columnId] = [];
+               }
+               
if(!this.atlasAssetArrayContains(this.columnAnnotations[columnId], annotation)){
+                       this.columnAnnotations[columnId].push(annotation);
+               }
+       },
+
+       componentWillUnmount : function() {
+               if(this.refreshInterval){
+                       clearInterval(this.refreshInterval);
+               }
+       },
+
+       referenceClick : function(asset){
+               if(this.state == null || this.state.selectedTable == null || 
this.state.selectedTable.reference.id != asset.reference.id){
+                       if(this.refreshInterval){
+                               clearInterval(this.refreshInterval);
+                       }
+                       this.setState({currentlyLoading : true, selectedTable: 
null, filteredColumns : [], columns: []});
+                       this.load(asset.reference);
+                       this.refreshInterval = 
setInterval(function(){this.load(asset.reference)}.bind(this), 15000);
+               }
+       },
+
+       doFilter : function(params){
+               var columns = this.state.columns.slice();
+               var filteredColumns = this.filterOnDataQualityScore(columns, 
params.qualityScoreFilter);
+               filteredColumns = this.filterOnDataClass(filteredColumns, 
params.dataClassFilter);
+               this.setState({filteredColumns: filteredColumns});
+       },
+
+       filterOnDataQualityScore : function(columns, equation){
+               if(equation.indexOf("All")>-1){
+                       return columns;
+               }
+
+               var columns = columns.slice();
+               var matchedColumns = [];
+               $.each(columns, function(index, col){
+                       var match = false;
+                       $.each(col.loadedAnnotations, function(k, annotation){
+                               if(equation && annotation.qualityScore){
+                                               
if(eval("annotation.qualityScore" + equation)){
+                                                       
if(matchedColumns.indexOf(col) == -1){
+                                                               
matchedColumns.push(col);
+                                                       }
+                                               }
+                               }
+                       }.bind(this));
+               }.bind(this));
+
+               return matchedColumns;
+       },
+
+       filterOnDataClass : function(columns, key){
+               if(key == "All"){
+                       return columns;
+               }
+               var matchedColumns = [];
+               $.each(columns, function(index, col){
+                       var match = false;
+                       $.each(col.loadedAnnotations, function(k, annotation){
+                               if(annotation.inferredDataClass &&
+                                               
annotation.inferredDataClass.className == key){
+                                       if(matchedColumns.indexOf(col) == -1){
+                                               matchedColumns.push(col);
+                                       }
+                               }
+                       });
+               });
+
+               return matchedColumns;
+       },
+
+       doImport : function(){
+               var params = {
+                               jdbcString : this.refs.jdbcInput.getValue(),
+                               user: this.refs.userInput.getValue(),
+                               password : this.refs.passInput.getValue(),
+                               database :this.refs.dbInput.getValue(),
+                               schema : this.refs.schemaInput.getValue(),
+                               table : this.refs.sourceInput.getValue()
+               };
+
+               this.setState({importingTable : true, tableWasImported : true, 
});
+
+               $.ajax({
+                     url: ODFGlobals.importUrl,
+                     contentType: "application/json",
+                     dataType: 'json',
+                     type: 'POST',
+                     data: JSON.stringify(params),
+                     success: function(data) {
+                         this.setState({importFeedback: {msg: "Registration 
successful!", style: "primary"}, importingTable: false});
+                     }.bind(this),
+                     error: function(xhr, status, err) {
+                                 if(this.isMounted()){
+                                       var errorMsg = status;
+                                       if(xhr.responseJSON && 
xhr.responseJSON.error){
+                                               errorMsg = 
xhr.responseJSON.error;
+                                       }
+                                       var msg = "Table could not be 
registered: " + errorMsg + ", " + err.toString();
+                                       this.setState({importFeedback: {msg: 
msg, style: "warning"}, importingTable: false});
+                                 }
+                     }.bind(this)
+                   });
+       },
+
+       closeImportingDialog : function(){
+               if(this.state.importingTable){
+                       return;
+               }
+
+               var newState = {tableWasImported: false, showImportDialog : 
false, importFeedback: {msg: null}};
+               if(this.state.tableWasImported){
+                       this.loadSources();
+                       newState.sources = null;
+                       newState.filteredSources = null;
+               }
+               this.setState(newState);
+       },
+
+       shopData : function(){
+               var selectedColumns = [];
+               var selectedSources = [];
+
+               $.each(this.state.columns, function(key, col){
+                       if(col.isSelected){
+                               selectedColumns.push(col);
+                       }
+               });
+
+               $.each(this.state.sources, function(key, src){
+                       if(src.isSelected){
+                               selectedSources.push(src);
+                       }
+               });
+
+               console.log("Do something with the selected columns!")
+               console.log(selectedColumns);
+               console.log(selectedSources);
+       },
+
+       filterSources : function(e){
+               var value = $(e.target).val();
+               var filtered = [];
+               if(value.trim() == ""){
+                       filtered = this.state.sources;
+               }else{
+                       $.each(this.state.sources, function(key, source){
+                               
if(source.name.toUpperCase().indexOf(value.toUpperCase()) > -1){
+                                       filtered.push(source);
+                               }
+                       });
+               }
+               this.setState({filteredSources : filtered});
+       },
+
+       storeImportDialogDefaults: function() {
+               var defaultValues = {
+                  "jdbcInput": this.refs.jdbcInput.getValue(),
+                  "userInput": this.refs.userInput.getValue(),
+                  "passInput": this.refs.passInput.getValue(),
+                  "dbInput": this.refs.dbInput.getValue(),
+                  "schemaInput": this.refs.schemaInput.getValue(),
+                  "sourceInput": this.refs.sourceInput.getValue(),
+               };
+               localStorage.setItem("odf-client-defaults", 
JSON.stringify(defaultValues) );
+       },
+
+       render : function(){
+               var columnRows = [];
+               var sourceHead = null;
+               var sourceList = null;
+               var columnsGridHeader = 
<thead><tr><th>Column</th><th>Datatype</th><th>Annotations</th></tr></thead>;
+               var currentlyLoadingImg = null;
+               if(this.state){
+                       var sourceListContent = null;
+                       if(this.state.sources){
+                               var sourceSpec =  {
+
+                                               attributes: [
+                                                      {key: "isSelected", 
label: "",
+                                                               func: 
function(val, asset){
+                                                                       return 
<SelectCheckbox onChange={function(selected){
+                                                                               
asset.isSelected = selected;
+                                                                       
}.bind(this)} asset={asset} />
+
+                                                               }},
+                                                               {key: "icon", 
label: "", func:
+                                                          function(val, asset){
+                                                                  if(asset && 
asset.type && UISpec[asset.type] && UISpec[asset.type].icon){
+                                                                          
return UISpec[asset.type].icon;
+                                                                  }
+                                                                  return 
UISpec["DefaultDocument"].icon;
+                                                               }
+                                                      },
+                                                          {key: "name", label: 
"Name"},
+                                          {key: "type", label: "Type"},
+                                          {key: "annotations", label: 
"Annotations",
+                                                         func: function(val){
+                                                                 if(!val){
+                                                                         
return 0;
+                                                                         }
+                                                                 return 
val.length;
+                                                               }
+                                          }
+                                   ]};
+
+                               sourceListContent = <ODFBrowser.ODFPagingTable 
rowAssets={this.state.filteredSources} onRowClick={this.referenceClick} 
spec={sourceSpec}/>;
+                       }else{
+                               sourceListContent = <Image 
src="img/lg_proc.gif" rounded />;
+                       }
+
+                       var sourceImportBtn = <Button style={{float:"right"}} 
onClick={function(){this.setState({showImportDialog: 
true});}.bind(this)}>Register new data set</Button>;
+                       var sourceImportingImg = null;
+                       if(this.state.importingTable){
+                               sourceImportingImg = <Image 
src="img/lg_proc.gif" rounded />;
+                       }
+
+                       var importFeedback = <h3><Label style={{whiteSpace: 
"normal"}} 
bsStyle={this.state.importFeedback.style}>{this.state.importFeedback.msg}</Label></h3>
+
+                       var storedDefaults = null;
+                       try {
+                          storedDefaults = 
JSON.parse(localStorage.getItem("odf-client-defaults"));
+                       } catch(e) {
+                               console.log("Couldnt parse defaults from 
localStorage: " + e);
+                               storedDefaults = {};
+                       }
+                       if (!storedDefaults) {
+                               storedDefaults = {};
+                       }
+                       console.log("Stored defaults: " + storedDefaults);
+
+                       var sourceImportDialog =  <Modal 
show={this.state.showImportDialog} onHide={this.closeImportingDialog}>
+                                                                         
<Modal.Header closeButton>
+                                                                            
<Modal.Title>Register new JDBC data set</Modal.Title>
+                                                                         
</Modal.Header>
+                                                                         
<Modal.Body>
+                                                                               
{importFeedback}
+                                                                           
<form>
+                                                                               
 <Input type="text" ref="jdbcInput" defaultValue={storedDefaults.jdbcInput} 
label="JDBC string" />
+                                                                            
<Input type="text" ref="userInput" defaultValue={storedDefaults.userInput} 
label="Username" />
+                                                                            
<Input type="password" ref="passInput" defaultValue={storedDefaults.passInput} 
label="Password" />
+                                                                            
<Input type="text" ref="dbInput" defaultValue={storedDefaults.dbInput} 
label="Database" />
+                                                                            
<Input type="text" ref="schemaInput" defaultValue={storedDefaults.schemaInput} 
label="Schema" />
+                                                                            
<Input type="text" ref="sourceInput" defaultValue={storedDefaults.sourceInput} 
label="Table" />
+                                                                            
</form>
+                                                                            
{sourceImportingImg}
+                                                                        
</Modal.Body>
+                                                                        
<Modal.Footer>
+                                                                        
<Button onClick={this.storeImportDialogDefaults}>Store values as 
defaults</Button>
+                                                                        
<Button bsStyle="primary" onClick={this.doImport}>Register</Button>
+                                                                        
<Button onClick={this.closeImportingDialog}>Close</Button>
+                                                                        
</Modal.Footer>
+                                                                       
</Modal>;
+                       sourceList = <Panel style={{float:"left", marginRight: 
30, maxWidth:600, minHeight: 550}}>
+                                                               
{sourceImportDialog}
+                                                               <h3 
style={{float: "left", marginTop: "5px"}}>
+                                                                       Data 
sets
+                                                               </h3>
+                                                               
{sourceImportBtn}<br style={{clear: "both"}}/>
+                                                               <Input 
onChange={this.filterSources} addonBefore={<Glyphicon glyph="search" />} 
label=" " type="text" placeholder="Filter ..." />
+                                                               <br/>
+                                                               
{sourceListContent}
+                                                       </Panel>;
+                       if(this.state.currentlyLoading){
+                               currentlyLoadingImg = <Image 
src="img/lg_proc.gif" rounded />;
+                       }
+                       var panel = <div style={{float: 
"left"}}>{currentlyLoadingImg}</div>;
+
+                       if(this.state.selectedTable){
+                               var source = this.state.selectedTable;
+                               var sourceAnnotations = [];
+                               if(source.loadedAnnotations){
+                                       //reverse so newest is at front
+                                       var sourceAnns = 
source.loadedAnnotations.slice();
+                                       sourceAnns.reverse();
+                                       var processedTypes = [];
+                                       $.each(sourceAnns, function(key, val){
+                                               
if(processedTypes.indexOf(val.annotationType) == -1){
+                                                       
processedTypes.push(val.annotationType);
+                                                       var summary = 
(val.summary ? ", " + val.summary : "");
+                                                       
sourceAnnotations.push(<ODFAnnotationMarker key={key} annotation={val}/>);
+                                               }
+                                       });
+                               }
+
+                               var hasColumns = (source.columns && 
source.columns.length > 0 ? true : false);
+                               var columnsString = (hasColumns ? "Columns: " + 
source.columns.length : null);
+                               var annotationsFilter = (hasColumns ? 
<FilterMenu onFilter={this.doFilter} dataClasses={this.state.dataClasses} 
style={{float: "right"}} /> : null);
+
+                               sourceHead = <div>
+                                                               
<h3>{source.name} </h3>
+                                                                       <div 
style={{}}>
+                                                                               
<NewAnalysisRequestButton dataSetId={this.state.selectedTable.reference.id} />
+                                                                       </div>
+                                                               <br/>
+                                                               Description: 
{source.description}
+                                                               <br/>
+                                                               {columnsString}
+                                                               
<br/>Annotations:{sourceAnnotations}
+                                                               <br/>
+                                                               
{annotationsFilter}
+                                                               </div>;
+
+                               panel = <Panel style={{float: "left", width: 
"50%"}} header={sourceHead}>
+                                                       {currentlyLoadingImg}
+                                               </Panel>;
+                       }
+                       var columnsTable = null;
+                       var filteredColumns = (this.state.filteredColumns ? 
this.state.filteredColumns : []).slice();
+
+                       if(filteredColumns.length > 0){
+                               var colSpec = {attributes: [{key: "isSelected", 
label: "Select",
+                                       func: function(val, col){
+                                               return <SelectCheckbox 
onChange={function(selected){
+                                                       col.isSelected = 
selected;
+                                               }.bind(this)} asset={col} />
+
+                                       }},
+                      {key: "name", label: "Name", sort: true},
+                          {key: "dataType", label: "Datatype"},
+                          {key: "loadedAnnotations", label: "Annotations",
+                                         func: function(annotations, obj){
+                                                 return <AnnotationsColumn 
annotations={annotations} />;
+                                         }
+                                 }]};
+                               columnsTable = <div><ODFBrowser.ODFPagingTable 
ref="columnsTable" rowAssets={filteredColumns} assetType={"columns"} 
spec={colSpec}/><br/><ODFAnnotationLegend /></div>;
+                               panel = (<Panel style={{float:"left", width: 
"50%"}} header={sourceHead}>
+                                                       {columnsTable}
+                                               </Panel>);
+                       }
+               }
+
+               var contentComponent = <Jumbotron>
+             <div>
+                <h2>Welcome to your Data Lake</h2>
+                       <Button bsStyle="success" onClick={this.shopData}>
+                               Shop selected data  <Glyphicon 
glyph="shopping-cart" />
+                       </Button>
+                       <br/>
+                       <br/>
+                        {sourceList}
+                        {panel}
+                       <div style={{clear: "both"}} />
+         </div>
+       </Jumbotron>;
+
+               return <div>{contentComponent}</div>;
+       }
+});
+
+var ODFTermPage = React.createClass({
+
+  getInitialState() {
+    return {terms: []};
+  },
+
+  loadTerms : function() {
+    // clear alert
+    this.props.alertCallback({type: "", message: ""});
+    var req = AtlasHelper.searchAtlasMetadata("from BusinessTerm",
+
+        function(data){
+                       if(!this.isMounted()){
+                               return;
+                       }
+                       this.setState({terms: data});
+        }.bind(this),
+
+        function() {
+        }.bind(this)
+    );
+  },
+
+  componentDidMount() {
+    this.loadTerms();
+  },
+
+  render: function() {
+     var terms = $.map(
+        this.state.terms,
+        function(term) {
+          return <tr style={{cursor: 'pointer'}} key={term.name} 
title={term.example} onClick={function(){
+                 var win = window.open(term.originRef, '_blank');
+                 win.focus();}
+          }>
+                  <td>
+                     {term.name}
+                  </td>
+                  <td>
+                       {term.description}
+                  </td>
+                 </tr>
+        }.bind(this)
+       );
+
+     return (
+       <div className="jumbotron">
+       <h2>Glossary</h2>
+       <br/>
+       <br/>
+       <Panel>
+                 <h3>Terms</h3>
+          <Table>
+                <thead>
+                       <tr>
+                       <th>Name</th>
+                       <th>Description</th>
+                       </tr>
+                </thead>
+             <tbody>
+                {terms}
+             </tbody>
+          </Table>
+          </Panel>
+       </div>
+     )
+   }
+});
+
+var ODFClient = React.createClass({
+
+   componentDidMount: function() {
+     $(window).bind("hashchange", this.parseUrl);
+     this.parseUrl();
+   },
+
+   parseUrl : function(){
+    var target = constants_ODFNavBar.odfDataLakePage;
+    var navAddition = null;
+    var hash = document.location.hash;
+    if(hash && hash.length > 1){
+      hash = hash.split("#")[1];
+      var split = hash.split("/");
+      var navHash = split[0];
+      if(split.length > 0){
+        navAddition = split.slice(1);
+      }
+      if(constants_ODFNavBar[navHash]){
+        target = constants_ODFNavBar[navHash];
+      }
+    }
+    this.setState({
+      activeNavBarItem: target,
+        navAddition: navAddition}
+    );
+  },
+
+  getInitialState: function() {
+    return ({
+        activeNavBarItem: constants_ODFNavBar.odfDataLakePage,
+        navAddition: null,
+        globalAlert: {
+          type: "",
+          message: ""
+        }
+    });
+  },
+
+  handleNavBarSelection: function(selection) {
+    $.each(constants_ODFNavBar, function(key, ref){
+      if(ref == selection){
+        document.location.hash = key;
+      }
+    });
+    this.setState({ activeNavBarItem: selection });
+  },
+
+  handleAlert: function(alertInfo) {
+    this.setState({ globalAlert: alertInfo });
+  },
+
+  render: function() {
+    var alertComp = null;
+    if (this.state.globalAlert.type != "") {
+       alertComp = <Alert 
bsStyle={this.state.globalAlert.type}>{this.state.globalAlert.message}</Alert>;
+    }
+
+    var contentComponent = <ODFDataLakePage alertCallback={this.handleAlert}/>;
+    if (this.state.activeNavBarItem == constants_ODFNavBar.odfDataLakePage) {
+       contentComponent = <ODFDataLakePage alertCallback={this.handleAlert}/>;
+    } else if (this.state.activeNavBarItem == constants_ODFNavBar.odfTermPage) 
{
+       contentComponent = <ODFTermPage alertCallback={this.handleAlert}/>;
+    }
+
+    var divStyle = {
+//      marginLeft: "80px",
+//      marginRight: "80px"
+    };
+
+    return (
+        <div>
+           <ODFNavBar activeKey={this.state.activeNavBarItem} 
selectCallback={this.handleNavBarSelection}></ODFNavBar>
+           <div style={divStyle}>
+              {alertComp}
+              {contentComponent}
+           </div>
+        </div>
+    );
+  }
+});
+
+var div = $("#odf-toplevel-div")[0];
+ReactDOM.render(<ODFClient/>, div);

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/webapp/scripts/odf-configuration-store.js
----------------------------------------------------------------------
diff --git a/odf/odf-web/src/main/webapp/scripts/odf-configuration-store.js 
b/odf/odf-web/src/main/webapp/scripts/odf-configuration-store.js
new file mode 100755
index 0000000..cf50075
--- /dev/null
+++ b/odf/odf-web/src/main/webapp/scripts/odf-configuration-store.js
@@ -0,0 +1,63 @@
+/**
+ *
+ * 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.
+ */
+var $ = require("jquery");
+var ODFGlobals = require("./odf-globals.js");
+
+var ConfigurationStore = {
+
+  // readUserDefinedProperties(successCallback, alertCallback) {
+   readConfig(successCallback, alertCallback) {
+          if (alertCallback) {
+            alertCallback({type: ""});
+          }
+     // clear alert
+
+     return $.ajax({
+       url: ODFGlobals.apiPrefix + "settings",
+       dataType: 'json',
+       type: 'GET',
+       success: successCallback,
+       error: function(xhr, status, err) {
+         if (alertCallback) {
+            var msg = "Error while reading user defined properties: " + 
err.toString();
+            alertCallback({type: "danger", message: msg});
+         }
+       }
+      }).abort;
+   },
+
+   updateConfig(config, successCallback, alertCallback) {
+               if (alertCallback) {
+                        alertCallback({type: ""});
+               }
+
+           return $.ajax({
+                      url: ODFGlobals.apiPrefix + "settings",
+                      contentType: "application/json",
+                      dataType: 'json',
+                      type: 'PUT',
+                      data: JSON.stringify(config),
+                      success: successCallback,
+                      error: function(xhr, status, err) {
+                        if (alertCallback) {
+                           var msg = "Error while reading user defined 
properties: " + err.toString();
+                           alertCallback({type: "danger", message: msg});
+                        }
+                      }
+            }).abort;
+   }
+}
+
+module.exports = ConfigurationStore;

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/webapp/scripts/odf-console.js
----------------------------------------------------------------------
diff --git a/odf/odf-web/src/main/webapp/scripts/odf-console.js 
b/odf/odf-web/src/main/webapp/scripts/odf-console.js
new file mode 100755
index 0000000..aa70808
--- /dev/null
+++ b/odf/odf-web/src/main/webapp/scripts/odf-console.js
@@ -0,0 +1,967 @@
+/**
+ *
+ * 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.
+ */
+//css imports
+require("bootstrap/dist/css/bootstrap.min.css");
+require("bootstrap-material-design/dist/css/bootstrap-material-design.min.css");
+require("bootstrap-material-design/dist/css/ripples.min.css");
+require("roboto-font/css/fonts.css");
+
+
+//js imports
+var $ = require("jquery");
+var bootstrap = require("bootstrap");
+
+var React = require("react");
+var ReactDOM = require("react-dom");
+var LinkedStateMixin = require("react-addons-linked-state-mixin");
+var ReactBootstrap = require("react-bootstrap");
+
+var ODFGlobals = require("./odf-globals.js");
+var ODFStats = require("./odf-statistics.js");
+var ODFSettings = require("./odf-settings.js");
+var ODFServices = require("./odf-services.js");
+var ODFBrowser = require("./odf-metadata-browser.js").ODFMetadataBrowser;
+var ODFRequestBrowser = require("./odf-request-browser.js");
+var AJAXCleanupMixin = require("./odf-mixins.js");
+var configurationStore = require("./odf-utils.js").ConfigurationStore;
+var servicesStore = require("./odf-utils.js").ServicesStore;
+var AtlasHelper = require("./odf-utils.js").AtlasHelper;
+var AnnotationStoreHelper = require("./odf-utils.js").AnnotationStoreHelper;
+var OdfAnalysisRequest = require("./odf-analysis-request.js");
+var LogViewer = require("./odf-logs.js");
+//var Notifications = require("./odf-notifications.js");
+var NewAnalysisRequestButton = OdfAnalysisRequest.NewAnalysisRequestButton;
+var NewAnalysisRequestDialog = OdfAnalysisRequest.NewAnalysisRequestDialog;
+var NewCreateAnnotationsButton = OdfAnalysisRequest.NewCreateAnnotationsButton;
+var NewCreateAnnotationsDialog = OdfAnalysisRequest.NewCreateAnnotationsDialog;
+
+var Button = ReactBootstrap.Button;
+var Nav = ReactBootstrap.Nav;
+var NavItem = ReactBootstrap.NavItem;
+var Navbar = ReactBootstrap.Navbar;
+var NavDropdown = ReactBootstrap.NavDropdown;
+var MenuItem = ReactBootstrap.MenuItem;
+var Jumbotron = ReactBootstrap.Jumbotron;
+var Grid = ReactBootstrap.Grid;
+var Row = ReactBootstrap.Row;
+var Col = ReactBootstrap.Col;
+var Table = ReactBootstrap.Table;
+var Modal = ReactBootstrap.Modal;
+var Input = ReactBootstrap.Input;
+var Alert = ReactBootstrap.Alert;
+var Panel = ReactBootstrap.Panel;
+var Label = ReactBootstrap.Label;
+var Input = ReactBootstrap.Input;
+var ProgressBar = ReactBootstrap.ProgressBar;
+var Image = ReactBootstrap.Image;
+var ListGroup = ReactBootstrap.ListGroup;
+var ListGroupItem = ReactBootstrap.ListGroupItem;
+var Tabs = ReactBootstrap.Tabs;
+var Tab = ReactBootstrap.Tab;
+var Glyphicon = ReactBootstrap.Glyphicon;
+
+var PerServiceStatusGraph = ODFStats.PerServiceStatusGraph;
+var TotalAnalysisGraph = ODFStats.TotalAnalysisGraph;
+var SystemDiagnostics = ODFStats.SystemDiagnostics;
+
+////////////////////////////////////////////////////////////////
+// toplevel navigation bar
+
+const constants_ODFNavBar = {
+  gettingStarted: "navKeyGettingStarted",
+  configuration: "navKeyConfiguration",
+  monitor: "navKeyMonitor",
+  discoveryServices: "navKeyDiscoveryServices",
+  data: "navKeyData",
+  analysis: "navKeyAnalysis"
+}
+
+var ODFNavBar = React.createClass({
+   render: function() {
+       return (
+         <Navbar inverse>
+           <Navbar.Header>
+             <Navbar.Brand>
+               <b>Open Discovery Framework</b>
+             </Navbar.Brand>
+             <Navbar.Toggle />
+           </Navbar.Header>
+           <Navbar.Collapse>
+             <Nav pullRight activeKey={this.props.activeKey} 
onSelect={this.props.selectCallback}>
+               <NavItem eventKey={constants_ODFNavBar.gettingStarted} 
href="#">Getting Started</NavItem>
+               <NavItem eventKey={constants_ODFNavBar.monitor} href="#">System 
Monitor</NavItem>
+               <NavItem eventKey={constants_ODFNavBar.configuration} 
href="#">Settings</NavItem>
+               <NavItem eventKey={constants_ODFNavBar.discoveryServices} 
href="#">Services</NavItem>
+               <NavItem eventKey={constants_ODFNavBar.data} href="#">Data 
sets</NavItem>
+               <NavItem eventKey={constants_ODFNavBar.analysis} 
href="#">Analysis</NavItem>
+             </Nav>
+           </Navbar.Collapse>
+         </Navbar>
+       );
+   }
+});
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Configuration page
+
+var ConfigurationPage = React.createClass({
+  componentWillMount() {
+      this.props.alertCallback({type: ""});
+  },
+
+  render: function() {
+    return (
+    <div className="jumbotron">
+      <Tabs position="left" defaultActiveyKey={1}>
+        <Tab eventKey={1} title="General">
+          <ODFSettings.ODFConfigPage alertCallback={this.props.alertCallback}/>
+        </Tab>
+        <Tab eventKey={2} title="Spark settings">
+          <ODFSettings.SparkConfigPage 
alertCallback={this.props.alertCallback}/>
+        </Tab>
+        <Tab eventKey={3} title="User-defined">
+          <ODFSettings.UserDefinedConfigPage 
alertCallback={this.props.alertCallback}/>
+        </Tab>
+      </Tabs>
+      </div>
+      );
+  }
+
+});
+
+const GettingStartedPage = React.createClass({
+  getInitialState() {
+     return ({version: "NOTFOUND"});
+  },
+
+  componentWillMount() {
+     this.props.alertCallback({type: ""});
+     $.ajax({
+         url: ODFGlobals.engineUrl + "/version",
+         type: 'GET',
+         success: function(data) {
+             this.setState(data);
+         }.bind(this)
+       });
+  },
+
+  render: function() {
+    var divStyle = {
+      marginLeft: "80px",
+      marginRight: "80px"
+    };
+    return (
+      <Jumbotron>
+      <div style={divStyle}>
+         <h2>Welcome to the Open Discovery Framework Console</h2>
+         <p/>The "Open Discovery Framework" (ODF) is an open metadata-based 
platform
+         that strives to be a common home for different analytics technologies
+         that discover characteristics of data sets and relationships between
+         them (think "AppStore for discovery algorithms").
+         Using ODF, applications can leverage new discovery algorithms and 
their
+         results with minimal integration effort.
+         <p/>
+         This console lets you administer and configure your ODF system, as 
well as
+         run analyses and browse their results.
+         <p/>
+         <p><Button target="_blank" href="doc" bsStyle="primary">Open 
Documentation</Button></p>
+         <p><Button target="_blank" href="swagger" bsStyle="success">Show API 
Reference</Button></p>
+         <p/>
+                Version: {this.state.version}
+         </div>
+       </Jumbotron>
+
+      )
+  }
+
+});
+
+/////////////////////////////////////////////////////////////////////
+// monitor page
+var StatusGraphs = React.createClass({
+
+       selectTab : function(key){
+               this.setState({key});
+       },
+
+       getInitialState() {
+           return {
+             key: "system_state"
+           };
+        },
+
+       render : function() {
+               var divStyle = {
+                    marginLeft: "20px"
+           };
+
+               return (
+                       <div>
+                               <Tabs position="left" 
activeKey={this.state.key} onSelect={this.selectTab}>
+                                       <Tab eventKey={"system_state"} 
title="System state">
+                                               <div style={divStyle}>
+                                                       <TotalAnalysisGraph 
visible={this.state.key == "system_state"} 
alertCallback={this.props.alertCallback}/>
+                                                       <PerServiceStatusGraph 
visible={this.state.key == "system_state"} 
alertCallback={this.props.alertCallback}/>
+                                               </div>
+                                       </Tab>
+                                   <Tab eventKey={"diagnostics"} 
title="Diagnostics">
+                                               <div style={divStyle}>
+                                                       <SystemDiagnostics 
visible={this.state.key == "diagnostics"} 
alertCallback={this.props.alertCallback}/>
+                                               </div>
+                                       </Tab>
+                                       <Tab eventKey={"logs"} title="System 
logs">
+                                               <div style={divStyle}>
+                                                       <LogViewer 
visible={this.state.key == "logs"} alertCallback={this.props.alertCallback}/>
+                                               </div>
+                                       </Tab>
+                               </Tabs>
+                       </div>
+         );
+       }
+
+
+});
+
+var MonitorPage = React.createClass({
+       mixins : [AJAXCleanupMixin],
+
+       getInitialState() {
+               return ( {
+                               monitorStatusVisible: false,
+                               monitorStatusStyle:"success",
+                               monitorStatusMessage: "OK",
+                               monitorWorkInProgress: false
+               });
+       },
+
+       componentWillMount() {
+          this.props.alertCallback({type: ""});
+       },
+
+       checkHealth() {
+               this.setState({monitorWorkInProgress: true, 
monitorStatusVisible: false});
+           var url = ODFGlobals.engineUrl + "/health";
+               var req = $.ajax({
+                url: url,
+                dataType: 'json',
+                type: 'GET',
+                success: function(data) {
+                        var status = data.status;
+                        var newState = {
+                               monitorStatusVisible: true,
+                               monitorWorkInProgress: false
+                        };
+
+                        if (status == "OK") {
+                                newState.monitorStatusStyle = "success";
+                        } else if (status == "WARNING") {
+                                newState.monitorStatusStyle = "warning";
+                        } else if (status == "ERROR") {
+                                newState.monitorStatusStyle = "danger";
+                        }
+                        // TODO show more than just the first message
+                        newState.monitorStatusMessage = "Status: " + status + 
". " + data.messages[0];
+
+                        this.setState(newState);
+                }.bind(this),
+                error: function(xhr, status, err) {
+                  if(this.isMounted()){
+                          this.setState({
+                          monitorStatusVisible: true,
+                          monitorStatusStyle:"danger",
+                          monitorStatusMessage: "An error occured: " + 
err.toString(),
+                          monitorWorkInProgress: false});
+                  };
+                }.bind(this)
+               });
+               this.storeAbort(req.abort);
+       },
+
+       performRestart : function(){
+               $.ajax({
+                     url: ODFGlobals.engineUrl + "/shutdown",
+                     contentType: "application/json",
+                     type: 'POST',
+                     data: JSON.stringify({restart: "true"}),
+                     success: function(data) {
+                                       this.setState({monitorStatusVisible : 
true, monitorStatusStyle: "info", monitorStatusMessage: "Restart in 
progress..."});
+                     }.bind(this),
+                     error: function(xhr, status, err) {
+                                       this.setState({monitorStatusVisible : 
true, monitorStatusStyle: "warning", monitorStatusMessage: "Restart request 
failed"});
+                     }.bind(this)
+                   });
+       },
+
+       render() {
+         var divStyle = {
+                     marginLeft: "20px"
+                   };
+         var monitorStatus = null;
+         if (this.state.monitorStatusVisible) {
+                 monitorStatus = <Alert 
bsStyle={this.state.monitorStatusStyle}>{this.state.monitorStatusMessage}</Alert>;
+         }
+         var progressIndicator = null;
+         if (this.state.monitorWorkInProgress) {
+                 progressIndicator = <Image src="img/lg_proc.gif" rounded />;
+         }
+         return (
+               <div className="jumbotron">
+               <h3>System health</h3>
+                 <div style={divStyle}>
+                       <Button className="btn-raised" bsStyle="primary" 
disabled={this.state.monitorWorkInProgress} onClick={this.checkHealth}>Check 
health</Button>
+                       <Button className="btn-raised" bsStyle="warning" 
onClick={this.performRestart}>Restart ODF</Button>
+                       {progressIndicator}
+                       {monitorStatus}
+                       <hr/>
+                       <div>
+                       </div>
+                       <StatusGraphs alertCallback={this.props.alertCallback}/>
+                 </div>
+               </div>
+         );
+       }
+
+});
+
+//////////////////////////////////////////////////////
+// discovery services page
+var DiscoveryServicesPage = React.createClass({
+  mixins : [AJAXCleanupMixin],
+
+  getInitialState() {
+         return ({discoveryServices: []});
+  },
+
+  loadDiscoveryServices() {
+         // clear alert
+    this.props.alertCallback({type: "", message: ""});
+
+       var req = $.ajax({
+           url: ODFGlobals.servicesUrl,
+           dataType: 'json',
+           type: 'GET',
+           success: function(data) {
+              this.setState({discoveryServices: data});
+           }.bind(this),
+           error: function(xhr, status, err) {
+          if(this.isMounted()){
+                  var msg = "Error while reading ODF services: " + 
err.toString();
+                  this.props.alertCallback({type: "danger", message: msg});
+          }
+           }.bind(this)
+         });
+
+       this.storeAbort(req.abort);
+  },
+
+  componentDidMount() {
+         this.loadDiscoveryServices();
+  },
+
+  render: function() {
+       var services = $.map(
+        this.state.discoveryServices,
+        function(dsreg) {
+          return <tr key={dsreg.id}>
+                  <td>
+                     <ODFServices.DiscoveryServiceInfo dsreg={dsreg} 
refreshCallback={this.loadDiscoveryServices} 
alertCallback={this.props.alertCallback}/>
+                  </td>
+                 </tr>
+        }.bind(this)
+    );
+
+       return (
+            <div className="jumbotron">
+           <h3>Services</h3>
+           This page lets you manage the services for this ODF instance.
+           You can add services manually by clicking the <em>Add Service</em> 
button or
+           register remote services (e.g. deployed on Bluemix) you have built 
with the ODF service developer kit by
+           clicking the <em>Register remote services</em> link.
+           <p/>
+                                        <ODFServices.AddDiscoveryServiceButton 
refreshCallback={this.loadDiscoveryServices}/>
+           <p/>
+               <Table bordered responsive>
+                <tbody>
+                {services}
+             </tbody>
+          </Table>
+            </div>
+       );
+  }
+
+});
+
+//////////////////////////////////////////////////////////////
+// Analysis Page
+var AnalysisRequestsPage = React.createClass({
+  mixins : [AJAXCleanupMixin],
+
+  getInitialState() {
+      return {recentAnalysisRequests: null, config: {}, services : []};
+  },
+
+  componentWillReceiveProps : function(nextProps){
+       var selection = null;
+       if(nextProps.navAddition && nextProps.navAddition.length > 0 && 
nextProps.navAddition[0] && nextProps.navAddition[0].length > 0){
+               var jsonAddition = {};
+
+               try{
+                       jsonAddition = 
JSON.parse(decodeURIComponent(nextProps.navAddition[0]));
+               }catch(e){
+
+               }
+
+               if(jsonAddition.requestId){
+                       $.each(this.state.recentAnalysisRequests, function(key, 
tracker){
+                               var reqId = jsonAddition.requestId;
+
+                               if(tracker.request.id == reqId){
+                                       selection = reqId;
+                               }
+                       }.bind(this));
+               }else if(jsonAddition.id && jsonAddition.repositoryId){
+                       selection = jsonAddition;
+               }
+       }
+
+       if(selection != this.state.selection){
+               this.setState({selection : selection});
+       }
+  },
+
+  componentDidMount() {
+         if(!this.refreshInterval){
+                 this.refreshInterval = 
window.setInterval(this.refreshAnalysisRequests, 5000);
+         }
+      this.initialLoadServices();
+      this.initialLoadRecentAnalysisRequests();
+  },
+
+  componentWillUnmount : function() {
+         if(this.refreshInterval){
+                 window.clearInterval(this.refreshInterval);
+         }
+  },
+
+  getDiscoveryServiceNameFromId(id) {
+      var servicesWithSameId = this.state.services.filter(
+         function(dsreg) {
+             return dsreg.id == id;
+         }
+      );
+      if (servicesWithSameId.length > 0) {
+        return servicesWithSameId[0].name;
+      }
+      return null;
+  },
+
+  refreshAnalysisRequests : function(){
+         var req = configurationStore.readConfig(
+                     function(config) {
+                         this.setState({config: config});
+                         const url = ODFGlobals.analysisUrl + 
"?offset=0&limit=20";
+                         $.ajax({
+                           url: url,
+                           dataType: 'json',
+                           type: 'GET',
+                           success: function(data) {
+                               $.each(data.analysisRequestTrackers, 
function(key, tracker){
+                                       //collect service names by id and add 
to json so that it can be displayed later
+                                       
$.each(tracker.discoveryServiceRequests, function(key, request){
+                                               var serviceName = 
this.getDiscoveryServiceNameFromId(request.discoveryServiceId);
+                                               request.discoveryServiceName = 
serviceName;
+                                       }.bind(this));
+                               }.bind(this));
+                               this.setState({recentAnalysisRequests: 
data.analysisRequestTrackers});
+                           }.bind(this),
+                           error: function(xhr, status, err) {
+                               if(status != "abort" ){
+                                       console.error(url, status, 
err.toString());
+                               }
+                               if(this.isMounted()){
+                                 var msg = "Error while refreshing recent 
analysis requests: " + err.toString();
+                                 this.props.alertCallback({type: "danger", 
message: msg});
+                               }
+                           }.bind(this)
+                         });
+                     }.bind(this),
+             this.props.alertCallback
+           );
+
+           this.storeAbort(req.abort);
+  },
+
+  initialLoadServices() {
+       this.setState({services: null});
+
+    var req = servicesStore.getServices(
+      function(services) {
+          this.setState({services: services});
+      }.bind(this),
+      this.props.alertCallback
+    );
+
+    this.storeAbort(req.abort);
+  },
+
+  initialLoadRecentAnalysisRequests() {
+       this.setState({recentAnalysisRequests: null});
+
+    var req = configurationStore.readConfig(
+      function(config) {
+          this.setState({config: config});
+          const url = ODFGlobals.analysisUrl + "?offset=0&limit=20";
+          $.ajax({
+            url: url,
+            dataType: 'json',
+            type: 'GET',
+            success: function(data) {
+               var selection = null;
+               $.each(data.analysisRequestTrackers, function(key, tracker){
+                       if(this.props.navAddition && 
this.props.navAddition.length > 0 && this.props.navAddition[0].length > 0){
+                               var reqId = "";
+                               try{
+                                       reqId = 
JSON.parse(decodeURIComponent(this.props.navAddition[0])).requestId
+                               }catch(e){
+
+                               }
+                               if(tracker.request.id == reqId){
+                                       selection = reqId;
+                               }
+                               }
+
+                       //collect service names by id and add to json so that 
it can be displayed later
+                       $.each(tracker.discoveryServiceRequests, function(key, 
request){
+                               var serviceName = 
this.getDiscoveryServiceNameFromId(request.discoveryServiceId);
+                               request.discoveryServiceName = serviceName;
+                       }.bind(this));
+               }.bind(this));
+
+               var newState = {recentAnalysisRequests: 
data.analysisRequestTrackers};
+               if(selection){
+                       newState.selection = selection;
+               }
+
+               this.setState(newState);
+            }.bind(this),
+            error: function(xhr, status, err) {
+               if(status != "abort" ){
+                       console.error(url, status, err.toString());
+               }
+               if(this.isMounted()){
+                 var msg = "Error while loading recent analysis requests: " + 
err.toString();
+                 this.props.alertCallback({type: "danger", message: msg});
+               }
+            }.bind(this)
+          });
+      }.bind(this),
+      this.props.alertCallback
+    );
+
+    this.storeAbort(req.abort);
+  },
+
+  cancelAnalysisRequest(tracker) {
+      var url = ODFGlobals.analysisUrl + "/" + tracker.request.id + "/cancel";
+
+      $.ajax({
+          url: url,
+          type: 'POST',
+          success: function() {
+                         if(this.isMounted()){
+                                 this.refreshAnalysisRequests();
+                         }
+          }.bind(this),
+          error: function(xhr, status, err) {
+                 if(status != "abort" ){
+                       console.error(url, status, err.toString());
+                 }
+
+                 var errMsg = null;
+                 if(err == "Forbidden"){
+                         errMsg = "only analyses that have not been started 
yet can be cancelled!";
+                 }else if(err == "Bad Request"){
+                         errMsg = "the requested analysis could not be found!";
+                 }
+                 if(this.isMounted()){
+                                 var msg = "Analysis could not be cancelled: " 
+ (errMsg ? errMsg : err.toString());
+                                 if(this.props.alertCallback){
+                                         this.props.alertCallback({type: 
"danger", message: msg});
+                                 }
+                 }
+          }.bind(this)
+      });
+  },
+
+  viewResultAnnotations : function(target){
+         this.setState({
+                       resultAnnotations : null,
+                       showAnnotations: true
+               });
+         var req = 
AnnotationStoreHelper.loadAnnotationsForRequest(target.request.id,
+                       function(data){
+                               this.setState({
+                                       resultAnnotations : data.annotations
+                               });
+                       }.bind(this),
+                       function(error){
+                               console.error('Annotations could not be loaded 
' + error);
+                       }
+               );
+         this.storeAbort(req.abort);
+  },
+
+  viewInAtlas : function(target){
+         var repo =  target.request.dataSets[0].repositoryId;
+         repo = repo.split("atlas:")[1];
+      var annotationQueryUrl = repo + 
"/#!/search?query=from%20ODFAnnotation%20where%20analysisRun%3D'"+ 
target.request.id + "'";
+         var win = window.open(annotationQueryUrl, '_blank');
+  },
+
+  render : function() {
+    var loadingImg = null;
+    if(this.state.recentAnalysisRequests == null){
+       loadingImg = <Image src="img/lg_proc.gif" rounded />;
+    }
+    var requestActions = [
+                           {
+                                  assetType: ["requests"],
+                                  actions : [
+                                     {
+                                         label: "Cancel analysis",
+                                         func: this.cancelAnalysisRequest,
+                                         filter: function(obj){
+                                                 var val = obj.status;
+                                                 if (val == "INITIALIZED" || 
val == "IN_DISCOVERY_SERVICE_QUEUE") {
+                                                         return true;
+                                                 }
+                                                 return false;
+                                         }
+                                     },
+                                     {
+                                         label: "View results",
+                                         func: this.viewResultAnnotations
+                                     },
+                                     {
+                                         label: "View results in atlas",
+                                         func: this.viewInAtlas
+                                     }
+                                  ]
+                               }
+                           ];
+    return (
+               <div className="jumbotron">
+                          <h3>Analysis requests</h3>
+                          <div>
+                       Click Refresh to refresh the list of existing analysis 
requests.
+                       Only the last 20 valid requests are shown.
+                        <p/>
+                       <NewAnalysisRequestButton bsStyle="primary" 
onClose={this.refreshAnalysisRequests} 
alertCallback={this.props.alertCallback}/>
+                       <NewCreateAnnotationsButton bsStyle="primary" 
onClose={this.refreshAnalysisRequests} 
alertCallback={this.props.alertCallback}/>
+
+                       <Button bsStyle="success" 
onClick={this.refreshAnalysisRequests}>Refresh</Button> &nbsp;
+               {loadingImg}
+                       <ODFRequestBrowser 
registeredServices={this.state.config.registeredServices} 
actions={requestActions} ref="requestBrowser" selection={this.state.selection} 
assets={this.state.recentAnalysisRequests}/>
+                      </div>
+               <Modal show={this.state.showAnnotations} 
onHide={function(){this.setState({showAnnotations : false})}.bind(this)}>
+                       <Modal.Header closeButton>
+                               <Modal.Title>Analysis results for analysis 
{this.state.resultTarget}</Modal.Title>
+                            </Modal.Header>
+                            <Modal.Body>
+                               <ODFBrowser ref="resultBrowser" 
type={"annotations"} assets={this.state.resultAnnotations} />
+                            </Modal.Body>
+                            <Modal.Footer>
+                           <Button 
onClick={function(){this.setState({showAnnotations : 
false})}.bind(this)}>Close</Button>
+                           </Modal.Footer>
+               </Modal>
+                   </div>
+    );
+  }
+
+});
+
+var AnalysisDataSetsPage = React.createClass({
+  mixins : [AJAXCleanupMixin],
+
+  componentDidMount() {
+      this.loadDataFiles();
+      this.loadTables();
+      this.loadDocuments();
+  },
+
+  getInitialState() {
+      return ({        showDataFiles: true,
+                       showHideDataFilesIcon: "chevron-up",
+                       showTables: true,
+                       showHideTablesIcon: "chevron-up",
+                       showDocuments: true,
+                       showHideDocumentsIcon: "chevron-up",
+                       config: null});
+  },
+
+  componentWillReceiveProps : function(nextProps){
+       if(nextProps.navAddition && nextProps.navAddition.length > 0 && 
nextProps.navAddition[0]){
+               this.setState({selection : nextProps.navAddition[0]});
+       }else{
+               this.setState({selection : null});
+       }
+  },
+
+  showHideDataFiles() {
+         this.setState({showDataFiles: !this.state.showDataFiles, 
showHideDataFilesIcon: (!this.state.showDataFiles? "chevron-up" : 
"chevron-down")});
+  },
+
+  showHideTables() {
+         this.setState({showTables: !this.state.showTables, 
showHideTablesIcon: (!this.state.showTables? "chevron-up" : "chevron-down")});
+  },
+
+  showHideDocuments() {
+         this.setState({showDocuments: !this.state.showDocuments, 
showHideDocumentsIcon: (!this.state.showDocuments ? "chevron-up" : 
"chevron-down")});
+  },
+
+  createAnnotations : function(target){
+               this.setState({showCreateAnnotationsDialog: true, selectedAsset 
: target.reference.id});
+  },
+
+  startAnalysis : function(target){
+               this.setState({showAnalysisRequestDialog: true, selectedAsset : 
target.reference.id});
+  },
+
+  viewInAtlas : function(target){
+         var win = window.open(target.reference.url, '_blank');
+         win.focus();
+  },
+
+  loadDataFiles : function(){
+         var  resultQuery = "from DataFile";
+         this.setState({
+                       dataFileAssets : null
+         });
+         var req = AtlasHelper.searchAtlasMetadata(resultQuery,
+                       function(data){
+                               this.setState({
+                                       dataFileAssets : data
+                               });
+                       }.bind(this),
+                       function(error){
+
+                       }
+               );
+         this.storeAbort(req.abort);
+  },
+
+  loadTables : function(){
+         var  resultQuery = "from Table";
+         this.setState({
+                       tableAssets : null
+         });
+         var req = AtlasHelper.searchAtlasMetadata(resultQuery,
+                       function(data){
+                               this.setState({
+                                       tableAssets : data
+                               });
+                       }.bind(this),
+                       function(error){
+
+                       }
+               );
+         this.storeAbort(req.abort);
+  },
+
+  loadDocuments : function(){
+         var  resultQuery = "from Document";
+         this.setState({
+                       docAssets : null
+         });
+         var req = AtlasHelper.searchAtlasMetadata(resultQuery,
+                       function(data){
+                               this.setState({
+                                       docAssets : data
+                               });
+                       }.bind(this),
+                       function(error){
+
+                       }
+               );
+         this.storeAbort(req.abort);
+  },
+
+  render() {
+    var actions = [
+             {
+                  assetType: ["DataFiles", "Tables", "Documents"],
+                  actions : [
+                     {
+                         label: "Start analysis (annotation types)",
+                         func: this.createAnnotations
+                     } ,
+                     {
+                         label: "Start analysis (service sequence)",
+                         func: this.startAnalysis
+                     } ,
+                     {
+                         label: "View in atlas",
+                         func: this.viewInAtlas
+                     }
+                   ]
+                }
+            ];
+
+    return (
+               <div className="jumbotron">
+                  <h3>Data sets</h3>
+                      <div>
+                        <NewAnalysisRequestDialog 
alertCallback={this.props.alertCallback} dataSetId={this.state.selectedAsset} 
show={this.state.showAnalysisRequestDialog} 
onClose={function(){this.setState({showAnalysisRequestDialog: 
false});}.bind(this)} />
+                        <NewCreateAnnotationsDialog 
alertCallback={this.props.alertCallback} dataSetId={this.state.selectedAsset} 
show={this.state.showCreateAnnotationsDialog} 
onClose={function(){this.setState({showCreateAnnotationsDialog: 
false});}.bind(this)} />
+                        Here are all data sets of the metadata repository that 
are available for analysis.
+                        <p/>
+                        <Panel collapsible expanded={this.state.showDataFiles} 
header={
+                                        <div style={{textAlign:"right"}}>
+                                               <span style={{float: 
"left"}}>Data Files</span>
+                                               <Button bsStyle="primary" 
onClick={function(){this.loadDataFiles();}.bind(this)}>
+                                                       Refresh
+                                               </Button>
+                                               <Button 
onClick={this.showHideDataFiles}>
+                                                       <Glyphicon 
glyph={this.state.showHideDataFilesIcon} />
+                                               </Button>
+                                       </div>}>
+                               <ODFBrowser ref="dataFileBrowser" 
type={"DataFiles"} selection={this.state.selection} actions={actions} 
assets={this.state.dataFileAssets} />
+                        </Panel>
+                        <Panel collapsible expanded={this.state.showTables} 
header={
+                                        <div style={{textAlign:"right"}}>
+                                               <span style={{float: 
"left"}}>Relational Tables</span>
+                                               <Button bsStyle="primary" 
onClick={function(){this.loadTables();}.bind(this)}>
+                                                       Refresh
+                                               </Button>
+                                               <Button 
onClick={this.showHideTables}>
+                                                       <Glyphicon 
glyph={this.state.showHideTablesIcon} />
+                                               </Button>
+                                       </div>}>
+                               <ODFBrowser ref="tableBrowser" type={"Tables"} 
actions={actions} assets={this.state.tableAssets} />
+                        </Panel>
+                        <Panel collapsible expanded={this.state.showDocuments} 
 header={
+                                        <div style={{textAlign:"right"}}>
+                                               <span style={{float: 
"left"}}>Documents</span>
+                                               <Button bsStyle="primary" 
onClick={function(){this.loadDocuments();}.bind(this)}>
+                                                       Refresh
+                                               </Button>
+                                               <Button 
onClick={this.showHideDocuments}>
+                                                       <Glyphicon 
glyph={this.state.showHideDocumentsIcon} />
+                                               </Button>
+                                       </div>}>
+                                       <ODFBrowser ref="docBrowser" 
type={"Documents"} actions={actions} assets={this.state.docAssets}/>
+                        </Panel>
+                      </div>
+                   </div>
+                    );
+  }
+
+});
+
+
+////////////////////////////////////////////////////////////////////////
+// main component
+var ODFUI = React.createClass({
+
+   componentDidMount: function() {
+          $(window).bind("hashchange", this.parseUrl);
+          this.parseUrl();
+   },
+
+   parseUrl : function(){
+         var target = constants_ODFNavBar.gettingStarted;
+         var navAddition = null;
+         var hash = document.location.hash;
+         if(hash && hash.length > 1){
+                 hash = hash.split("#")[1];
+                 var split = hash.split("/");
+                 var navHash = split[0];
+                 if(split.length > 0){
+                         navAddition = split.slice(1);
+                 }
+                 if(constants_ODFNavBar[navHash]){
+                         target = constants_ODFNavBar[navHash];
+                 }
+         }
+         this.setState({
+                 activeNavBarItem: target,
+             navAddition: navAddition}
+         );
+  },
+
+  getInitialState: function() {
+         return ({
+             activeNavBarItem: constants_ODFNavBar.gettingStarted,
+             navAddition: null,
+             globalAlert: {
+               type: "",
+               message: ""
+             }
+         });
+  },
+
+  handleNavBarSelection: function(selection) {
+         $.each(constants_ODFNavBar, function(key, ref){
+                 if(ref == selection){
+                         document.location.hash = key;
+                 }
+         });
+    this.setState({ activeNavBarItem: selection });
+  },
+
+  handleAlert: function(alertInfo) {
+    this.setState({ globalAlert: alertInfo });
+  },
+
+  render: function() {
+    var alertComp = null;
+    if (this.state.globalAlert.type != "") {
+       alertComp = <Alert 
bsStyle={this.state.globalAlert.type}>{this.state.globalAlert.message}</Alert>;
+    }
+
+    var contentComponent = <GettingStartedPage 
alertCallback={this.handleAlert}/>;
+    if (this.state.activeNavBarItem == constants_ODFNavBar.configuration) {
+       contentComponent = <ConfigurationPage 
alertCallback={this.handleAlert}/>;
+    } else if (this.state.activeNavBarItem == 
constants_ODFNavBar.discoveryServices) {
+       contentComponent = <DiscoveryServicesPage 
alertCallback={this.handleAlert}/>;
+    } else if (this.state.activeNavBarItem == constants_ODFNavBar.monitor) {
+       contentComponent = <MonitorPage alertCallback={this.handleAlert}/>;
+    } else if (this.state.activeNavBarItem == constants_ODFNavBar.analysis) {
+       contentComponent = <AnalysisRequestsPage 
navAddition={this.state.navAddition} alertCallback={this.handleAlert}/>;
+    } else if (this.state.activeNavBarItem == constants_ODFNavBar.data) {
+       contentComponent = <AnalysisDataSetsPage 
navAddition={this.state.navAddition} alertCallback={this.handleAlert}/>;
+    }
+
+    var divStyle = {
+      marginLeft: "80px",
+      marginRight: "80px"
+    };
+
+    return (
+        <div>
+           <ODFNavBar activeKey={this.state.activeNavBarItem} 
selectCallback={this.handleNavBarSelection}></ODFNavBar>
+           <div style={divStyle}>
+              {alertComp}
+              {contentComponent}
+           </div>
+        </div>
+    );
+  }
+});
+
+var div = $("#odf-toplevel-div")[0];
+ReactDOM.render(<ODFUI/>, div);

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/webapp/scripts/odf-globals.js
----------------------------------------------------------------------
diff --git a/odf/odf-web/src/main/webapp/scripts/odf-globals.js 
b/odf/odf-web/src/main/webapp/scripts/odf-globals.js
new file mode 100755
index 0000000..d67a2d3
--- /dev/null
+++ b/odf/odf-web/src/main/webapp/scripts/odf-globals.js
@@ -0,0 +1,54 @@
+/**
+ *
+ * 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.
+ */
+const CONTEXT_ROOT = ""; // window.location.origin + "/" + 
(window.location.pathname.split("/")[1].length > 0 ? 
window.location.pathname.split("/")[1] + "/" : "");
+const API_PREFIX = CONTEXT_ROOT + API_PATH;
+const SERVICES_URL = API_PREFIX + "services";
+const ANALYSIS_URL = API_PREFIX + "analyses";
+const ENGINE_URL = API_PREFIX + "engine";
+const CONFIG_URL = API_PREFIX + "config";
+const METADATA_URL = API_PREFIX + "metadata";
+const IMPORT_URL = API_PREFIX + "import";
+const ANNOTATIONS_URL = API_PREFIX + "annotations";
+
+var OdfUrls = {
+       "contextRoot": CONTEXT_ROOT,
+       "apiPrefix": API_PREFIX,
+       "servicesUrl": SERVICES_URL,
+       "analysisUrl": ANALYSIS_URL,
+       "engineUrl": ENGINE_URL,
+       "configUrl": CONFIG_URL,
+       "metadataUrl": METADATA_URL,
+       "importUrl": IMPORT_URL,
+       "annotationsUrl": ANNOTATIONS_URL,
+
+       getPathValue: function(obj, path) {
+           var value = obj;
+        $.each(path.split("."),
+            function(propKey, prop) {
+               // if value is null, do nothing
+               if (value) {
+                   if(value[prop] != null && value[prop] != undefined){
+                       value = value[prop];
+                   } else {
+                       value = null;
+                   }
+               }
+           }
+        );
+        return value;
+       }
+};
+
+module.exports = OdfUrls;

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/6d19e129/odf/odf-web/src/main/webapp/scripts/odf-logs.js
----------------------------------------------------------------------
diff --git a/odf/odf-web/src/main/webapp/scripts/odf-logs.js 
b/odf/odf-web/src/main/webapp/scripts/odf-logs.js
new file mode 100755
index 0000000..ecca602
--- /dev/null
+++ b/odf/odf-web/src/main/webapp/scripts/odf-logs.js
@@ -0,0 +1,83 @@
+/**
+ *
+ * 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.
+ */
+var $ = require("jquery");
+var React = require("react");
+var ReactDOM = require("react-dom");
+var d3 = require("d3");
+var ReactBootstrap = require("react-bootstrap");
+var ReactD3 = require("react-d3-components");
+var ODFGlobals = require("./odf-globals.js");
+var AJAXCleanupMixin = require("./odf-mixins.js");
+var Input = ReactBootstrap.Input;
+
+var REFRESH_DELAY = 5000;
+
+var ODFLogViewer = React.createClass({
+       mixins : [AJAXCleanupMixin],
+
+       getInitialState : function(){
+               return {logLevel : "ALL", log : ""};
+       },
+
+       getLogs : function() {
+               const url = ODFGlobals.engineUrl + 
"/log?numberOfLogs=50&logLevel=" + this.state.logLevel;
+        var req = $.ajax({
+            url: url,
+            contentType: "text/plain",
+            type: 'GET',
+            success: function(data) {
+               this.setState({log: data});
+            }.bind(this),
+            error: function(xhr, status, err) {
+              var msg = "ODF log request failed, " + err.toString();
+              this.props.alertCallback({type: "danger", message: msg});
+            }.bind(this)
+        });
+
+        this.storeAbort(req.abort);
+       },
+
+       componentWillMount : function() {
+               this.getLogs();
+       },
+
+       componentWillUnmount () {
+           this.refreshInterval && clearInterval(this.refreshInterval);
+           this.refreshInterval = false;
+       },
+
+       componentWillReceiveProps: function(nextProps){
+               if(!nextProps.visible){
+                        this.refreshInterval && 
clearInterval(this.refreshInterval);
+                        this.refreshInterval = false;
+               }else if(!this.refreshInterval){
+                       this.refreshInterval = window.setInterval(this.getLogs, 
REFRESH_DELAY);
+               }
+       },
+       render : function(){
+               return (<div>
+                                       <h4>ODF system logs</h4>
+                                       <h5>(This only works for the node this 
web application is running on, logs from other ODF nodes in a clustered 
environment will not be displayed)</h5>
+                                       <Input label="Log level:" type="select" 
onChange={(el) => {this.setState({logLevel : el.target.value}); 
this.getLogs()}} value={this.state.logLevel}>
+                                       <option value="ALL">ALL</option>
+                                       <option value="FINE">FINE</option>
+                                       <option value="INFO">INFO</option>
+                                       <option value="WARNING">WARNING</option>
+                               </Input>
+                               <textarea disabled style={{width: '100%', 
height: '700px'}} value={this.state.log} /></div>);
+       }
+});
+
+module.exports = ODFLogViewer;

Reply via email to