diff --git a/14.1.1-0-20/docs/components/defaultView.js b/14.1.1-0-20/docs/components/defaultView.js new file mode 100644 index 0000000000000..cc5a58c8b1210 --- /dev/null +++ b/14.1.1-0-20/docs/components/defaultView.js @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * + * 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. + */ + +/* global Vue */ + +window.allComponents["defaultView"] = { + init: function(){ + // create a script element + const scriptElt = document.createElement("script"); + scriptElt.id = "default-view"; + scriptElt.type = "text/x-template"; + scriptElt.text = + ` + + + + + + + + + + + `; + + // insert it in the document + const firstScriptElt = document.getElementsByTagName('script')[0]; + firstScriptElt.parentNode.insertBefore(scriptElt, firstScriptElt); + + window.scrollFix = function (hashtag) { + var element = document.querySelector(hashtag); + if(element){ + var initialY = window.pageYOffset; + var elementY = initialY + element.getBoundingClientRect().top; + var targetY = document.body.scrollHeight - elementY < window.innerHeight + ? document.body.scrollHeight - window.innerHeight + : elementY; + + // offset + targetY -= 75; + window.scrollTo(0, targetY); + } + }; + + Vue.component('defaultView', { + template: '#default-view', + mounted () { + if (this.$route.hash) { + setTimeout(() => scrollFix(this.$route.hash), 2); + } + }, + methods: { + afterLeave () { + if (this.$route.hash) { + setTimeout(() => scrollFix(this.$route.hash), 2); + } else { + window.scrollTo(0, 0); + } + } + } + }); + } +}; diff --git a/14.1.1-0-20/docs/components/docFooter.js b/14.1.1-0-20/docs/components/docFooter.js new file mode 100644 index 0000000000000..2db6509149de7 --- /dev/null +++ b/14.1.1-0-20/docs/components/docFooter.js @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * + * 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. + */ + +/* global Vue */ + +window.allComponents["docFooter"] = { + init: function(){ + // create a script element + const scriptElt = document.createElement("script"); + scriptElt.id = "doc-footer"; + scriptElt.type = "text/x-template"; + scriptElt.text = + ` + + + + + arrow_back + + Previous + + + + + + + + + + Next + + + arrow_forward + + + + + `; + + // insert it in the document + const firstScriptElt = document.getElementsByTagName('script')[0]; + firstScriptElt.parentNode.insertBefore(scriptElt, firstScriptElt); + + Vue.component('docFooter', { + template: '#doc-footer', + computed: { + previous () { + return this.$store.state.previous; + }, + next () { + return this.$store.state.next; + } + } + }); + } +}; diff --git a/14.1.1-0-20/docs/components/docNav.js b/14.1.1-0-20/docs/components/docNav.js new file mode 100644 index 0000000000000..6b10c38df2549 --- /dev/null +++ b/14.1.1-0-20/docs/components/docNav.js @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * + * 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. + */ + +/* global Vue, navItems, config */ + +window.allComponents['docNav'] = { + init: function(){ + // create a script element + const scriptElt = document.createElement("script"); + scriptElt.id = "doc-nav"; + scriptElt.type = "text/x-template"; + scriptElt.text = + ` + + + + + + + {{ navIcon }} + + + + {{ navTitle }} + + + + Version: {{ release === releases[0] ? \`(\${release})\` : release }} + arrow_drop_down + + + + {{ release }} + + + {{ release }} + + + + + Version: {{ release === releases[0] ? \`(\${release})\` : release }} + + + + + + + + + + + `; + + // insert it in the document + const firstScriptElt = document.getElementsByTagName('script')[0]; + firstScriptElt.parentNode.insertBefore(scriptElt, firstScriptElt); + + Vue.component('docNav', { + template: '#doc-nav', + data: function () { + return { + navIcon: config.navIcon, + navLogo: config.navLogo, + navTitle: config.navTitle, + release: config.release, + releases: config.releases, + items: navItems + }; + }, + computed: { + filter() { + const color = this.$store.state.currentColor; + let hue = 0; + if (color === 'purple') { + hue = 420; + } else if (color === 'darken-3 pink') { + hue = 480; + } else if (color === 'indigo') { + hue = 370; + } else if (color === 'cyan') { + hue = 337; + } else if (color === 'teal') { + hue = 315; + } + return { + filter: `hue-rotate(${hue}deg)` + }; + }, + isActive: { + get() { + return this.$store.state.sidebar; + }, + set(val) { + this.$store.commit('vuetify/SIDEBAR', val); + } + } + }, + methods: { + setIsSearching(val) { + this.$store.commit('sitegen/ISSEARCHING', val); + } + } + }); + } +}; diff --git a/14.1.1-0-20/docs/components/docToolbar.js b/14.1.1-0-20/docs/components/docToolbar.js new file mode 100644 index 0000000000000..84bb4eee1004d --- /dev/null +++ b/14.1.1-0-20/docs/components/docToolbar.js @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * + * 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. + */ + +/* global Vue, lunr, search_index, searchIndex */ + +window.allComponents['docToolbar'] = { + init: function(){ + // create a script element + const scriptElt = document.createElement("script"); + scriptElt.id = "doc-toolbar"; + scriptElt.type = "text/x-template"; + scriptElt.text = + `
+ + + + search + + + + + + +
+ + + +
+
+
{{searchMeta}}
+
+
    +
  1. + +
    +

    +

    +
    +
    + +
    +

    +

    +
    +
    +
  2. +
+
+
+
+
+
`; + + // insert it in the document + const firstScriptElt = document.getElementsByTagName('script')[0]; + firstScriptElt.parentNode.insertBefore(scriptElt, firstScriptElt); + + var messages = { + placeholder: "Type to start searching", + none: "No matching documents", + one: "1 matching document", + other: "# matching documents" + }; + + const truncate = (string, n) => { + let i = n; + if (string.length > i) { + while (string[i] !== " " && --i > 0) + ; + return `${string.substring(0, i)}...`; + } + return string; + }; + + Vue.component('docToolbar', { + template: '#doc-toolbar', + data: function () { + return{ + results: [], + values_: null, + index_: null, + searchMeta: messages.placeholder, + search: '' + }; + }, + + watch: { + search(val) { + /* Abort early, if index is not build or input hasn't changed */ + if (!this.index_ || val === this.value_) + return; + + this.value_ = val; + + /* Abort early, if search input is empty */ + if (this.value_ === null || this.value_.length === 0) { + this.results = []; + this.searchMeta = messages.placeholder; + return; + } + + /* Perform search on index and group sections by document */ + const result = this.index_ + + /* Append trailing wildcard to all terms for prefix querying */ + .query(query => { + this.value_.toLowerCase().split(" ") + .filter(Boolean) + .forEach(term => { + query.term(term, {wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING}); + }); + }) + + /* Process query results */ + .reduce((items, item) => { + const doc = this.docs_.get(item.ref); + //if (doc.parent) { + // const ref = doc.parent.location + // items.set(ref, (items.get(ref) || []).concat(item)) + //} else { + const ref = doc.location; + items.set(ref, (items.get(ref) || [])); + //} + return items; + }, new Map); + + /* Assemble regular expressions for matching */ + const matches = []; + this.value_.toLowerCase().split(" ") + .filter(Boolean) + .forEach(query => { + matches.push(new RegExp(`(|${lunr.tokenizer.separator})(${query})`, "img")); + }); + + const highlight = (_, separator, token) => + `${separator}${token}`; + + /* Reset stack and render results */ + this.results = []; + result.forEach((items, ref) => { + const doc = this.docs_.get(ref); + + const entry = {}; + + /* render article */ + entry.doc = {}; + entry.doc.location = doc.location; + entry.doc.title = doc.title; + entry.doc.h1 = doc.title; + entry.doc.text = doc.text; + matches.forEach(match => { + entry.doc.h1 = entry.doc.h1.replace(match, highlight); + entry.doc.text = entry.doc.text.replace(match, highlight); + }); + + /* render sections */ + entry.sections = items.map(item => { + const section = this.docs_.get(item.ref); + const sectionEntry = {}; + sectionEntry.location = section.location; + sectionEntry.title = section.title; + sectionEntry.h1 = section.title; + sectionEntry.text = truncate(section.text, 400); + matches.forEach(match => { + sectionEntry.h1 = sectionEntry.h1.replace(match, highlight); + sectionEntry.text = sectionEntry.text.replace(match, highlight); + }); + return sectionEntry; + }); + + this.results.push(entry); + }); + + /* Update search metadata */ + switch (result.size) { + case 0: + this.searchMeta = messages.none; + break; + case 1: + this.searchMeta = messages.one; + break; + default: + this.searchMeta = messages.other.replace("#", result.size); + } + } + }, + mounted() { + this.init(); + }, + methods: { + init() { + const _this = this; + searchIndex.then(function (search_index) { + _this.initDocSearch(search_index); + }).catch(function (ex){ + console.error("searchIndex error", ex); + }); + }, + setIsSearching(val) { + this.$refs.toolbar.isScrolling = !val; + this.$store.commit('sitegen/ISSEARCHING', val); + if (val) { + this.$nextTick(() => { + this.$refs.search.focus(); + }); + } else { + this.search = null; + } + }, + initDocSearch(search_index) { + /* Preprocess and index sections and documents */ + const data = search_index.docs; + this.docs_ = data.reduce((docs, doc) => { + const [path, hash] = doc.location.split("#"); + + /* Associate section with parent document */ + if (hash) { + doc.parent = docs.get(path); + + /* Override page title with document title if first section */ + if (doc.parent && !doc.parent.done) { + doc.parent.title = doc.title; + doc.parent.text = doc.text; + doc.parent.done = true; + } + } + + /* Index sections and documents, but skip top-level headline */ + if (!doc.parent || doc.parent.title !== doc.title) + docs.set(doc.location, doc); + return docs; + }, new Map); + + /* eslint-disable no-invalid-this */ + const docs = this.docs_, + lang = this.lang_; + + /* Create stack and index */ + this.stack_ = []; + this.index_ = lunr(function () { + const filters = { + "search.pipeline.trimmer": lunr.trimmer, + "search.pipeline.stopwords": lunr.stopWordFilter + }; + + /* Disable stop words filter and trimmer, if desired */ + const pipeline = Object.keys(filters).reduce((result, name) => { + result.push(filters[name]); + return result; + }, []); + + /* Remove stemmer, as it cripples search experience */ + this.pipeline.reset(); + if (pipeline) + this.pipeline.add(...pipeline); + + /* Index fields */ + this.field("title", {boost: 10}); + this.field("text"); + this.ref("location"); + + /* Index documents */ + docs.forEach(doc => this.add(doc)); + }); + }, + toggleSidebar() { + this.$store.commit('vuetify/SIDEBAR', !this.$store.state.sidebar); + } + } + }); + } +}; diff --git a/14.1.1-0-20/docs/components/docView.js b/14.1.1-0-20/docs/components/docView.js new file mode 100644 index 0000000000000..8b03585437693 --- /dev/null +++ b/14.1.1-0-20/docs/components/docView.js @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * + * 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. + */ + +/* global Vue */ + +window.allComponents["docView"] = { + init: function(){ + // create a script element + const scriptElt = document.createElement("script"); + scriptElt.id = "doc-view"; + scriptElt.type = "text/x-template"; + scriptElt.text = + ` + + `; + + // insert it in the document + const firstScriptElt = document.getElementsByTagName('script')[0]; + firstScriptElt.parentNode.insertBefore(scriptElt, firstScriptElt); + + Vue.component('docView', { + template: '#doc-view' + }); + } +}; diff --git a/14.1.1-0-20/docs/components/mainView.js b/14.1.1-0-20/docs/components/mainView.js new file mode 100644 index 0000000000000..b7f8ea37be7f6 --- /dev/null +++ b/14.1.1-0-20/docs/components/mainView.js @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * + * 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. + */ + +/* global config, Vue */ + +window.allComponents["mainView"] = { + init: function(){ + // create a script element + const scriptElt = document.createElement("script"); + scriptElt.id = "main-view"; + scriptElt.type = "text/x-template"; + scriptElt.text = + ` + + + + `; + + // insert it in the document + const firstScriptElt = document.getElementsByTagName('script')[0]; + firstScriptElt.parentNode.insertBefore(scriptElt, firstScriptElt); + + Vue.component('mainView', { + template: '#main-view', + created() { + const metaData = this.$route.meta || {}; + const section = this.$route.path.split('/'); + let h1 = metaData.h1; + let h1Prefix = null; + + if (section.length > 2) { + h1Prefix = capitalize(section[1]); + } + + if (metaData.h1Prefix) { + h1Prefix = metaData.h1Prefix; + } + + if (h1Prefix) { + h1 = ` +
${h1Prefix}  —   +
${h1}
+ `; + } + document.title = `${metaData.title}`; + this.$store.commit('vuetify/H1', h1); + }, + computed: { + component() { + if (this.$route.meta.customLayout !== null) + return this.$route.meta.customLayout; + return 'defaultView'; + } + }, + watch: { + '$route'() { + this.setMeta(); + this.$store.commit('vuetify/COLOR', this.getColor(this.$route.path)); + this.$store.commit('vuetify/NAVGROUP', this.getNavGroup(this.$route.path)); + this.getPrevNext(); + } + }, + mounted() { + this.$store.commit('vuetify/COLOR', this.getColor(this.$route.path)); + this.getPrevNext(); + }, + methods: { + setMeta() { + if (typeof document === 'undefined') return; + const metaData = this.$route.meta || {}; + const section = this.$route.path.split('/'); + let h1 = metaData.h1; + let h1Prefix = null; + + if (section.length > 2) { + h1Prefix = capitalize(section[1]); + } + + if (metaData.h1Prefix) { + h1Prefix = metaData.h1Prefix; + } + + if (h1Prefix) { + h1 = `${h1Prefix}  —  ${h1}`; + } + + document.title = `${metaData.title}`; + document.querySelector('meta[name="description"]').setAttribute('content', metaData.description); + document.querySelector('meta[name="keywords"]').setAttribute('content', metaData.keywords); + + this.$store.commit('vuetify/H1', h1); + }, + getNavGroup(path){ + return path.split('/')[1]; + }, + getColor(path) { + const section = path.split('/'); + if (section !== 'undefined' && section.length > 1) { + color = config.pathColors["/" + section[1]]; + } + if(color == undefined){ + color = config.pathColors['*']; + } + if (color === undefined) { + color = 'primary'; + } + return color; + }, + getNext(currentIndex) { + for(var i=currentIndex ; i < this.$router.options.routes.length - 1 ; i++){ + let _next = this.$router.options.routes[i + 1]; + if(_next.meta && _next.meta.hasNav === true){ + return _next; + } + } + return null; + }, + getPrevNext() { + const currentIndex = this.$router.options.routes.findIndex(r => r.path === this.$route.path); + const previous = currentIndex > 0 ? this.$router.options.routes[currentIndex - 1] : null; + const next = this.getNext(currentIndex); + + this.$store.commit('vuetify/NEXT', { + name: next ? next.meta && next.meta.h1 : null, + color: next ? this.getColor(next.path) : null, + route: next ? next.path : null + }); + + this.$store.commit('vuetify/PREVIOUS', { + name: previous ? previous.meta && previous.meta.h1 : null, + color: previous ? this.getColor(previous.path) : null, + route: previous && previous.meta.customLayout === null ? previous.path : null + }); + }, + meta(obj) { + this.title = obj.h1; + this.$store.commit('vuetify/TITLE', obj.title); + this.$store.commit('vuetify/DESCRIPTION', obj.description); + this.$store.commit('vuetify/KEYWORDS', obj.keywords); + } + } + }); + } +}; diff --git a/14.1.1-0-20/docs/components/markup.js b/14.1.1-0-20/docs/components/markup.js new file mode 100644 index 0000000000000..ec15116815a35 --- /dev/null +++ b/14.1.1-0-20/docs/components/markup.js @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * + * 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. + */ + +/* global Vue, hljs */ + +window.allComponents["markup"] = { + init: function(){ + // create a script element + const scriptElt = document.createElement("script"); + scriptElt.id = "markup"; + scriptElt.type = "text/x-template"; + scriptElt.text = + `
+
+
+
+
+ content_copy + + Copied + +
+
+
`; + + // insert it in the document + const firstScriptElt = document.getElementsByTagName('script')[0]; + firstScriptElt.parentNode.insertBefore(scriptElt, firstScriptElt); + + Vue.component('markup', { + template: '#markup', + data: function() { + return { + copied: false, + content: '' + }; + }, + props: { + lang: { + type: String, + required: false + }, + title: { + type: String, + required: false + } + }, + mounted: function () { + hljs.highlightBlock(this.$refs.markup); + }, + methods: { + copyMarkup () { + const markup = this.$refs.markup; + markup.setAttribute('contenteditable', 'true'); + markup.focus(); + document.execCommand('selectAll', false, null); + this.copied = document.execCommand('copy'); + markup.removeAttribute('contenteditable'); + setTimeout(() => this.copied = false, 2000); + } + } + }); + } +}; diff --git a/14.1.1-0-20/docs/css/helidon-sitegen.css b/14.1.1-0-20/docs/css/helidon-sitegen.css new file mode 100644 index 0000000000000..3be1b0820f166 --- /dev/null +++ b/14.1.1-0-20/docs/css/helidon-sitegen.css @@ -0,0 +1,769 @@ +/* + * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * + * 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. + */ + +/* overrides on top of vuetify css */ +.container.fluid { + height:100%; +} + +a { + text-decoration: none; + color:#3f51b5!important; + word-break:break-word +} + +.section a:before, .section-def a:before{ + transition:color .125s!important; +} +.section a:active, .section a:hover, .section-def a:active, .section-def a:hover{ + color:#536dfe!important; +} + +p { + margin-bottom: 16px; + margin-top: 16px; +} + +.mb-3 { + margin-bottom: 16px!important; +} + +/* nav menu link */ + +.navigation-drawer { + padding-bottom: 20px; +} + +.navigation-drawer>.list .list__tile:not(.list__tile--active) { + color:white!important; +} +/* active nav menu link*/ +.navigation-drawer>.list .list__tile--active { + color: #939699!important; +} + +pre, code { + display: inline; + word-break: break-word; + padding: 4px; + font-weight: 500; + box-shadow: none; + color: #37474f; + /*font-size: 0.85rem;*/ +} + +code:after, code:before { + content: none; +} + +.display-1 { + font-weight: 300; +} + +h2 { + font-weight:300; + letter-spacing:normal!important; + font-size:34px!important; + line-height:40px!important; + margin-top:40px!important; + margin-bottom:16px!important; +} + +h3 { + font-weight:300; + letter-spacing:normal!important; + font-size:26px!important; + line-height:40px!important; + margin-top:40px!important; + margin-bottom:16px!important; +} + +h4 { + font-weight:300; + letter-spacing:normal!important; + font-size:22px!important; + line-height:40px!important; + margin-top:40px!important; + margin-bottom:16px!important; +} + +/* App component styles */ + +[data-app]>main>.container{ + min-height:calc(100vh - 156px) +} + +#app{ + -webkit-transition:.3s ease-in-out; + -o-transition:.3s ease-in-out; + transition:.3s ease-in-out +} + +/* DocView component styles */ + +.view{ + margin-left:auto; + margin-right:auto; + height:100%; +} + +dt{ + font-weight:900 +} + +.section li { + line-height:1.8; +} + +ul.ulist { + padding-left:24px; +} + +.section ul li p { + margin-bottom: 0; + margin-top: 0; + line-height: inherit; +} + +.section p{ + line-height:1.6; + font-size: 1.15rem; + margin-bottom:1rem +} + +.section p strong { + font-weight:900 +} + +/* DocNavBar component styles */ + +.vuetify .router-link-active{ + display:-webkit-box; + display:-ms-flexbox; + display:flex; + -webkit-box-align:center; + -ms-flex-align:center; + align-items:center; + text-decoration:none; +} + +.navLogo { + padding-top:4px; + width:40px; + height:40px; + border-radius:50%; + background-color:#f5f5f5; +} + +.navLogo img{ + width:32px!important; + height:32px!important; +} + +.vuetify .list__tile__avatar img{ + border-radius:0!important +} +.navigation-drawer--mini-variant .your-logo-here{ + display:none +} + +/** search results **/ + +.search-container { + width:100%; + position:relative; +} + +.search-input { + border-radius: 2px 2px 0 0 !important; + z-index:2; +} + +.search-overlay { + position: fixed; + width: 100%; + height: 100%; + transition: width 0s,height 0s,opacity .25s; + opacity: 1; + top: 0; + left: 0; + z-index:1; + background-color: rgba(0,0,0,.54); +} + +.search-output{ + margin-right: 16px; + width: 100%; + position: absolute; + background-color: white; + box-shadow:inset 0 0.1rem 0 rgba(0,0,0,.07); + z-index: 2; + max-height: 75vh; + overflow-y: auto; +} + +.search-output::-webkit-scrollbar{ + width:.4rem; + height:.4rem +} +.search-output::-webkit-scrollbar-thumb{ + background-color:rgba(0,0,0,.26) +} +.search-output::-webkit-scrollbar-thumb:hover{ + background-color:#536dfe +} + + +.search-result-meta { + line-height: 46px; + padding-left: 56px; + color: rgba(0,0,0,.54); + background-color: rgba(0,0,0,.07); +} + +.search-result{ + color: rgba(0,0,0,.87); + word-break: break-word; +} + +.search-result > ol { + margin: 0; + padding: 0; + border-top: .1rem solid rgba(0,0,0,.07); + list-style: none; +} + +.search-result > ol > li { + box-shadow: 0 -0.1rem 0 rgba(0,0,0,.07); +} +.search-result > ol > li > a { + display: block; + transition: background .25s; + outline: 0; + overflow: hidden; + color: inherit; + text-decoration: none; +} + +.search-result > ol > li > a:hover{ + background-color:rgba(83,109,254,.1) +} + +.search-result > ol > li > a > article { + padding-left: 56px; + padding-right: 52px; +} + +.search-result > ol > li > a:first-child> article:before{ + position: absolute; + left: 0; + padding: 0 1rem 0 1rem; + transition: opacity .25s; + color: rgba(0,0,0,.54); + content: "\E880"; + font-family: Material Icons; + font-style: normal; + font-variant: normal; + font-size: 1.6rem; + display: inline-block; + font-weight: 400; + line-height: 1; + cursor: pointer; +} +.search-result > ol > li > a > article em { + font-style: normal; + font-weight: 700; + text-decoration: underline; +} + +.search-result > ol > li > a:first-child > article > h1 { + margin: 1.1rem 0; + font-size: 1.1rem; + font-weight: 400; + line-height: 1.4; +} + +.search-result > ol > li > a > article > h1 { + font-size: 0.9rem; + font-weight: 700; + line-height: 1.4; +} + +.search-result > ol > li > a > article > p { + display: -webkit-box; + max-height: 3rem; + margin: .5em 0; + color: rgba(0,0,0,.54); + line-height: 1.5; + text-overflow: ellipsis; + overflow: hidden; + -webkit-line-clamp: 2; +} + +/* DocFooter component styles */ + +.main-footer{ + height:auto; + margin-top:30px; +} +.main-footer .col{ + -webkit-transition:.3s ease-in-out; + -o-transition:.3s ease-in-out; + transition:.3s ease-in-out +} +.main-footer .list__tile{ + height:100px +} +.main-footer .list__tile__title{ + color:#fff +} +.main-footer .list__tile__sub-title{ + color:hsla(0,0%,100%,.3) +} + +/* Markup component styles */ + +.block-title { + margin-top:20px; + margin-bottom:10px; + font-size: 1rem; + font-weight: 500; + color: rgba(0,0,0,.7); +} + +.markup { + font-size: 1.2rem; + transition: .3s ease-out; + box-shadow: none; + display: block; + padding: 0; + margin-bottom: 20px; + background:rgba(0,0,0,.04); + border-radius: 2px; + position: relative; + align-items: center; +} + +.markup pre { + display:block; + padding: 0.5rem 0.5rem; + overflow: auto; +} + +.markup .markup__copy { + position: absolute; + right: 0.8rem; + top: 0.8rem; + cursor: pointer; +} + +.markup__copy__message{ + display:block; + position:absolute; + top:0; + right:2rem; + padding:.4rem 1rem; + -webkit-transform:translateX(.8rem); + transform:translateX(.8rem); + transition:opacity .175s,-webkit-transform .25s cubic-bezier(.9,.1,.9,0); + transition:transform .25s cubic-bezier(.9,.1,.9,0),opacity .175s; + transition:transform .25s cubic-bezier(.9,.1,.9,0),opacity .175s,-webkit-transform .25s cubic-bezier(.9,.1,.9,0); + border-radius:.2rem; + background-color:rgba(0,0,0,.54); + color:#fff; + font-size:0.9rem; + white-space:nowrap; + opacity:0; + pointer-events:none +} +.markup__copy__message--active{ + -webkit-transform:translateX(0); + transform:translateX(0); + transition:opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1); + transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms; + transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1); + opacity:1; + pointer-events:auto +} +.markup__copy__message:before{ + content:attr(aria-label) +} +.markup__copy__message:after{ + display:block; + position:absolute; + top:50%; + right:-.4rem; + width:0; + margin-top:-.4rem; + border-width:.4rem 0 .4rem .4rem; + border-style:solid; + border-color:transparent rgba(0,0,0,.54); + content:"" +} + +.markup__copy .icon { + transition: opacity .2s ease-in; + font-size: 1.25rem; + opacity: 0.2; + color: rgba(0,0,0, 0.7); + cursor: pointer; +} + +.markup:hover .icon { + opacity: 0.64; +} + +.listing { + line-height: 1; +} +.listing pre { + display:block; + padding: 16px; + overflow: auto; +} + +.markup code { + background: transparent; + line-height: 1.5; + width: 100%; +} + +.markup code { + font-weight: 400 !important; + position: relative; + box-shadow: none; + overflow: visible; + word-break: break-word; + flex-wrap: wrap; + align-items: center; + vertical-align: middle; + white-space: pre; +} + +.markup code:before { + display:none; +} + +.listing pre::-webkit-scrollbar,.markup pre::-webkit-scrollbar, .card__text::-webkit-scrollbar, .table__overflow::-webkit-scrollbar{ + width:.4rem; + height:.4rem +} +.listing pre::-webkit-scrollbar-thumb, .markup pre::-webkit-scrollbar-thumb, .card__text::-webkit-scrollbar-thumb, .table__overflow::-webkit-scrollbar-thumb{ + background-color:rgba(0,0,0,.26) +} +.listing pre::-webkit-scrollbar-thumb:hover, .markup pre::-webkit-scrollbar-thumb:hover, .card__text::-webkit-scrollbar-thumb:hover, .table__overflow::-webkit-scrollbar-thumb:hover{ + background-color:#536dfe +} + +table > thead > tr > th { + text-align: left; +} + +.table__overflow code, table__overflow kbd { + white-space: nowrap !important; +} + +.table__overflow { +} + +.no_wrap_table { + white-space: nowrap!important; + overflow: auto; +} + +.no_wrap_cell { + white-space: nowrap!important; + max-width: 100px; +} + +.card__example .card__text { + padding:0; +} + +.card .toolbar__content a.btn { + color: white !important; +} + +.card__text pre code:after, .card__text pre code:before { + content: ""; + letter-spacing: -1px; +} + +.card__title { + padding-bottom: 0px!important; +} + +.card__text.caption { + font-size: inherit !important; + text-align: center; +} + +.card__link-hover { + height: 100%; + width: 100%; + position: absolute; + border: 2px solid transparent; + transition: .2s; +} + +.card__link-hover:hover { + border-color: #3f51b596; +} + +/* Section components styles */ + +.section-def { + margin-bottom: 30px; +} + +.section-def dt{ + font-weight: 900; + margin-bottom: 16px; +} + +.section-def .section-def__card { + top: 24px; + margin-top: -24px; +} + +.section-header{ + letter-spacing:2px; + text-align:left!important; + white-space:nowrap; + overflow:hidden; + -o-text-overflow:ellipsis; + text-overflow:ellipsis; + position:relative; + padding-bottom:.5rem; + display:-webkit-box; + display:-ms-flexbox; + display:flex; + -webkit-box-align:center; + -ms-flex-align:center; + align-items:center +} + +.section-header:after{ + content:""; + position:absolute; + left:0; + bottom:0; + width:100px; + height:3px +} + +.pillars .flex{ + display:-webkit-box; + display:-ms-flexbox; + display:flex; + -webkit-box-flex:1; + -ms-flex:1 1 auto; + flex:1 1 auto; + -webkit-box-orient:vertical; + -webkit-box-direction:normal; + -ms-flex-direction:column; + flex-direction:column +} + +.pillars .flex .card{ + -webkit-box-flex:1; + -ms-flex:1 1 auto; + flex:1 1 auto +} + +.xxx-large { + font-size: 6rem; +} + +/* callouts */ +pre .conum[data-value] { + position: relative; + top: -.125em; +} + +/* callout */ + +ul.colist { + list-style-type: none; + margin-bottom: 20px; +} +ul.colist >li[data-value]{ + padding-left:28px; +} +ul.colist >li[data-value]:before{ + margin:2px; + margin-left:-28px; + margin-right: 8px; + color: #fff; + background-color: rgba(0,0,0,.87); + font-weight: bold; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + border-radius: 50%; + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + font-size: 12px; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + height: 20px; + min-width: 20px; + width: 20px; + -webkit-transition: .3s cubic-bezier(.25,.8,.25,1); + -o-transition: .3s cubic-bezier(.25,.8,.25,1); + transition: .3s cubic-bezier(.25,.8,.25,1); + content:attr(data-value); +} + +.conum[data-value] { + margin:2px; + color: #fff; + background-color: rgba(0,0,0,.87); + font-weight: bold; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + border-radius: 50%; + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + font-size: 12px; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + height: 20px; + margin-right: 8px; + min-width: 20px; + width: 20px; + -webkit-transition: .3s cubic-bezier(.25,.8,.25,1); + -o-transition: .3s cubic-bezier(.25,.8,.25,1); + transition: .3s cubic-bezier(.25,.8,.25,1); +} +.conum[data-value]:after { + content: attr(data-value); +} + +/** admonitions **/ + +.admonition { + box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2); + position:relative; + margin:1.5625em 0; + padding:0 1.2rem; + border-left:.4rem solid #448aff; + border-radius:.2rem; + font-size:1.1rem; + overflow:auto +} +html .admonition>:last-child{ + margin-bottom:1.2rem +} +.admonition .admonition { + margin:1em 0 +} +.admonition>.admonition-title { + font-style: italic; + font-weight: 500; +} +.admonition>.admonition-textlabel { + margin:0 -1.2rem; + padding:.8rem 1.2rem .8rem 4rem; + border-bottom:.1rem solid rgba(68,138,255,.1); + background-color:rgba(68,138,255,.1); + font-weight:500 +} +.admonition>.admonition-inline { + padding-left:2.8rem; +} +.admonition>.admonition-textlabel:last-child{ + margin-bottom:0 +} +.admonition>.admonition-textlabel:before, .admonition>.admonition-inline:before{ + position:absolute; + left:1.2rem; + color:#448aff; + font-size:2rem; + content:"\E3C9"; + font-family:Material Icons; + font-style:normal; + font-variant:normal; + font-weight:400; + line-height:1; + text-transform:none; + white-space:nowrap; + speak:none; + word-wrap:normal; + direction:ltr +} +.admonition.note { + border-left-color:#00b8d4 +} +.admonition.note>.admonition-textlabel { + border-bottom-color:.1rem solid rgba(0,184,212,.1); + background-color:rgba(0,184,212,.1) +} +.admonition.note>.admonition-textlabel:before, .admonition.note>.admonition-inline:before{ + color:#00b8d4; + content:"\E88E" +} +.admonition.tip { + border-left-color:#00bfa5 +} +.admonition.tip>.admonition-textlabel{ + border-bottom-color:.1rem solid rgba(0,191,165,.1); + background-color:rgba(0,191,165,.1) +} +.admonition.tip>.admonition-textlabel:before, .admonition.tip>.admonition-inline:before{ + color:#00bfa5; + content:"\E90F" +} +.admonition.warning{ + border-left-color:#ff9100; + background-color:transparent!important; +} +.admonition.warning>.admonition-textlabel { + border-bottom-color:.1rem solid rgba(255,145,0,.1); + background-color:rgba(255,145,0,.1) +} +.admonition.warning>.admonition-textlabel:before, .admonition.warning>.admonition-inline:before{ + color:#ff9100; + content:"\E002" +} +.admonition.important { + border-left-color:#ff5252 +} + +.admonition.important>.admonition-textlabel{ + border-bottom-color:.1rem solid rgba(255,82,82,.1); + background-color:rgba(255,82,82,.1) +} +.admonition.important>.admonition-textlabel:before,.admonition.important>.admonition-inline:before { + color:#ff5252; + content:"\E000" +} +.admonition.caution{ + border-left-color:#ff1744 +} +.admonition.caution>.admonition-textlabel{ + border-bottom-color:.1rem solid rgba(255,23,68,.1); + background-color:rgba(255,23,68,.1) +} +.admonition.caution>.admonition-textlabel:before, .admonition.caution>.admonition-inline:before{ + color:#ff1744; + content:"\E80E" +} diff --git a/14.1.1-0-20/docs/docs/images/GitHub-Mark-64px.png b/14.1.1-0-20/docs/docs/images/GitHub-Mark-64px.png new file mode 100644 index 0000000000000..182a1a3f734fc Binary files /dev/null and b/14.1.1-0-20/docs/docs/images/GitHub-Mark-64px.png differ diff --git a/14.1.1-0-20/docs/docs/images/logo.png b/14.1.1-0-20/docs/docs/images/logo.png new file mode 100644 index 0000000000000..53a78fa23fa80 Binary files /dev/null and b/14.1.1-0-20/docs/docs/images/logo.png differ diff --git a/14.1.1-0-20/docs/index.html b/14.1.1-0-20/docs/index.html new file mode 100644 index 0000000000000..9df91f6763bb8 --- /dev/null +++ b/14.1.1-0-20/docs/index.html @@ -0,0 +1,65 @@ + + + + + + + + + Overview + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/14.1.1-0-20/docs/libs/superagent.js b/14.1.1-0-20/docs/libs/superagent.js new file mode 100644 index 0000000000000..6b3ca9731099f --- /dev/null +++ b/14.1.1-0-20/docs/libs/superagent.js @@ -0,0 +1,2073 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.superagent = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 500. + * + * @param {Number} count + * @param {Function} [fn] + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.retry = function retry(count, fn){ + // Default to 1 if no count passed or true + if (arguments.length === 0 || count === true) count = 1; + if (count <= 0) count = 0; + this._maxRetries = count; + this._retries = 0; + this._retryCallback = fn; + return this; +}; + +var ERROR_CODES = [ + 'ECONNRESET', + 'ETIMEDOUT', + 'EADDRINFO', + 'ESOCKETTIMEDOUT' +]; + +/** + * Determine if a request should be retried. + * (Borrowed from segmentio/superagent-retry) + * + * @param {Error} err + * @param {Response} [res] + * @returns {Boolean} + */ +RequestBase.prototype._shouldRetry = function(err, res) { + if (!this._maxRetries || this._retries++ >= this._maxRetries) { + return false; + } + if (this._retryCallback) { + try { + var override = this._retryCallback(err, res); + if (override === true) return true; + if (override === false) return false; + // undefined falls back to defaults + } catch(e) { + console.error(e); + } + } + if (res && res.status && res.status >= 500 && res.status != 501) return true; + if (err) { + if (err.code && ~ERROR_CODES.indexOf(err.code)) return true; + // Superagent timeout + if (err.timeout && err.code == 'ECONNABORTED') return true; + if (err.crossDomain) return true; + } + return false; +}; + +/** + * Retry request + * + * @return {Request} for chaining + * @api private + */ + +RequestBase.prototype._retry = function() { + + this.clearTimeout(); + + // node + if (this.req) { + this.req = null; + this.req = this.request(); + } + + this._aborted = false; + this.timedout = false; + + return this._end(); +}; + +/** + * Promise support + * + * @param {Function} resolve + * @param {Function} [reject] + * @return {Request} + */ + +RequestBase.prototype.then = function then(resolve, reject) { + if (!this._fullfilledPromise) { + var self = this; + if (this._endCalled) { + console.warn("Warning: superagent request was sent twice, because both .end() and .then() were called. Never call .end() if you use promises"); + } + this._fullfilledPromise = new Promise(function(innerResolve, innerReject) { + self.end(function(err, res) { + if (err) innerReject(err); + else innerResolve(res); + }); + }); + } + return this._fullfilledPromise.then(resolve, reject); +}; + +RequestBase.prototype.catch = function(cb) { + return this.then(undefined, cb); +}; + +/** + * Allow for extension + */ + +RequestBase.prototype.use = function use(fn) { + fn(this); + return this; +}; + +RequestBase.prototype.ok = function(cb) { + if ('function' !== typeof cb) throw Error("Callback required"); + this._okCallback = cb; + return this; +}; + +RequestBase.prototype._isResponseOK = function(res) { + if (!res) { + return false; + } + + if (this._okCallback) { + return this._okCallback(res); + } + + return res.status >= 200 && res.status < 300; +}; + +/** + * Get request header `field`. + * Case-insensitive. + * + * @param {String} field + * @return {String} + * @api public + */ + +RequestBase.prototype.get = function(field){ + return this._header[field.toLowerCase()]; +}; + +/** + * Get case-insensitive header `field` value. + * This is a deprecated internal API. Use `.get(field)` instead. + * + * (getHeader is no longer used internally by the superagent code base) + * + * @param {String} field + * @return {String} + * @api private + * @deprecated + */ + +RequestBase.prototype.getHeader = RequestBase.prototype.get; + +/** + * Set header `field` to `val`, or multiple fields with one object. + * Case-insensitive. + * + * Examples: + * + * req.get('/') + * .set('Accept', 'application/json') + * .set('X-API-Key', 'foobar') + * .end(callback); + * + * req.get('/') + * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' }) + * .end(callback); + * + * @param {String|Object} field + * @param {String} val + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.set = function(field, val){ + if (isObject(field)) { + for (var key in field) { + this.set(key, field[key]); + } + return this; + } + this._header[field.toLowerCase()] = val; + this.header[field] = val; + return this; +}; + +/** + * Remove header `field`. + * Case-insensitive. + * + * Example: + * + * req.get('/') + * .unset('User-Agent') + * .end(callback); + * + * @param {String} field + */ +RequestBase.prototype.unset = function(field){ + delete this._header[field.toLowerCase()]; + delete this.header[field]; + return this; +}; + +/** + * Write the field `name` and `val`, or multiple fields with one object + * for "multipart/form-data" request bodies. + * + * ``` js + * request.post('/upload') + * .field('foo', 'bar') + * .end(callback); + * + * request.post('/upload') + * .field({ foo: 'bar', baz: 'qux' }) + * .end(callback); + * ``` + * + * @param {String|Object} name + * @param {String|Blob|File|Buffer|fs.ReadStream} val + * @return {Request} for chaining + * @api public + */ +RequestBase.prototype.field = function(name, val) { + // name should be either a string or an object. + if (null === name || undefined === name) { + throw new Error('.field(name, val) name can not be empty'); + } + + if (this._data) { + console.error(".field() can't be used if .send() is used. Please use only .send() or only .field() & .attach()"); + } + + if (isObject(name)) { + for (var key in name) { + this.field(key, name[key]); + } + return this; + } + + if (Array.isArray(val)) { + for (var i in val) { + this.field(name, val[i]); + } + return this; + } + + // val should be defined now + if (null === val || undefined === val) { + throw new Error('.field(name, val) val can not be empty'); + } + if ('boolean' === typeof val) { + val = '' + val; + } + this._getFormData().append(name, val); + return this; +}; + +/** + * Abort the request, and clear potential timeout. + * + * @return {Request} + * @api public + */ +RequestBase.prototype.abort = function(){ + if (this._aborted) { + return this; + } + this._aborted = true; + this.xhr && this.xhr.abort(); // browser + this.req && this.req.abort(); // node + this.clearTimeout(); + this.emit('abort'); + return this; +}; + +RequestBase.prototype._auth = function(user, pass, options, base64Encoder) { + switch (options.type) { + case 'basic': + this.set('Authorization', 'Basic ' + base64Encoder(user + ':' + pass)); + break; + + case 'auto': + this.username = user; + this.password = pass; + break; + + case 'bearer': // usage would be .auth(accessToken, { type: 'bearer' }) + this.set('Authorization', 'Bearer ' + user); + break; + } + return this; +}; + +/** + * Enable transmission of cookies with x-domain requests. + * + * Note that for this to work the origin must not be + * using "Access-Control-Allow-Origin" with a wildcard, + * and also must set "Access-Control-Allow-Credentials" + * to "true". + * + * @api public + */ + +RequestBase.prototype.withCredentials = function(on) { + // This is browser-only functionality. Node side is no-op. + if (on == undefined) on = true; + this._withCredentials = on; + return this; +}; + +/** + * Set the max redirects to `n`. Does noting in browser XHR implementation. + * + * @param {Number} n + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.redirects = function(n){ + this._maxRedirects = n; + return this; +}; + +/** + * Maximum size of buffered response body, in bytes. Counts uncompressed size. + * Default 200MB. + * + * @param {Number} n + * @return {Request} for chaining + */ +RequestBase.prototype.maxResponseSize = function(n){ + if ('number' !== typeof n) { + throw TypeError("Invalid argument"); + } + this._maxResponseSize = n; + return this; +}; + +/** + * Convert to a plain javascript object (not JSON string) of scalar properties. + * Note as this method is designed to return a useful non-this value, + * it cannot be chained. + * + * @return {Object} describing method, url, and data of this request + * @api public + */ + +RequestBase.prototype.toJSON = function() { + return { + method: this.method, + url: this.url, + data: this._data, + headers: this._header, + }; +}; + +/** + * Send `data` as the request body, defaulting the `.type()` to "json" when + * an object is given. + * + * Examples: + * + * // manual json + * request.post('/user') + * .type('json') + * .send('{"name":"tj"}') + * .end(callback) + * + * // auto json + * request.post('/user') + * .send({ name: 'tj' }) + * .end(callback) + * + * // manual x-www-form-urlencoded + * request.post('/user') + * .type('form') + * .send('name=tj') + * .end(callback) + * + * // auto x-www-form-urlencoded + * request.post('/user') + * .type('form') + * .send({ name: 'tj' }) + * .end(callback) + * + * // defaults to x-www-form-urlencoded + * request.post('/user') + * .send('name=tobi') + * .send('species=ferret') + * .end(callback) + * + * @param {String|Object} data + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.send = function(data){ + var isObj = isObject(data); + var type = this._header['content-type']; + + if (this._formData) { + console.error(".send() can't be used if .attach() or .field() is used. Please use only .send() or only .field() & .attach()"); + } + + if (isObj && !this._data) { + if (Array.isArray(data)) { + this._data = []; + } else if (!this._isHost(data)) { + this._data = {}; + } + } else if (data && this._data && this._isHost(this._data)) { + throw Error("Can't merge these send calls"); + } + + // merge + if (isObj && isObject(this._data)) { + for (var key in data) { + this._data[key] = data[key]; + } + } else if ('string' == typeof data) { + // default to x-www-form-urlencoded + if (!type) this.type('form'); + type = this._header['content-type']; + if ('application/x-www-form-urlencoded' == type) { + this._data = this._data + ? this._data + '&' + data + : data; + } else { + this._data = (this._data || '') + data; + } + } else { + this._data = data; + } + + if (!isObj || this._isHost(data)) { + return this; + } + + // default to json + if (!type) this.type('json'); + return this; +}; + +/** + * Sort `querystring` by the sort function + * + * + * Examples: + * + * // default order + * request.get('/user') + * .query('name=Nick') + * .query('search=Manny') + * .sortQuery() + * .end(callback) + * + * // customized sort function + * request.get('/user') + * .query('name=Nick') + * .query('search=Manny') + * .sortQuery(function(a, b){ + * return a.length - b.length; + * }) + * .end(callback) + * + * + * @param {Function} sort + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.sortQuery = function(sort) { + // _sort default to true but otherwise can be a function or boolean + this._sort = typeof sort === 'undefined' ? true : sort; + return this; +}; + +/** + * Compose querystring to append to req.url + * + * @api private + */ +RequestBase.prototype._finalizeQueryString = function(){ + var query = this._query.join('&'); + if (query) { + this.url += (this.url.indexOf('?') >= 0 ? '&' : '?') + query; + } + this._query.length = 0; // Makes the call idempotent + + if (this._sort) { + var index = this.url.indexOf('?'); + if (index >= 0) { + var queryArr = this.url.substring(index + 1).split('&'); + if ('function' === typeof this._sort) { + queryArr.sort(this._sort); + } else { + queryArr.sort(); + } + this.url = this.url.substring(0, index) + '?' + queryArr.join('&'); + } + } +}; + +// For backwards compat only +RequestBase.prototype._appendQueryString = function() {console.trace("Unsupported");} + +/** + * Invoke callback with timeout error. + * + * @api private + */ + +RequestBase.prototype._timeoutError = function(reason, timeout, errno){ + if (this._aborted) { + return; + } + var err = new Error(reason + timeout + 'ms exceeded'); + err.timeout = timeout; + err.code = 'ECONNABORTED'; + err.errno = errno; + this.timedout = true; + this.abort(); + this.callback(err); +}; + +RequestBase.prototype._setTimeouts = function() { + var self = this; + + // deadline + if (this._timeout && !this._timer) { + this._timer = setTimeout(function(){ + self._timeoutError('Timeout of ', self._timeout, 'ETIME'); + }, this._timeout); + } + // response timeout + if (this._responseTimeout && !this._responseTimeoutTimer) { + this._responseTimeoutTimer = setTimeout(function(){ + self._timeoutError('Response timeout of ', self._responseTimeout, 'ETIMEDOUT'); + }, this._responseTimeout); + } +}; + +},{"./is-object":2}],4:[function(require,module,exports){ +'use strict'; + +/** + * Module dependencies. + */ + +var utils = require('./utils'); + +/** + * Expose `ResponseBase`. + */ + +module.exports = ResponseBase; + +/** + * Initialize a new `ResponseBase`. + * + * @api public + */ + +function ResponseBase(obj) { + if (obj) return mixin(obj); +} + +/** + * Mixin the prototype properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +function mixin(obj) { + for (var key in ResponseBase.prototype) { + obj[key] = ResponseBase.prototype[key]; + } + return obj; +} + +/** + * Get case-insensitive `field` value. + * + * @param {String} field + * @return {String} + * @api public + */ + +ResponseBase.prototype.get = function(field) { + return this.header[field.toLowerCase()]; +}; + +/** + * Set header related properties: + * + * - `.type` the content type without params + * + * A response of "Content-Type: text/plain; charset=utf-8" + * will provide you with a `.type` of "text/plain". + * + * @param {Object} header + * @api private + */ + +ResponseBase.prototype._setHeaderProperties = function(header){ + // TODO: moar! + // TODO: make this a util + + // content-type + var ct = header['content-type'] || ''; + this.type = utils.type(ct); + + // params + var params = utils.params(ct); + for (var key in params) this[key] = params[key]; + + this.links = {}; + + // links + try { + if (header.link) { + this.links = utils.parseLinks(header.link); + } + } catch (err) { + // ignore + } +}; + +/** + * Set flags such as `.ok` based on `status`. + * + * For example a 2xx response will give you a `.ok` of __true__ + * whereas 5xx will be __false__ and `.error` will be __true__. The + * `.clientError` and `.serverError` are also available to be more + * specific, and `.statusType` is the class of error ranging from 1..5 + * sometimes useful for mapping respond colors etc. + * + * "sugar" properties are also defined for common cases. Currently providing: + * + * - .noContent + * - .badRequest + * - .unauthorized + * - .notAcceptable + * - .notFound + * + * @param {Number} status + * @api private + */ + +ResponseBase.prototype._setStatusProperties = function(status){ + var type = status / 100 | 0; + + // status / class + this.status = this.statusCode = status; + this.statusType = type; + + // basics + this.info = 1 == type; + this.ok = 2 == type; + this.redirect = 3 == type; + this.clientError = 4 == type; + this.serverError = 5 == type; + this.error = (4 == type || 5 == type) + ? this.toError() + : false; + + // sugar + this.accepted = 202 == status; + this.noContent = 204 == status; + this.badRequest = 400 == status; + this.unauthorized = 401 == status; + this.notAcceptable = 406 == status; + this.forbidden = 403 == status; + this.notFound = 404 == status; +}; + +},{"./utils":5}],5:[function(require,module,exports){ +'use strict'; + +/** + * Return the mime type for the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.type = function(str){ + return str.split(/ *; */).shift(); +}; + +/** + * Return header field parameters. + * + * @param {String} str + * @return {Object} + * @api private + */ + +exports.params = function(str){ + return str.split(/ *; */).reduce(function(obj, str){ + var parts = str.split(/ *= */); + var key = parts.shift(); + var val = parts.shift(); + + if (key && val) obj[key] = val; + return obj; + }, {}); +}; + +/** + * Parse Link header fields. + * + * @param {String} str + * @return {Object} + * @api private + */ + +exports.parseLinks = function(str){ + return str.split(/ *, */).reduce(function(obj, str){ + var parts = str.split(/ *; */); + var url = parts[0].slice(1, -1); + var rel = parts[1].split(/ *= */)[1].slice(1, -1); + obj[rel] = url; + return obj; + }, {}); +}; + +/** + * Strip content related fields from `header`. + * + * @param {Object} header + * @return {Object} header + * @api private + */ + +exports.cleanHeader = function(header, changesOrigin){ + delete header['content-type']; + delete header['content-length']; + delete header['transfer-encoding']; + delete header['host']; + // secuirty + if (changesOrigin) { + delete header['authorization']; + delete header['cookie']; + } + return header; +}; + +},{}],6:[function(require,module,exports){ + +/** + * Expose `Emitter`. + */ + +if (typeof module !== 'undefined') { + module.exports = Emitter; +} + +/** + * Initialize a new `Emitter`. + * + * @api public + */ + +function Emitter(obj) { + if (obj) return mixin(obj); +}; + +/** + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; + } + return obj; +} + +/** + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + +Emitter.prototype.on = +Emitter.prototype.addEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks['$' + event] = this._callbacks['$' + event] || []) + .push(fn); + return this; +}; + +/** + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + +Emitter.prototype.once = function(event, fn){ + function on() { + this.off(event, on); + fn.apply(this, arguments); + } + + on.fn = fn; + this.on(event, on); + return this; +}; + +/** + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + +Emitter.prototype.off = +Emitter.prototype.removeListener = +Emitter.prototype.removeAllListeners = +Emitter.prototype.removeEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; + } + + // specific event + var callbacks = this._callbacks['$' + event]; + if (!callbacks) return this; + + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks['$' + event]; + return this; + } + + // remove specific handler + var cb; + for (var i = 0; i < callbacks.length; i++) { + cb = callbacks[i]; + if (cb === fn || cb.fn === fn) { + callbacks.splice(i, 1); + break; + } + } + return this; +}; + +/** + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} + */ + +Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1) + , callbacks = this._callbacks['$' + event]; + + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); + } + } + + return this; +}; + +/** + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public + */ + +Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks['$' + event] || []; +}; + +/** + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public + */ + +Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; +}; + +},{}],7:[function(require,module,exports){ +/** + * Root reference for iframes. + */ + +var root; +if (typeof window !== 'undefined') { // Browser window + root = window; +} else if (typeof self !== 'undefined') { // Web Worker + root = self; +} else { // Other environments + console.warn("Using browser-only version of superagent in non-browser environment"); + root = this; +} + +var Emitter = require('component-emitter'); +var RequestBase = require('./request-base'); +var isObject = require('./is-object'); +var ResponseBase = require('./response-base'); +var Agent = require('./agent-base'); + +/** + * Noop. + */ + +function noop(){}; + +/** + * Expose `request`. + */ + +var request = exports = module.exports = function(method, url) { + // callback + if ('function' == typeof url) { + return new exports.Request('GET', method).end(url); + } + + // url first + if (1 == arguments.length) { + return new exports.Request('GET', method); + } + + return new exports.Request(method, url); +} + +exports.Request = Request; + +/** + * Determine XHR. + */ + +request.getXHR = function () { + if (root.XMLHttpRequest + && (!root.location || 'file:' != root.location.protocol + || !root.ActiveXObject)) { + return new XMLHttpRequest; + } else { + try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {} + try { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch(e) {} + try { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch(e) {} + try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {} + } + throw Error("Browser-only version of superagent could not find XHR"); +}; + +/** + * Removes leading and trailing whitespace, added to support IE. + * + * @param {String} s + * @return {String} + * @api private + */ + +var trim = ''.trim + ? function(s) { return s.trim(); } + : function(s) { return s.replace(/(^\s*|\s*$)/g, ''); }; + +/** + * Serialize the given `obj`. + * + * @param {Object} obj + * @return {String} + * @api private + */ + +function serialize(obj) { + if (!isObject(obj)) return obj; + var pairs = []; + for (var key in obj) { + pushEncodedKeyValuePair(pairs, key, obj[key]); + } + return pairs.join('&'); +} + +/** + * Helps 'serialize' with serializing arrays. + * Mutates the pairs array. + * + * @param {Array} pairs + * @param {String} key + * @param {Mixed} val + */ + +function pushEncodedKeyValuePair(pairs, key, val) { + if (val != null) { + if (Array.isArray(val)) { + val.forEach(function(v) { + pushEncodedKeyValuePair(pairs, key, v); + }); + } else if (isObject(val)) { + for(var subkey in val) { + pushEncodedKeyValuePair(pairs, key + '[' + subkey + ']', val[subkey]); + } + } else { + pairs.push(encodeURIComponent(key) + + '=' + encodeURIComponent(val)); + } + } else if (val === null) { + pairs.push(encodeURIComponent(key)); + } +} + +/** + * Expose serialization method. + */ + +request.serializeObject = serialize; + +/** + * Parse the given x-www-form-urlencoded `str`. + * + * @param {String} str + * @return {Object} + * @api private + */ + +function parseString(str) { + var obj = {}; + var pairs = str.split('&'); + var pair; + var pos; + + for (var i = 0, len = pairs.length; i < len; ++i) { + pair = pairs[i]; + pos = pair.indexOf('='); + if (pos == -1) { + obj[decodeURIComponent(pair)] = ''; + } else { + obj[decodeURIComponent(pair.slice(0, pos))] = + decodeURIComponent(pair.slice(pos + 1)); + } + } + + return obj; +} + +/** + * Expose parser. + */ + +request.parseString = parseString; + +/** + * Default MIME type map. + * + * superagent.types.xml = 'application/xml'; + * + */ + +request.types = { + html: 'text/html', + json: 'application/json', + xml: 'text/xml', + urlencoded: 'application/x-www-form-urlencoded', + 'form': 'application/x-www-form-urlencoded', + 'form-data': 'application/x-www-form-urlencoded' +}; + +/** + * Default serialization map. + * + * superagent.serialize['application/xml'] = function(obj){ + * return 'generated xml here'; + * }; + * + */ + +request.serialize = { + 'application/x-www-form-urlencoded': serialize, + 'application/json': JSON.stringify, +}; + +/** + * Default parsers. + * + * superagent.parse['application/xml'] = function(str){ + * return { object parsed from str }; + * }; + * + */ + +request.parse = { + 'application/x-www-form-urlencoded': parseString, + 'application/json': JSON.parse, +}; + +/** + * Parse the given header `str` into + * an object containing the mapped fields. + * + * @param {String} str + * @return {Object} + * @api private + */ + +function parseHeader(str) { + var lines = str.split(/\r?\n/); + var fields = {}; + var index; + var line; + var field; + var val; + + for (var i = 0, len = lines.length; i < len; ++i) { + line = lines[i]; + index = line.indexOf(':'); + if (index === -1) { // could be empty line, just skip it + continue; + } + field = line.slice(0, index).toLowerCase(); + val = trim(line.slice(index + 1)); + fields[field] = val; + } + + return fields; +} + +/** + * Check if `mime` is json or has +json structured syntax suffix. + * + * @param {String} mime + * @return {Boolean} + * @api private + */ + +function isJSON(mime) { + // should match /json or +json + // but not /json-seq + return /[\/+]json($|[^-\w])/.test(mime); +} + +/** + * Initialize a new `Response` with the given `xhr`. + * + * - set flags (.ok, .error, etc) + * - parse header + * + * Examples: + * + * Aliasing `superagent` as `request` is nice: + * + * request = superagent; + * + * We can use the promise-like API, or pass callbacks: + * + * request.get('/').end(function(res){}); + * request.get('/', function(res){}); + * + * Sending data can be chained: + * + * request + * .post('/user') + * .send({ name: 'tj' }) + * .end(function(res){}); + * + * Or passed to `.send()`: + * + * request + * .post('/user') + * .send({ name: 'tj' }, function(res){}); + * + * Or passed to `.post()`: + * + * request + * .post('/user', { name: 'tj' }) + * .end(function(res){}); + * + * Or further reduced to a single call for simple cases: + * + * request + * .post('/user', { name: 'tj' }, function(res){}); + * + * @param {XMLHTTPRequest} xhr + * @param {Object} options + * @api private + */ + +function Response(req) { + this.req = req; + this.xhr = this.req.xhr; + // responseText is accessible only if responseType is '' or 'text' and on older browsers + this.text = ((this.req.method !='HEAD' && (this.xhr.responseType === '' || this.xhr.responseType === 'text')) || typeof this.xhr.responseType === 'undefined') + ? this.xhr.responseText + : null; + this.statusText = this.req.xhr.statusText; + var status = this.xhr.status; + // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request + if (status === 1223) { + status = 204; + } + this._setStatusProperties(status); + this.header = this.headers = parseHeader(this.xhr.getAllResponseHeaders()); + // getAllResponseHeaders sometimes falsely returns "" for CORS requests, but + // getResponseHeader still works. so we get content-type even if getting + // other headers fails. + this.header['content-type'] = this.xhr.getResponseHeader('content-type'); + this._setHeaderProperties(this.header); + + if (null === this.text && req._responseType) { + this.body = this.xhr.response; + } else { + this.body = this.req.method != 'HEAD' + ? this._parseBody(this.text ? this.text : this.xhr.response) + : null; + } +} + +ResponseBase(Response.prototype); + +/** + * Parse the given body `str`. + * + * Used for auto-parsing of bodies. Parsers + * are defined on the `superagent.parse` object. + * + * @param {String} str + * @return {Mixed} + * @api private + */ + +Response.prototype._parseBody = function(str) { + var parse = request.parse[this.type]; + if (this.req._parser) { + return this.req._parser(this, str); + } + if (!parse && isJSON(this.type)) { + parse = request.parse['application/json']; + } + return parse && str && (str.length || str instanceof Object) + ? parse(str) + : null; +}; + +/** + * Return an `Error` representative of this response. + * + * @return {Error} + * @api public + */ + +Response.prototype.toError = function(){ + var req = this.req; + var method = req.method; + var url = req.url; + + var msg = 'cannot ' + method + ' ' + url + ' (' + this.status + ')'; + var err = new Error(msg); + err.status = this.status; + err.method = method; + err.url = url; + + return err; +}; + +/** + * Expose `Response`. + */ + +request.Response = Response; + +/** + * Initialize a new `Request` with the given `method` and `url`. + * + * @param {String} method + * @param {String} url + * @api public + */ + +function Request(method, url) { + var self = this; + this._query = this._query || []; + this.method = method; + this.url = url; + this.header = {}; // preserves header name case + this._header = {}; // coerces header names to lowercase + this.on('end', function(){ + var err = null; + var res = null; + + try { + res = new Response(self); + } catch(e) { + err = new Error('Parser is unable to parse the response'); + err.parse = true; + err.original = e; + // issue #675: return the raw response if the response parsing fails + if (self.xhr) { + // ie9 doesn't have 'response' property + err.rawResponse = typeof self.xhr.responseType == 'undefined' ? self.xhr.responseText : self.xhr.response; + // issue #876: return the http status code if the response parsing fails + err.status = self.xhr.status ? self.xhr.status : null; + err.statusCode = err.status; // backwards-compat only + } else { + err.rawResponse = null; + err.status = null; + } + + return self.callback(err); + } + + self.emit('response', res); + + var new_err; + try { + if (!self._isResponseOK(res)) { + new_err = new Error(res.statusText || 'Unsuccessful HTTP response'); + } + } catch(custom_err) { + new_err = custom_err; // ok() callback can throw + } + + // #1000 don't catch errors from the callback to avoid double calling it + if (new_err) { + new_err.original = err; + new_err.response = res; + new_err.status = res.status; + self.callback(new_err, res); + } else { + self.callback(null, res); + } + }); +} + +/** + * Mixin `Emitter` and `RequestBase`. + */ + +Emitter(Request.prototype); +RequestBase(Request.prototype); + +/** + * Set Content-Type to `type`, mapping values from `request.types`. + * + * Examples: + * + * superagent.types.xml = 'application/xml'; + * + * request.post('/') + * .type('xml') + * .send(xmlstring) + * .end(callback); + * + * request.post('/') + * .type('application/xml') + * .send(xmlstring) + * .end(callback); + * + * @param {String} type + * @return {Request} for chaining + * @api public + */ + +Request.prototype.type = function(type){ + this.set('Content-Type', request.types[type] || type); + return this; +}; + +/** + * Set Accept to `type`, mapping values from `request.types`. + * + * Examples: + * + * superagent.types.json = 'application/json'; + * + * request.get('/agent') + * .accept('json') + * .end(callback); + * + * request.get('/agent') + * .accept('application/json') + * .end(callback); + * + * @param {String} accept + * @return {Request} for chaining + * @api public + */ + +Request.prototype.accept = function(type){ + this.set('Accept', request.types[type] || type); + return this; +}; + +/** + * Set Authorization field value with `user` and `pass`. + * + * @param {String} user + * @param {String} [pass] optional in case of using 'bearer' as type + * @param {Object} options with 'type' property 'auto', 'basic' or 'bearer' (default 'basic') + * @return {Request} for chaining + * @api public + */ + +Request.prototype.auth = function(user, pass, options){ + if (1 === arguments.length) pass = ''; + if (typeof pass === 'object' && pass !== null) { // pass is optional and can be replaced with options + options = pass; + pass = ''; + } + if (!options) { + options = { + type: 'function' === typeof btoa ? 'basic' : 'auto', + }; + } + + var encoder = function(string) { + if ('function' === typeof btoa) { + return btoa(string); + } + throw new Error('Cannot use basic auth, btoa is not a function'); + }; + + return this._auth(user, pass, options, encoder); +}; + +/** + * Add query-string `val`. + * + * Examples: + * + * request.get('/shoes') + * .query('size=10') + * .query({ color: 'blue' }) + * + * @param {Object|String} val + * @return {Request} for chaining + * @api public + */ + +Request.prototype.query = function(val){ + if ('string' != typeof val) val = serialize(val); + if (val) this._query.push(val); + return this; +}; + +/** + * Queue the given `file` as an attachment to the specified `field`, + * with optional `options` (or filename). + * + * ``` js + * request.post('/upload') + * .attach('content', new Blob(['hey!'], { type: "text/html"})) + * .end(callback); + * ``` + * + * @param {String} field + * @param {Blob|File} file + * @param {String|Object} options + * @return {Request} for chaining + * @api public + */ + +Request.prototype.attach = function(field, file, options){ + if (file) { + if (this._data) { + throw Error("superagent can't mix .send() and .attach()"); + } + + this._getFormData().append(field, file, options || file.name); + } + return this; +}; + +Request.prototype._getFormData = function(){ + if (!this._formData) { + this._formData = new root.FormData(); + } + return this._formData; +}; + +/** + * Invoke the callback with `err` and `res` + * and handle arity check. + * + * @param {Error} err + * @param {Response} res + * @api private + */ + +Request.prototype.callback = function(err, res){ + if (this._shouldRetry(err, res)) { + return this._retry(); + } + + var fn = this._callback; + this.clearTimeout(); + + if (err) { + if (this._maxRetries) err.retries = this._retries - 1; + this.emit('error', err); + } + + fn(err, res); +}; + +/** + * Invoke callback with x-domain error. + * + * @api private + */ + +Request.prototype.crossDomainError = function(){ + var err = new Error('Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.'); + err.crossDomain = true; + + err.status = this.status; + err.method = this.method; + err.url = this.url; + + this.callback(err); +}; + +// This only warns, because the request is still likely to work +Request.prototype.buffer = Request.prototype.ca = Request.prototype.agent = function(){ + console.warn("This is not supported in browser version of superagent"); + return this; +}; + +// This throws, because it can't send/receive data as expected +Request.prototype.pipe = Request.prototype.write = function(){ + throw Error("Streaming is not supported in browser version of superagent"); +}; + +/** + * Check if `obj` is a host object, + * we don't want to serialize these :) + * + * @param {Object} obj + * @return {Boolean} + * @api private + */ +Request.prototype._isHost = function _isHost(obj) { + // Native objects stringify to [object File], [object Blob], [object FormData], etc. + return obj && 'object' === typeof obj && !Array.isArray(obj) && Object.prototype.toString.call(obj) !== '[object Object]'; +} + +/** + * Initiate request, invoking callback `fn(res)` + * with an instanceof `Response`. + * + * @param {Function} fn + * @return {Request} for chaining + * @api public + */ + +Request.prototype.end = function(fn){ + if (this._endCalled) { + console.warn("Warning: .end() was called twice. This is not supported in superagent"); + } + this._endCalled = true; + + // store callback + this._callback = fn || noop; + + // querystring + this._finalizeQueryString(); + + return this._end(); +}; + +Request.prototype._end = function() { + var self = this; + var xhr = (this.xhr = request.getXHR()); + var data = this._formData || this._data; + + this._setTimeouts(); + + // state change + xhr.onreadystatechange = function(){ + var readyState = xhr.readyState; + if (readyState >= 2 && self._responseTimeoutTimer) { + clearTimeout(self._responseTimeoutTimer); + } + if (4 != readyState) { + return; + } + + // In IE9, reads to any property (e.g. status) off of an aborted XHR will + // result in the error "Could not complete the operation due to error c00c023f" + var status; + try { status = xhr.status } catch(e) { status = 0; } + + if (!status) { + if (self.timedout || self._aborted) return; + return self.crossDomainError(); + } + self.emit('end'); + }; + + // progress + var handleProgress = function(direction, e) { + if (e.total > 0) { + e.percent = e.loaded / e.total * 100; + } + e.direction = direction; + self.emit('progress', e); + }; + if (this.hasListeners('progress')) { + try { + xhr.onprogress = handleProgress.bind(null, 'download'); + if (xhr.upload) { + xhr.upload.onprogress = handleProgress.bind(null, 'upload'); + } + } catch(e) { + // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist. + // Reported here: + // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context + } + } + + // initiate request + try { + if (this.username && this.password) { + xhr.open(this.method, this.url, true, this.username, this.password); + } else { + xhr.open(this.method, this.url, true); + } + } catch (err) { + // see #1149 + return this.callback(err); + } + + // CORS + if (this._withCredentials) xhr.withCredentials = true; + + // body + if (!this._formData && 'GET' != this.method && 'HEAD' != this.method && 'string' != typeof data && !this._isHost(data)) { + // serialize stuff + var contentType = this._header['content-type']; + var serialize = this._serializer || request.serialize[contentType ? contentType.split(';')[0] : '']; + if (!serialize && isJSON(contentType)) { + serialize = request.serialize['application/json']; + } + if (serialize) data = serialize(data); + } + + // set header fields + for (var field in this.header) { + if (null == this.header[field]) continue; + + if (this.header.hasOwnProperty(field)) + xhr.setRequestHeader(field, this.header[field]); + } + + if (this._responseType) { + xhr.responseType = this._responseType; + } + + // send stuff + this.emit('request', this); + + // IE11 xhr.send(undefined) sends 'undefined' string as POST payload (instead of nothing) + // We need null here if data is undefined + xhr.send(typeof data !== 'undefined' ? data : null); + return this; +}; + +request.agent = function() { + return new Agent(); +}; + +["GET", "POST", "OPTIONS", "PATCH", "PUT", "DELETE"].forEach(function(method) { + Agent.prototype[method.toLowerCase()] = function(url, fn) { + var req = new request.Request(method, url); + this._setDefaults(req); + if (fn) { + req.end(fn); + } + return req; + }; +}); + +Agent.prototype.del = Agent.prototype['delete']; + +/** + * GET `url` with optional callback `fn(res)`. + * + * @param {String} url + * @param {Mixed|Function} [data] or fn + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.get = function(url, data, fn) { + var req = request('GET', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.query(data); + if (fn) req.end(fn); + return req; +}; + +/** + * HEAD `url` with optional callback `fn(res)`. + * + * @param {String} url + * @param {Mixed|Function} [data] or fn + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.head = function(url, data, fn) { + var req = request('HEAD', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.query(data); + if (fn) req.end(fn); + return req; +}; + +/** + * OPTIONS query to `url` with optional callback `fn(res)`. + * + * @param {String} url + * @param {Mixed|Function} [data] or fn + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.options = function(url, data, fn) { + var req = request('OPTIONS', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +}; + +/** + * DELETE `url` with optional `data` and callback `fn(res)`. + * + * @param {String} url + * @param {Mixed} [data] + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +function del(url, data, fn) { + var req = request('DELETE', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +} + +request['del'] = del; +request['delete'] = del; + +/** + * PATCH `url` with optional `data` and callback `fn(res)`. + * + * @param {String} url + * @param {Mixed} [data] + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.patch = function(url, data, fn) { + var req = request('PATCH', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +}; + +/** + * POST `url` with optional `data` and callback `fn(res)`. + * + * @param {String} url + * @param {Mixed} [data] + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.post = function(url, data, fn) { + var req = request('POST', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +}; + +/** + * PUT `url` with optional `data` and callback `fn(res)`. + * + * @param {String} url + * @param {Mixed|Function} [data] or fn + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.put = function(url, data, fn) { + var req = request('PUT', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +}; + +},{"./agent-base":1,"./is-object":2,"./request-base":3,"./response-base":4,"component-emitter":6}]},{},[7])(7) +}); diff --git a/14.1.1-0-20/docs/libs/vuex-router-sync.js b/14.1.1-0-20/docs/libs/vuex-router-sync.js new file mode 100644 index 0000000000000..835bb1d79ecd9 --- /dev/null +++ b/14.1.1-0-20/docs/libs/vuex-router-sync.js @@ -0,0 +1,106 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +// vuex-router-sync source code pasted here because no proper cdn service found +function sync(store, router, options) { + var moduleName = (options || {}).moduleName || 'route' + + store.registerModule(moduleName, { + namespaced: true, + state: cloneRoute(router.currentRoute), + mutations: { + 'ROUTE_CHANGED': function (state, transition) { + store.state[moduleName] = cloneRoute(transition.to, transition.from) + } + } + }) + + var isTimeTraveling = false + var currentPath + + // sync router on store change + store.watch( + function (state) { + return state[moduleName] + }, + function (route) { + if (route.fullPath === currentPath) { + return + } + isTimeTraveling = true + var methodToUse = currentPath == null ? + 'replace' : + 'push' + currentPath = route.fullPath + router[methodToUse](route) + }, { + sync: true + } + ) + + // sync store on router navigation + router.afterEach(function (to, from) { + if (isTimeTraveling) { + isTimeTraveling = false + return + } + currentPath = to.fullPath + store.commit(moduleName + '/ROUTE_CHANGED', { + to: to, + from: from + }) + }) +} + +function cloneRoute(to, from) { + var clone = { + name: to.name, + path: to.path, + hash: to.hash, + query: to.query, + params: to.params, + fullPath: to.fullPath, + meta: to.meta + } + if (from) { + clone.from = cloneRoute(from) + } + return Object.freeze(clone) +} diff --git a/14.1.1-0-20/docs/main/app.js b/14.1.1-0-20/docs/main/app.js new file mode 100644 index 0000000000000..5553ebcd713e0 --- /dev/null +++ b/14.1.1-0-20/docs/main/app.js @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * + * 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. + */ + +/* global Vue, releases, Vuex, Vuetify, superagent */ + +const config = createConfig(); +const navItems = createNav(); +const searchIndex = new Promise(function(resolve, reject){ + superagent.get("main/search-index.json").end(function (error, response) { + if(error){ + reject("unable to load search index: " + error); + return; + } + resolve(JSON.parse(response.text)); + }); +}); + +function main() { + + // add components + for (var compKey in window.allComponents) { + comp = window.allComponents[compKey]; + comp.init(); + } + + Vuetify.default(window.Vue, { + theme: config.theme + }); + + Vue.use(Vuex); + const store = new Vuex.Store({ + state: { + isSearching: false, + h1: null, + sidebar: null, + currentColor: 'transparent', + previous: {}, + next: {}, + navGroup: null, + }, + actions: {}, + mutations: { + 'sitegen/ISSEARCHING'(state, payload){ + state.isSearching = payload; + }, + 'vuetify/COLOR'(state, payload) { + state.currentColor = payload; + }, + 'vuetify/PREVIOUS'(state, payload) { + state.previous = payload; + }, + 'vuetify/NEXT'(state, payload) { + state.next = payload; + }, + 'vuetify/SIDEBAR'(state, payload) { + state.sidebar = payload; + }, + 'vuetify/H1'(state, payload) { + state.h1 = payload; + }, + 'vuetify/NAVGROUP'(state, payload) { + state.navGroup = payload; + } + }, + getters: {} + }); + + const router = new VueRouter({ routes: createRoutes() }); + sync(store, router); + + new Vue({ + router, + store + }).$mount("#app"); +} diff --git a/14.1.1-0-20/docs/main/config.js b/14.1.1-0-20/docs/main/config.js new file mode 100644 index 0000000000000..dca787bec0ab4 --- /dev/null +++ b/14.1.1-0-20/docs/main/config.js @@ -0,0 +1,254 @@ +function createConfig() { + return { + home: "docs/about/01_overview", + release: "14.1.1.0.20", + releases: [ + "14.1.1.0.20" + ], + pathColors: { + "*": "blue-grey" + }, + theme: { + primary: '#1976D2', + secondary: '#424242', + accent: '#82B1FF', + error: '#FF5252', + info: '#2196F3', + success: '#4CAF50', + warning: '#FFC107' + }, + navTitle: 'Oracle Coherence CE', + navIcon: null, + navLogo: 'docs/images/logo.png' + }; +} + +function createRoutes(){ + return [ + { + path: '/docs/about/01_overview', + meta: { + h1: 'Overview', + title: 'Overview', + h1Prefix: null, + description: 'Oracle Coherence documentation', + keywords: 'coherence, java, documentation', + customLayout: null, + hasNav: true + }, + component: loadPage('docs-about-01_overview', '/docs/about/01_overview', {}) + }, + { + path: '/docs/about/02_introduction', + meta: { + h1: 'Introduction', + title: 'Introduction', + h1Prefix: null, + description: null, + keywords: null, + customLayout: null, + hasNav: true + }, + component: loadPage('docs-about-02_introduction', '/docs/about/02_introduction', {}) + }, + { + path: '/docs/about/03_quickstart', + meta: { + h1: 'Quick Start', + title: 'Quick Start', + h1Prefix: null, + description: null, + keywords: null, + customLayout: null, + hasNav: true + }, + component: loadPage('docs-about-03_quickstart', '/docs/about/03_quickstart', {}) + }, + { + path: '/coherence-docker/README', + meta: { + h1: 'Coherence OCI Image', + title: 'Coherence OCI Image', + h1Prefix: null, + description: null, + keywords: null, + customLayout: null, + hasNav: true + }, + component: loadPage('coherence-docker-README', '/coherence-docker/README', {}) + }, + { + path: '/coherence-grpc-proxy-client/README', + meta: { + h1: 'Coherence gRPC Client', + title: 'Coherence gRPC Client', + h1Prefix: null, + description: null, + keywords: null, + customLayout: null, + hasNav: false + }, + component: loadPage('coherence-grpc-proxy-client-README', '/coherence-grpc-proxy-client/README', {}) + }, + { + path: '/coherence-cdi/README', + meta: { + h1: 'Coherence CDI', + title: 'Coherence CDI', + h1Prefix: null, + description: null, + keywords: null, + customLayout: null, + hasNav: false + }, + component: loadPage('coherence-cdi-README', '/coherence-cdi/README', {}) + }, + { + path: '/coherence-mp/README', + meta: { + h1: 'Coherence MP', + title: 'Coherence MP', + h1Prefix: null, + description: 'Oracle Coherence documentation', + keywords: 'coherence, java, documentation', + customLayout: null, + hasNav: false + }, + component: loadPage('coherence-mp-README', '/coherence-mp/README', {}) + }, + { + path: '/coherence-mp/metrics/README', + meta: { + h1: 'Coherence MicroProfile Metrics', + title: 'Coherence MicroProfile Metrics', + h1Prefix: null, + description: null, + keywords: null, + customLayout: null, + hasNav: false + }, + component: loadPage('coherence-mp-metrics-README', '/coherence-mp/metrics/README', {}) + }, + { + path: '/coherence-grpc-proxy/README', + meta: { + h1: 'Coherence gRPC Server', + title: 'Coherence gRPC Server', + h1Prefix: null, + description: null, + keywords: null, + customLayout: null, + hasNav: false + }, + component: loadPage('coherence-grpc-proxy-README', '/coherence-grpc-proxy/README', {}) + }, + { + path: '/docs/README', + meta: { + h1: 'Coherence Documentation Module', + title: 'Coherence Documentation Module', + h1Prefix: null, + description: null, + keywords: null, + customLayout: null, + hasNav: false + }, + component: loadPage('docs-README', '/docs/README', {}) + }, + { + path: '/coherence-mp/config/README', + meta: { + h1: 'Coherence MicroProfile Config', + title: 'Coherence MicroProfile Config', + h1Prefix: null, + description: null, + keywords: null, + customLayout: null, + hasNav: false + }, + component: loadPage('coherence-mp-config-README', '/coherence-mp/config/README', {}) + }, + { + path: '/', redirect: '/docs/about/01_overview' + }, + { + path: '*', redirect: '/' + } + ]; +} + +function createNav(){ + return [ + { header: 'Core documentation' }, + { + title: 'About', + action: 'assistant', + group: '/about', + items: [ + { href: '/docs/about/01_overview', title: 'Overview' }, + { href: '/docs/about/02_introduction', title: 'Introduction' }, + { href: '/docs/about/03_quickstart', title: 'Quick Start' } + ] + }, + { + title: 'Coherence OCI Images', + action: 'fa-th', + group: '/coherence-docker', + items: [ + { href: '/coherence-docker/README', title: 'Coherence OCI Image' } + ] + }, + { divider: true }, + { header: 'Documentation' }, + { + title: 'Official Documentation', + action: 'import_contacts', + href: 'https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/index.html', + target: '_blank' + }, + { + title: 'Coherence CE Java API', + action: 'library_books', + href: 'https://oracle.github.io/coherence/14.1.1-0-20/api/java/index.html', + target: '_blank' + }, + { + title: 'Coherence CE .NET Core API', + action: 'library_books', + href: 'https://coherence.community/14.1.1.0/api/dotnet-core/index.html', + target: '_blank' + }, + { + title: 'Coherence CE .NET API', + action: 'library_books', + href: 'https://oracle.github.io/coherence/14.1.1.0/api/dotnet/index.html', + target: '_blank' + }, + { + title: 'Coherence CE C++ API', + action: 'library_books', + href: 'https://oracle.github.io/coherence/14.1.1.0/api/cpp/index.html', + target: '_blank' + }, + { divider: true }, + { header: 'Additional Resources' }, + { + title: 'Slack', + action: 'fa-slack', + href: 'https://join.slack.com/t/oraclecoherence/shared_invite/enQtNzcxNTQwMTAzNjE4LTJkZWI5ZDkzNGEzOTllZDgwZDU3NGM2YjY5YWYwMzM3ODdkNTU2NmNmNDFhOWIxMDZlNjg2MzE3NmMxZWMxMWE', + target: '_blank' + }, + { + title: 'Coherence Community', + action: 'people', + href: 'https://oracle.github.io/coherence/index.html', + target: '_blank' + }, + { + title: 'GitHub', + action: 'fa-github-square', + href: 'https://github.com/oracle/coherence', + target: '_blank' + } + ]; +} \ No newline at end of file diff --git a/14.1.1-0-20/docs/main/search-index.json b/14.1.1-0-20/docs/main/search-index.json new file mode 100644 index 0000000000000..4c6ab048f5c03 --- /dev/null +++ b/14.1.1-0-20/docs/main/search-index.json @@ -0,0 +1,254 @@ +{ + "docs": [ + { + "location": "/docs/about/03_quickstart", + "text": " Java - jdk8 or higher Maven - 3.6.3 or higher ", + "title": "Prerequisites" + }, + { + "location": "/docs/about/03_quickstart", + "text": " As Coherence is generally embedded into an application by using Coherence APIs, the natural place to consume this dependency is from Maven: <dependencies> <dependency> <groupId>com.oracle.coherence.ce</groupId> <artifactId>coherence</artifactId> <version>14.1.1-0-1</version> </dependency> </dependencies> You can also get Coherence from the official Docker image . For other language clients, use ( C++ and .NET ), and for the non-community edition, see Oracle Technology Network . ", + "title": "Downloading Coherence Community Edition" + }, + { + "location": "/docs/about/03_quickstart", + "text": " This document details how to quickly get up and running with Coherence. Prerequisites Java - jdk8 or higher Maven - 3.6.3 or higher Downloading Coherence Community Edition As Coherence is generally embedded into an application by using Coherence APIs, the natural place to consume this dependency is from Maven: <dependencies> <dependency> <groupId>com.oracle.coherence.ce</groupId> <artifactId>coherence</artifactId> <version>14.1.1-0-1</version> </dependency> </dependencies> You can also get Coherence from the official Docker image . For other language clients, use ( C++ and .NET ), and for the non-community edition, see Oracle Technology Network . ", + "title": "Quick Start" + }, + { + "location": "/docs/about/03_quickstart", + "text": " To run a CohQL console: $> mvn -DgroupId=com.oracle.coherence.ce -DartifactId=coherence -Dversion=14.1.1-0-1 dependency:get $> export COH_JAR=~/.m2/repository/com/oracle/coherence/ce/coherence/14.1.1-0-1/coherence-14.1.1-0-1.jar $> java -jar $COH_JAR & $> java -cp $COH_JAR com.tangosol.coherence.dslquery.QueryPlus CohQL> select * from welcomes CohQL> insert into welcomes key 'english' value 'Hello' CohQL> insert into welcomes key 'spanish' value 'Hola' CohQL> insert into welcomes key 'french' value 'Bonjour' CohQL> select key(), value() from welcomes Results [\"french\", \"Bonjour\"] [\"english\", \"Hello\"] [\"spanish\", \"Hola\"] CohQL> bye $> java -cp $COH_JAR com.tangosol.coherence.dslquery.QueryPlus CohQL> select key(), value() from welcomes Results [\"french\", \"Bonjour\"] [\"english\", \"Hello\"] [\"spanish\", \"Hola\"] CohQL> bye $> kill %1 ", + "title": " CohQL Console" + }, + { + "location": "/docs/about/03_quickstart", + "text": " To run the Coherence console: $> mvn -DgroupId=com.oracle.coherence.ce -DartifactId=coherence -Dversion=14.1.1-0-1 dependency:get $> export COH_JAR=~/.m2/repository/com/oracle/coherence/ce/coherence/14.1.1-0-1/coherence-14.1.1-0-1.jar $> java -jar $COH_JAR & $> java -cp $COH_JAR com.tangosol.net.CacheFactory Map (?): cache welcomes Map (welcomes): get english null Map (welcomes): put english Hello null Map (welcomes): put spanish Hola null Map (welcomes): put french Bonjour null Map (welcomes): get english Hello Map (welcomes): list french = Bonjour spanish = Hola english = Hello Map (welcomes): bye $> java -cp $COH_JAR com.tangosol.net.CacheFactory Map (?): cache welcomes Map (welcomes): list french = Bonjour spanish = Hola english = Hello Map (welcomes): bye $> kill %1 ", + "title": " Coherence Console" + }, + { + "location": "/docs/about/03_quickstart", + "text": " The following example illustrates the procedure to start a storage enabled Coherence Server, followed by a storage disabled Coherence Console. Using the console, data is inserted, retrieved, and then the console is terminated. The console is restarted and data is once again retrieved to illustrate the permanence of the data. This example uses the out-of-the-box cache configuration and therefore, explicitly specifying that the console is storage disabled is unnecessary. Coherence cluster members discover each other via one of two mechanisms; multicast (default) or Well Known Addressing (deterministic broadcast). If your system does not support multicast, enable WKA by specifying -Dcoherence.wka=localhost for both processes started in the following console examples. CohQL Console To run a CohQL console: $> mvn -DgroupId=com.oracle.coherence.ce -DartifactId=coherence -Dversion=14.1.1-0-1 dependency:get $> export COH_JAR=~/.m2/repository/com/oracle/coherence/ce/coherence/14.1.1-0-1/coherence-14.1.1-0-1.jar $> java -jar $COH_JAR & $> java -cp $COH_JAR com.tangosol.coherence.dslquery.QueryPlus CohQL> select * from welcomes CohQL> insert into welcomes key 'english' value 'Hello' CohQL> insert into welcomes key 'spanish' value 'Hola' CohQL> insert into welcomes key 'french' value 'Bonjour' CohQL> select key(), value() from welcomes Results [\"french\", \"Bonjour\"] [\"english\", \"Hello\"] [\"spanish\", \"Hola\"] CohQL> bye $> java -cp $COH_JAR com.tangosol.coherence.dslquery.QueryPlus CohQL> select key(), value() from welcomes Results [\"french\", \"Bonjour\"] [\"english\", \"Hello\"] [\"spanish\", \"Hola\"] CohQL> bye $> kill %1 Coherence Console To run the Coherence console: $> mvn -DgroupId=com.oracle.coherence.ce -DartifactId=coherence -Dversion=14.1.1-0-1 dependency:get $> export COH_JAR=~/.m2/repository/com/oracle/coherence/ce/coherence/14.1.1-0-1/coherence-14.1.1-0-1.jar $> java -jar $COH_JAR & $> java -cp $COH_JAR com.tangosol.net.CacheFactory Map (?): cache welcomes Map (welcomes): get english null Map (welcomes): put english Hello null Map (welcomes): put spanish Hola null Map (welcomes): put french Bonjour null Map (welcomes): get english Hello Map (welcomes): list french = Bonjour spanish = Hola english = Hello Map (welcomes): bye $> java -cp $COH_JAR com.tangosol.net.CacheFactory Map (?): cache welcomes Map (welcomes): list french = Bonjour spanish = Hola english = Hello Map (welcomes): bye $> kill %1 ", + "title": "CLI Hello Coherence" + }, + { + "location": "/docs/about/03_quickstart", + "text": " Create a maven project either manually or by using an archetype such as maven-archetype-quickstart Add a dependency to the pom file: <dependencies> <dependency> <groupId>com.oracle.coherence.ce</groupId> <artifactId>coherence</artifactId> <version>14.1.1-0-1</version> </dependency> </dependencies> Copy and paste the following source to a file named src/main/java/HelloCoherence.java: import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; public class HelloCoherence { // ----- static methods ------------------------------------------------- public static void main(String[] asArgs) { NamedCache<String, String> cache = CacheFactory.getCache(\"welcomes\"); System.out.printf(\"Accessing cache \\\"%s\\\" containing %d entries\", cache.getCacheName(), cache.size()); cache.put(\"english\", \"Hello\"); cache.put(\"spanish\", \"Hola\"); cache.put(\"french\" , \"Bonjour\"); // list cache.entrySet().forEach(System.out::println); } } Compile the maven project: mvn package Start a Storage server mvn exec:java -Dexec.mainClass=\"com.tangosol.net.DefaultCacheServer\" & Run HelloCoherence mvn exec:java -Dexec.mainClass=\"HelloCoherence\" Confirm that you see the output including the following: Accessing cache \"welcomes\" containing 3 entries ConverterEntry{Key=\"french\", Value=\"Bonjour\"} ConverterEntry{Key=\"spanish\", Value=\"Hola\"} ConverterEntry{Key=\"english\", Value=\"Hello\"} Kill the storage server started earlier: kill %1 ", + "title": "Build HelloCoherence " + }, + { + "location": "/docs/about/03_quickstart", + "text": " The following example illustrates starting a storage enabled Coherence server, followed by running the HelloCoherence application. The HelloCoherence application inserts and retrieves data from the Coherence server. Build HelloCoherence Create a maven project either manually or by using an archetype such as maven-archetype-quickstart Add a dependency to the pom file: <dependencies> <dependency> <groupId>com.oracle.coherence.ce</groupId> <artifactId>coherence</artifactId> <version>14.1.1-0-1</version> </dependency> </dependencies> Copy and paste the following source to a file named src/main/java/HelloCoherence.java: import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; public class HelloCoherence { // ----- static methods ------------------------------------------------- public static void main(String[] asArgs) { NamedCache<String, String> cache = CacheFactory.getCache(\"welcomes\"); System.out.printf(\"Accessing cache \\\"%s\\\" containing %d entries\", cache.getCacheName(), cache.size()); cache.put(\"english\", \"Hello\"); cache.put(\"spanish\", \"Hola\"); cache.put(\"french\" , \"Bonjour\"); // list cache.entrySet().forEach(System.out::println); } } Compile the maven project: mvn package Start a Storage server mvn exec:java -Dexec.mainClass=\"com.tangosol.net.DefaultCacheServer\" & Run HelloCoherence mvn exec:java -Dexec.mainClass=\"HelloCoherence\" Confirm that you see the output including the following: Accessing cache \"welcomes\" containing 3 entries ConverterEntry{Key=\"french\", Value=\"Bonjour\"} ConverterEntry{Key=\"spanish\", Value=\"Hola\"} ConverterEntry{Key=\"english\", Value=\"Hello\"} Kill the storage server started earlier: kill %1 ", + "title": " Programmatic Hello Coherence Example" + }, + { + "location": "/docs/about/03_quickstart", + "text": "$> git clone git@github.com:oracle/coherence.git $> cd coherence/prj # build all modules $> mvn clean install # build all modules skipping tests $> mvn clean install -DskipTests # build a specific module, including all dependent modules and run tests $> mvn -am -pl test/functional/persistence clean verify # build only coherence.jar without running tests $> mvn -am -pl coherence clean install -DskipTests # build only coherence.jar and skip compilation of CDBs and tests $> mvn -am -pl coherence clean install -DskipTests -Dtde.compile.not.required ", + "title": " Building" + }, + { + "location": "/coherence-docker/README", + "text": " The Coherence image uses a distroless base image containing OpenJDK. There are many advantages of a distroless image, security being the main one. Of course, you are free to use whatever base image or build mechanism you want for your own images. The image built by the coherence-docker module contains the following Coherence components: Component Description Coherence The core Coherence server Coherence Extend A Coherence*Extend proxy, exposed on port 20000 Coherence Management Coherence Management over REST, exposed on port 30000 Coherence Metrics Coherence Metrics, exposed on port 9612 Coherence Tracing Coherence tracing is configured to use a Jaeger tracing server. See the Tracing section below. ", + "title": "Image Contents" + }, + { + "location": "/coherence-docker/README", + "text": " Assuming that you have first cloned the Coherence CE project the to build the Coherence image run the following command from the top-level Maven prj/ folder: mvn clean install -P docker -pl coherence-docker The name of the image produced comes from properties in the coherence-docker module pom.xml file. ${docker.registry}/coherence-ce:<version> Where <version> is the version of the product from the pom.xml file. The ${docker.registry} property is the name of the registry that the image will be published to, by default this is oraclecoherence . So, if the version in the pom.xml is 14.1.1-0-20 the image produced will be oraclecoherence/coherence-ce:14.1.1-0-20 To change the registry name the image can be built by specifying the docker.registry property, for example: mvn clean install -P docker -pl coherence-docker -Ddocker.registry=foo The example above would build an image named foo/coherence:14.1.1-0-20 ", + "title": "Building the Image" + }, + { + "location": "/coherence-docker/README", + "text": " This image can be run in Kubernetes using the Coherence Operator . The sections below on additional configurations do not apply when using the Coherence Operator to run the image in Kubernetes. The operator provides functionality to configure the container correctly. ", + "title": "Run the Image in Kubernetes" + }, + { + "location": "/coherence-docker/README", + "text": " Run the image just like any other image. In Docker this command would be: docker run -d -P oraclecoherence/coherence-ce:{project-version} The -P parameter will ensure that the Extend, gRPC, management and metrics ports will all be exposed. Run the Image in Kubernetes This image can be run in Kubernetes using the Coherence Operator . The sections below on additional configurations do not apply when using the Coherence Operator to run the image in Kubernetes. The operator provides functionality to configure the container correctly. ", + "title": "Run the image" + }, + { + "location": "/coherence-docker/README", + "text": " Many options in Coherence can be set from System properties prefiexed with coherence. . The issue here is that System properties are not very easy to pass into the JVM in the container, whereas environment variables are. To help with this the main class which runs in the container will convert any environment variable prefixed with coherence. into a System property before it starts Coherence. docker run -d -P \\ -e coherence.cluster=testing \\ -e coherence.role=storage \\ oraclecoherence/coherence-ce:{project-version} The example above sets two environment variables, coherence.cluster=testing and coherence.role=storage . These will be converted to System properties so Coherence will start the same as it would if the variables had been passed to the JVM command line as -Dcoherence.cluster=testing -Dcoherence.role=storage This only applies to environment variables prefixed with coherence. that have not already set as System properties some other way. ", + "title": "Specifying Coherence System Properties" + }, + { + "location": "/coherence-docker/README", + "text": " Images built with JIB have a fixed entrypoint configured to run the application. This is not very flexible if additional options need to be passed to the JVM. The Coherence image makes use of the JVM’s ability to load options at start-up from a file by using a JVM option @<file-name> . The Coherence image entrypoint contains @/args/jvm-args.txt , so the JVM will load additional options on start-up from a file named /args/jvm-args.txt . This means that additional options can be provided by adding a volume mapping that adds this file to the container. For example, to set the heap to 5g, the Coherence cluster name to test-cluster and role name to storage then additional JVM arguments will be required. Create a file named jvm-args.txt containing these properties: -Xms5g -Xmx5g -Dcoherence.cluster=test-cluster -Dcoherence.role=storage If the file has been created in a local directory named /home/oracle/test-args then the image can be run with the following command: docker run -d -P -v /home/oracle/test-args:/args oraclecoherence/coherence-ce:{project-version} This will cause Docker to mount the local /home/oracle/test-args directory to the /args directory in the container where the JVM will find the jvm-args.txt file. ", + "title": "Specifying JVM Options" + }, + { + "location": "/coherence-docker/README", + "text": " Images built with JIB have a fixed classpath configured, which is not very flexible if additional resources need to be added to the classpath. The Coherence image maps two additional directories to the classpath that are empty in the image and may be used to add items to the classpath by mapping external volumes to these directories. The additional classpath entries are: /coherence/ext/lib/* - this will add all .jar files under the /coherence/ext/lib/ directory to the classpath /coherence/ext/conf - this adds /coherence/ext/conf to the classpath so that any classes, packages or other resource files in this directory will be added to the classpath. For example: On the local Docker host there is a folder called /dev/my-app/lib that contains .jar files to be added to the container classpath. docker run -d -P -v /dev/my-app/lib:/coherence/ext/lib oraclecoherence/coherence-ce:{project-version} The command above maps the local directory /dev/my-app/lib to the /coherence/ext/lib in the container so that any .jar files in the /dev/my-app/lib directory will now be on the Coherence JVM’s classpath. On the local Docker host there is a folder called /dev/my-app/classes that contains .class files and other application resources to be added to the container classpath. docker run -d -P -v /dev/my-app/classes:/coherence/ext/conf oraclecoherence/coherence-ce:{project-version} The command above maps the local directory /dev/my-app/classes to the /coherence/ext/conf in the container so that any classes and resource files in the /dev/my-app/classes directory will now be on the Coherence JVM’s classpath. ", + "title": "Adding to the Classpath" + }, + { + "location": "/coherence-docker/README", + "text": " This module builds an example Coherence OCI compatible image. The image built in this module is a demo and example of how to build a Coherence image using the JIB Maven Plugin . The image is not intended to be used in production deployments or as a base image, it is specifically for demos, experimentation and learning purposes. Image Contents The Coherence image uses a distroless base image containing OpenJDK. There are many advantages of a distroless image, security being the main one. Of course, you are free to use whatever base image or build mechanism you want for your own images. The image built by the coherence-docker module contains the following Coherence components: Component Description Coherence The core Coherence server Coherence Extend A Coherence*Extend proxy, exposed on port 20000 Coherence Management Coherence Management over REST, exposed on port 30000 Coherence Metrics Coherence Metrics, exposed on port 9612 Coherence Tracing Coherence tracing is configured to use a Jaeger tracing server. See the Tracing section below. Building the Image Assuming that you have first cloned the Coherence CE project the to build the Coherence image run the following command from the top-level Maven prj/ folder: mvn clean install -P docker -pl coherence-docker The name of the image produced comes from properties in the coherence-docker module pom.xml file. ${docker.registry}/coherence-ce:<version> Where <version> is the version of the product from the pom.xml file. The ${docker.registry} property is the name of the registry that the image will be published to, by default this is oraclecoherence . So, if the version in the pom.xml is 14.1.1-0-20 the image produced will be oraclecoherence/coherence-ce:14.1.1-0-20 To change the registry name the image can be built by specifying the docker.registry property, for example: mvn clean install -P docker -pl coherence-docker -Ddocker.registry=foo The example above would build an image named foo/coherence:14.1.1-0-20 Run the image Run the image just like any other image. In Docker this command would be: docker run -d -P oraclecoherence/coherence-ce:{project-version} The -P parameter will ensure that the Extend, gRPC, management and metrics ports will all be exposed. Run the Image in Kubernetes This image can be run in Kubernetes using the Coherence Operator . The sections below on additional configurations do not apply when using the Coherence Operator to run the image in Kubernetes. The operator provides functionality to configure the container correctly. Specifying Coherence System Properties Many options in Coherence can be set from System properties prefiexed with coherence. . The issue here is that System properties are not very easy to pass into the JVM in the container, whereas environment variables are. To help with this the main class which runs in the container will convert any environment variable prefixed with coherence. into a System property before it starts Coherence. docker run -d -P \\ -e coherence.cluster=testing \\ -e coherence.role=storage \\ oraclecoherence/coherence-ce:{project-version} The example above sets two environment variables, coherence.cluster=testing and coherence.role=storage . These will be converted to System properties so Coherence will start the same as it would if the variables had been passed to the JVM command line as -Dcoherence.cluster=testing -Dcoherence.role=storage This only applies to environment variables prefixed with coherence. that have not already set as System properties some other way. Specifying JVM Options Images built with JIB have a fixed entrypoint configured to run the application. This is not very flexible if additional options need to be passed to the JVM. The Coherence image makes use of the JVM’s ability to load options at start-up from a file by using a JVM option @<file-name> . The Coherence image entrypoint contains @/args/jvm-args.txt , so the JVM will load additional options on start-up from a file named /args/jvm-args.txt . This means that additional options can be provided by adding a volume mapping that adds this file to the container. For example, to set the heap to 5g, the Coherence cluster name to test-cluster and role name to storage then additional JVM arguments will be required. Create a file named jvm-args.txt containing these properties: -Xms5g -Xmx5g -Dcoherence.cluster=test-cluster -Dcoherence.role=storage If the file has been created in a local directory named /home/oracle/test-args then the image can be run with the following command: docker run -d -P -v /home/oracle/test-args:/args oraclecoherence/coherence-ce:{project-version} This will cause Docker to mount the local /home/oracle/test-args directory to the /args directory in the container where the JVM will find the jvm-args.txt file. Adding to the Classpath Images built with JIB have a fixed classpath configured, which is not very flexible if additional resources need to be added to the classpath. The Coherence image maps two additional directories to the classpath that are empty in the image and may be used to add items to the classpath by mapping external volumes to these directories. The additional classpath entries are: /coherence/ext/lib/* - this will add all .jar files under the /coherence/ext/lib/ directory to the classpath /coherence/ext/conf - this adds /coherence/ext/conf to the classpath so that any classes, packages or other resource files in this directory will be added to the classpath. For example: On the local Docker host there is a folder called /dev/my-app/lib that contains .jar files to be added to the container classpath. docker run -d -P -v /dev/my-app/lib:/coherence/ext/lib oraclecoherence/coherence-ce:{project-version} The command above maps the local directory /dev/my-app/lib to the /coherence/ext/lib in the container so that any .jar files in the /dev/my-app/lib directory will now be on the Coherence JVM’s classpath. On the local Docker host there is a folder called /dev/my-app/classes that contains .class files and other application resources to be added to the container classpath. docker run -d -P -v /dev/my-app/classes:/coherence/ext/conf oraclecoherence/coherence-ce:{project-version} The command above maps the local directory /dev/my-app/classes to the /coherence/ext/conf in the container so that any classes and resource files in the /dev/my-app/classes directory will now be on the Coherence JVM’s classpath. ", + "title": "Coherence OCI Image" + }, + { + "location": "/coherence-docker/README", + "text": " Multiple containers can be started to form a cluster. By default, Coherence uses multi-cast for cluster discovery but in containers this either will not work, or is not reliable, so well-known-addressing can be used. This example is going to use basic Docker commands and links between containers. There are other ways to achieve the same sort of functionality depending on the network configurations you want to use in Docker. First, determine the name to be used for the first container, in this example it will be storage-1 . Next, create a ` Start the first container in the cluster: docker run -d -P \\ --name storage-1 \\ --hostname storage-1 \\ -e coherence.wka=storage-1 \\ -e coherence.cluster=testing \\ oraclecoherence/coherence-ce:{project-version} The first container has been started with a container name of storage-1 , and the host name also set to storage-1 . The container sets the WKA host name to storage-1 using -e coherence.wka=storage-1 (this will be converted to the System property coherence.wka=storage-1 see Specifying Coherence System Properties above). The container sets the Coherence cluster name to testing using -e coherence.cluster=testing (this will be converted to the System property coherence.cluster=testing see Specifying Coherence System Properties above). The important part here is that the container has a name, and the --hostname option has also been set. This will allow the subsequent cluster members to find this container. Now, subsequent containers can be started using the same cluster name and WKA host name, but with different container names and a link to the first container, all the containers will form a single Coherence cluster: docker run -d -P \\ --name storage-2 \\ --link storage-1 \\ -e coherence.wka=storage-1 \\ -e coherence.cluster=testing \\ oraclecoherence/coherence-ce:{project-version} docker run -d -P \\ --name storage-3 \\ --link storage-1 \\ -e coherence.wka=storage-1 \\ -e coherence.cluster=testing \\ oraclecoherence/coherence-ce:{project-version} Two more containers, storage-2 and storage-3 will now be part of the cluster. All the members must have a --link option to the first container and have the same WKA and cluster name properties. ", + "title": "Clustering" + }, + { + "location": "/coherence-docker/README", + "text": " The Coherence image comes with tracing already configured, it just requires a suitable Jaeger server to send spans to. The simplest way to start is deploy the Jaeger all-in-one server, for example: docker run -d --name jaeger \\ -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \\ -p 5775:5775/udp \\ -p 6831:6831/udp \\ -p 6832:6832/udp \\ -p 5778:5778 \\ -p 16686:16686 \\ -p 14268:14268 \\ -p 14250:14250 \\ -p 9411:9411 \\ jaegertracing/all-in-one:latest The Jaeger UI will be available to browse to at http://127.0.0.1:16686 Jaeger has been started with a container name of jaeger , so it will be discoverable using that host name by the Coherence containers. Start the Coherence container with a link to the Jaeger container and set the JAEGER_AGENT_HOST environment variable to jaeger : docker run -d -P --link jaeger \\ -e JAEGER_AGENT_HOST=jaeger \\ oraclecoherence/coherence-ce:{project-version} Once the Coherence container is running perform some interations with it using one of the exposed services, i.e Extend or gRPC, and spans will be sent to the Jaeger collector and will be visible in the UI by querying for the coherence service name. The service name used can be changed by setting the JAEGER_SERVICE_NAME environment variable when starting the container, for example: docker run -d -P --link jaeger \\ -e JAEGER_AGENT_HOST=jaeger \\ -e JAEGER_SERVICE_NAME=coherence-test oraclecoherence/coherence-ce:{project-version} Spans will now be sent to Jaeger with the service name coherence-test . Tracing is very useful to show what happens under the covers for a given Coherence API call. Traces are more interesting when they come from a Coherence cluster with multiple members, where the traces span different cluster members. This can easily be done by running multiple containers with tracing enabled and configuring Clustering as described above. ", + "title": "Tracing" + }, + { + "location": "/coherence-grpc-proxy-client/README", + "text": "", + "title": "Coherence gRPC Client" + }, + { + "location": "/coherence-cdi/README", + "text": " Coherence CDI provides support for CDI (Contexts and Dependency Injection) within Coherence cluster members. It allows you both to inject Coherence-managed resources, such as NamedCache and Session instances into CDI managed beans, and to inject CDI beans into Coherence-managed resources, such as event interceptors and cache stores, and to handle Coherence server-side events using CDI observer methods. In addition, Coherence CDI provides support for automatic injection of transient objects upon deserialization. This allows you to inject CDI managed beans such as services and repositories (to use DDD nomenclature) into transient objects, such as entry processor and even data class instances, greatly simplifying implementation of true Domain Driven applications. ", + "title": "Coherence CDI" + }, + { + "location": "/coherence-cdi/README", + "text": " In order to inject an instance of a NamedCache into your CDI bean, you simply need to define an injection point for it: @Inject private NamedCache<Long, Person> people; In the example above we’ve assumed that the cache name you want to inject is the same as the name of the field you are injecting into, people . If that’s not the case, you can use @Cache qualifier to specify the name of the cache you want to obtain explicitly: @Inject @Cache(\"people\") private NamedCache<Long, Person> m_people; This is also what you have to do if you are using constructor injection instead: @Inject public MyClass(@Cache(\"people\") NamedCache<Long, Person> people) { ... } All of the examples above assume that you want to use the default ConfigurableCacheFactory , which is often, but not always the case. For example, you may have an Extend client that connects to multiple Coherence clusters, in which case you would have multiple Coherence cache config files, and multiple ConfigurableCacheFactoriy instances. In this case you would use @CacheFactory qualifier to specify the URI of the cache configuration to use: @Inject @CacheFactory(\"products-cluster.xml\") private NamedCache<Long, Product> products; @Inject @CacheFactory(\"customers-cluster.xml\") private NamedCache<Long, Customer> customers; You can replace NamedCache in any of the examples above with AsyncNamedCache in order to inject asynchronous variant of the NamedCache API: @Inject private AsyncNamedCache<Long, Person> people; You can also inject cache views , which are effectively instances of a ContinuousQueryCache , either by declaring the injection point as ContinuousQueryCache instead of NamedCache , or by simply adding @CacheView qualifier: @Inject private ContinuousQueryCache<Long, Person> people; @Inject @CacheView private NamedCache<Long, Person> people; The examples above are equivalent, and both will bring all the data from the backing cache into a local view, as they will use AlwaysFilter when constructing CQC . If you want to limit the data in the view to a subset, you can implement a custom filter binding (recommended), or use a built-in @WhereFilter for convenience, which allows you to specify a filter using CohQL: @Inject @CacheView @WhereFilter(\"gender = 'MALE'\") @Cache(\"people\") private NamedCache<Long, Person> men; @Inject @CacheView @WhereFilter(\"gender = 'FEMALE'\") @Cache(\"people\") private NamedCache<Long, Person> women; The ContinuousQueryCache , and cache views by extension, also support transformation of the cached value on the server, in order to reduce both the amount of data stored locally and the amount of data transferred over the network. For example, you may have a complex Person objects in the backing cache, but only need their names in order to populate a drop down on the client UI. In that case, you can implement a custom extractor binding (recommended), or use a built-in @PropertyExtractor for convenience: @Inject @CacheView @PropertyExtractor(\"fullName\") @Cache(\"people\") private NamedCache<Long, String> names; Note that the value type in the example above has changed from Person to String , due to server-side transformation caused by the specified @PropertyExtractor . ", + "title": "Injecting NamedCache and related objects" + }, + { + "location": "/coherence-cdi/README", + "text": " In order to inject an instance of a NamedTopic into your CDI bean, you simply need to define an injection point for it: @Inject private NamedTopic<Order> orders; In the example above we’ve assumed that the topic name you want to inject is the same as the name of the field you are injecting into, in this case orders . If that’s not the case, you can use @Topic qualifier to specify the name of the cache you want to obtain explicitly: @Inject @Topic(\"orders\") private NamedTopic<Order> m_orders; This is also what you have to do if you are using constructor injection instead: @Inject public MyClass(@Topic(\"orders\") NamedTopic<Order> orders) { ... } All of the examples above assume that you want to use the default ConfigurableCacheFactory , which is often, but not always the case. For example, you may have an Extend client that connects to multiple Coherence clusters, in which case you would have multiple Coherence cache config files, and multiple ConfigurableCacheFactoriy instances. In this case you would use @CacheFactory qualifier to specify the URI of the cache configuration to use: @Inject @CacheFactory(\"payments-cluster.xml\") private NamedTopic<PaymentRequest> payments; @Inject @CacheFactory(\"shipments-cluster.xml\") private NamedTopic<ShippingRequest> shipments; The examples above allow you to inject a NamedTopic instance into your CDI bean, but it is often simpler and more convenient to inject Publisher or Subscriber for a given topic instead. This can be easily accomplished by replacing NamedTopic<T> in any of the examples above with either Publisher<T> : @Inject private Publisher<Order> orders; @Inject @Topic(\"orders\") private Publisher<Order> m_orders; @Inject @CacheFactory(\"payments-cluster.xml\") private Publisher<PaymentRequest> payments; or Subscriber<T> : @Inject private Subscriber<Order> orders; @Inject @Topic(\"orders\") private Subscriber<Order> m_orders; @Inject @CacheFactory(\"payments-cluster.xml\") private Subscriber<PaymentRequest> payments; Basically, all topic-related details, such as topic name (based on either injection point name or the explicit name from @Topic annotation), cache factory URI and message type, will be used under the hood to retrieve the NamedTopic , and to obtain Publisher or Subscriber from it. Additionally, if you want to place your Subscriber s into a subscriber group, you can easily accomplish that by adding @SubscriberGroup qualifier to the injection point: @Inject @SubscriberGroup(\"orders-queue\") private Subscriber<Order> orders; ", + "title": "Injecting NamedTopic and related objects" + }, + { + "location": "/coherence-cdi/README", + "text": " If you need an instance of a Cluster interface somewhere in your application, you can easily obtain it via injection: @Inject private Cluster cluster; You can do the same if you need an instance of an OperationalContext : @Inject private OperationalContext ctx; ", + "title": " Cluster and OperationalContext Injection" + }, + { + "location": "/coherence-cdi/README", + "text": " On rare occasions when you need to use either of these directly, Coherence CDI makes it trivial to do so. To obtain an instance of a default CCF or Session , all you need to do is inject them into the class that needs to use them: @Inject private ConfigurableCacheFactory ccf; @Inject private Session session; If you need a specific CCF or Session you can simply qualify them using @CacheFactory qualifier and specifying the URI of the cache config file to use: @Inject @CacheFactory(\"my-cache-config.xml\") private ConfigurableCacheFactory ccf; @Inject @CacheFactory(\"my-cache-config.xml\") private Session session; ", + "title": " ConfigurableCacheFactory and Session Injection" + }, + { + "location": "/coherence-cdi/README", + "text": " While in most cases you won’t have to deal with serializers directly, Coherence CDI makes it simple to obtain named serializers (and to register new ones) when you need. To get a default Serializer for the current context class loader, you can simply inject it: @Inject private Serializer defaultSerializer; However, it may be more useful to inject one of the named serializers defined in the operational configuration, which can be easily accomplished using @SerializerFormat qualifier: @Inject @SerializerFormat(\"java\") private Serializer javaSerializer; @Inject @SerializerFormat(\"pof\") private Serializer pofSerializer; In addition to the serializers defined in the operational config, the example above will also perform BeanManager lookup for a named bean that implements Serializer interface. That means that if you implemented a custom Serializer bean, such as: @Named(\"json\") @ApplicationScoped public class JsonSerializer implements Serializer { ... } it would be automatically discovered and registered by the CDI, and you would then be able to inject it just as easily as the named serializers defined in the operational config: @Inject @SerializerFormat(\"json\") private Serializer jsonSerializer; ", + "title": " Serializer Injection" + }, + { + "location": "/coherence-cdi/README", + "text": " While the injection of a NamedCache , NamedTopic , and related instances, as shown above, is probably the single most useful feature of Coherence CDI, it is certainly not the only one. The following sections describe other Coherence artifacts that can be injected using Coherence CDI. Cluster and OperationalContext Injection If you need an instance of a Cluster interface somewhere in your application, you can easily obtain it via injection: @Inject private Cluster cluster; You can do the same if you need an instance of an OperationalContext : @Inject private OperationalContext ctx; ConfigurableCacheFactory and Session Injection On rare occasions when you need to use either of these directly, Coherence CDI makes it trivial to do so. To obtain an instance of a default CCF or Session , all you need to do is inject them into the class that needs to use them: @Inject private ConfigurableCacheFactory ccf; @Inject private Session session; If you need a specific CCF or Session you can simply qualify them using @CacheFactory qualifier and specifying the URI of the cache config file to use: @Inject @CacheFactory(\"my-cache-config.xml\") private ConfigurableCacheFactory ccf; @Inject @CacheFactory(\"my-cache-config.xml\") private Session session; Serializer Injection While in most cases you won’t have to deal with serializers directly, Coherence CDI makes it simple to obtain named serializers (and to register new ones) when you need. To get a default Serializer for the current context class loader, you can simply inject it: @Inject private Serializer defaultSerializer; However, it may be more useful to inject one of the named serializers defined in the operational configuration, which can be easily accomplished using @SerializerFormat qualifier: @Inject @SerializerFormat(\"java\") private Serializer javaSerializer; @Inject @SerializerFormat(\"pof\") private Serializer pofSerializer; In addition to the serializers defined in the operational config, the example above will also perform BeanManager lookup for a named bean that implements Serializer interface. That means that if you implemented a custom Serializer bean, such as: @Named(\"json\") @ApplicationScoped public class JsonSerializer implements Serializer { ... } it would be automatically discovered and registered by the CDI, and you would then be able to inject it just as easily as the named serializers defined in the operational config: @Inject @SerializerFormat(\"json\") private Serializer jsonSerializer; ", + "title": "Other Supported Injection Points" + }, + { + "location": "/coherence-cdi/README", + "text": " CDI, and dependency injection in general, make it easy for application classes to declare the dependencies they need and let the runtime provide them when necessary. This makes the applications easier to develop, test and reason about, and the code significantly cleaner. Coherence CDI allows you to do the same for Coherence objects, such as Cluster , Session , NamedCache , ContinuousQueryCache , ConfigurableCacheFactory , etc. Injecting NamedCache and related objects In order to inject an instance of a NamedCache into your CDI bean, you simply need to define an injection point for it: @Inject private NamedCache<Long, Person> people; In the example above we’ve assumed that the cache name you want to inject is the same as the name of the field you are injecting into, people . If that’s not the case, you can use @Cache qualifier to specify the name of the cache you want to obtain explicitly: @Inject @Cache(\"people\") private NamedCache<Long, Person> m_people; This is also what you have to do if you are using constructor injection instead: @Inject public MyClass(@Cache(\"people\") NamedCache<Long, Person> people) { ... } All of the examples above assume that you want to use the default ConfigurableCacheFactory , which is often, but not always the case. For example, you may have an Extend client that connects to multiple Coherence clusters, in which case you would have multiple Coherence cache config files, and multiple ConfigurableCacheFactoriy instances. In this case you would use @CacheFactory qualifier to specify the URI of the cache configuration to use: @Inject @CacheFactory(\"products-cluster.xml\") private NamedCache<Long, Product> products; @Inject @CacheFactory(\"customers-cluster.xml\") private NamedCache<Long, Customer> customers; You can replace NamedCache in any of the examples above with AsyncNamedCache in order to inject asynchronous variant of the NamedCache API: @Inject private AsyncNamedCache<Long, Person> people; You can also inject cache views , which are effectively instances of a ContinuousQueryCache , either by declaring the injection point as ContinuousQueryCache instead of NamedCache , or by simply adding @CacheView qualifier: @Inject private ContinuousQueryCache<Long, Person> people; @Inject @CacheView private NamedCache<Long, Person> people; The examples above are equivalent, and both will bring all the data from the backing cache into a local view, as they will use AlwaysFilter when constructing CQC . If you want to limit the data in the view to a subset, you can implement a custom filter binding (recommended), or use a built-in @WhereFilter for convenience, which allows you to specify a filter using CohQL: @Inject @CacheView @WhereFilter(\"gender = 'MALE'\") @Cache(\"people\") private NamedCache<Long, Person> men; @Inject @CacheView @WhereFilter(\"gender = 'FEMALE'\") @Cache(\"people\") private NamedCache<Long, Person> women; The ContinuousQueryCache , and cache views by extension, also support transformation of the cached value on the server, in order to reduce both the amount of data stored locally and the amount of data transferred over the network. For example, you may have a complex Person objects in the backing cache, but only need their names in order to populate a drop down on the client UI. In that case, you can implement a custom extractor binding (recommended), or use a built-in @PropertyExtractor for convenience: @Inject @CacheView @PropertyExtractor(\"fullName\") @Cache(\"people\") private NamedCache<Long, String> names; Note that the value type in the example above has changed from Person to String , due to server-side transformation caused by the specified @PropertyExtractor . Injecting NamedTopic and related objects In order to inject an instance of a NamedTopic into your CDI bean, you simply need to define an injection point for it: @Inject private NamedTopic<Order> orders; In the example above we’ve assumed that the topic name you want to inject is the same as the name of the field you are injecting into, in this case orders . If that’s not the case, you can use @Topic qualifier to specify the name of the cache you want to obtain explicitly: @Inject @Topic(\"orders\") private NamedTopic<Order> m_orders; This is also what you have to do if you are using constructor injection instead: @Inject public MyClass(@Topic(\"orders\") NamedTopic<Order> orders) { ... } All of the examples above assume that you want to use the default ConfigurableCacheFactory , which is often, but not always the case. For example, you may have an Extend client that connects to multiple Coherence clusters, in which case you would have multiple Coherence cache config files, and multiple ConfigurableCacheFactoriy instances. In this case you would use @CacheFactory qualifier to specify the URI of the cache configuration to use: @Inject @CacheFactory(\"payments-cluster.xml\") private NamedTopic<PaymentRequest> payments; @Inject @CacheFactory(\"shipments-cluster.xml\") private NamedTopic<ShippingRequest> shipments; The examples above allow you to inject a NamedTopic instance into your CDI bean, but it is often simpler and more convenient to inject Publisher or Subscriber for a given topic instead. This can be easily accomplished by replacing NamedTopic<T> in any of the examples above with either Publisher<T> : @Inject private Publisher<Order> orders; @Inject @Topic(\"orders\") private Publisher<Order> m_orders; @Inject @CacheFactory(\"payments-cluster.xml\") private Publisher<PaymentRequest> payments; or Subscriber<T> : @Inject private Subscriber<Order> orders; @Inject @Topic(\"orders\") private Subscriber<Order> m_orders; @Inject @CacheFactory(\"payments-cluster.xml\") private Subscriber<PaymentRequest> payments; Basically, all topic-related details, such as topic name (based on either injection point name or the explicit name from @Topic annotation), cache factory URI and message type, will be used under the hood to retrieve the NamedTopic , and to obtain Publisher or Subscriber from it. Additionally, if you want to place your Subscriber s into a subscriber group, you can easily accomplish that by adding @SubscriberGroup qualifier to the injection point: @Inject @SubscriberGroup(\"orders-queue\") private Subscriber<Order> orders; Other Supported Injection Points While the injection of a NamedCache , NamedTopic , and related instances, as shown above, is probably the single most useful feature of Coherence CDI, it is certainly not the only one. The following sections describe other Coherence artifacts that can be injected using Coherence CDI. Cluster and OperationalContext Injection If you need an instance of a Cluster interface somewhere in your application, you can easily obtain it via injection: @Inject private Cluster cluster; You can do the same if you need an instance of an OperationalContext : @Inject private OperationalContext ctx; ConfigurableCacheFactory and Session Injection On rare occasions when you need to use either of these directly, Coherence CDI makes it trivial to do so. To obtain an instance of a default CCF or Session , all you need to do is inject them into the class that needs to use them: @Inject private ConfigurableCacheFactory ccf; @Inject private Session session; If you need a specific CCF or Session you can simply qualify them using @CacheFactory qualifier and specifying the URI of the cache config file to use: @Inject @CacheFactory(\"my-cache-config.xml\") private ConfigurableCacheFactory ccf; @Inject @CacheFactory(\"my-cache-config.xml\") private Session session; Serializer Injection While in most cases you won’t have to deal with serializers directly, Coherence CDI makes it simple to obtain named serializers (and to register new ones) when you need. To get a default Serializer for the current context class loader, you can simply inject it: @Inject private Serializer defaultSerializer; However, it may be more useful to inject one of the named serializers defined in the operational configuration, which can be easily accomplished using @SerializerFormat qualifier: @Inject @SerializerFormat(\"java\") private Serializer javaSerializer; @Inject @SerializerFormat(\"pof\") private Serializer pofSerializer; In addition to the serializers defined in the operational config, the example above will also perform BeanManager lookup for a named bean that implements Serializer interface. That means that if you implemented a custom Serializer bean, such as: @Named(\"json\") @ApplicationScoped public class JsonSerializer implements Serializer { ... } it would be automatically discovered and registered by the CDI, and you would then be able to inject it just as easily as the named serializers defined in the operational config: @Inject @SerializerFormat(\"json\") private Serializer jsonSerializer; ", + "title": "Injecting Coherence Objects into CDI Beans" + }, + { + "location": "/coherence-cdi/README", + "text": " All of the examples above used synchronous observers by specifying @Observes qualifier for each observer method. However, Coherence CDI fully supports asynchronous CDI observers as well. All you need to do is replace @Observes with @ObservesAsync in any of the examples above. private void onActivated(@ObservesAsync @Activated LifecycleEvent event) { // handle cache factory activation } private void onCreatedPeople(@ObservesAsync @Created @CacheName(\"people\") CacheLifecycleEvent event) { // handle creation of the 'people' cache } private void onExecuting(@ObservesAsync @Executing @CacheName(\"people\") @Processor(Uppercase.class) EntryProcessorEvent event) { // intercept 'Uppercase` entry processor execution against 'people' cache } However, there is an important caveat. Coherence events fall into two categories: pre- and post-commit events. All of the events whose name ends with ing , such as Inserting , Updating , Removing or Executing are pre-commit, which means that they can either modify the data or even completely cancel the operation by throwing an exception, but in order to do so they must be synchronous to ensure that they are executed on the same thread that is executing the operation that triggered the event. That means that you can observe them using asynchronous CDI observers, but if you want to mutate the set of entries that are part of the event payload, or veto the event by throwing an exception, you must use synchronous CDI observer. ", + "title": "Using Asynchronous Observers" + }, + { + "location": "/coherence-cdi/README", + "text": " While the above examples show that you can implement any Coherence EventInterceptor as a CDI bean and register it using <cdi:bean> element within the cache configuration file, Coherence CDI also provides a much simpler way to accomplish the same goal using standard CDI Events and Observers. The first thing you need to do is register a single global interceptor within the cache config: <cache-config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:cdi=\"class://com.oracle.coherence.cdi.CdiNamespaceHandler\" xmlns=\"http://xmlns.oracle.com/coherence/coherence-cache-config\" xsi:schemaLocation=\"http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd\"> <interceptors> <interceptor> <instance> <cdi:bean>com.oracle.coherence.cdi.EventDispatcher</cdi:bean> </instance> </interceptor> </interceptors> <!-- the rest of cache config as usual --> </cache-config> Coherence CDI EventDispatcher bean will then listen to all events raised by all Coherence event dispatchers and re-raise them as CDI events that you can observe. For example, to implement the equivalent of cacheListener interceptor above, you would simply define an observer method in any CDI bean that wants to be notified when the event happens: private void onInserting(@Observes @Inserting EntryEvent<?, ?> event) { // handle INSERTING event on any cache } The observer method above will receive all INSERTING events for all the caches, across all the services, but you can use CDI qualifiers to control that behavior: private void onInserting(@Observes @Updated @CacheName(\"people\") EntryEvent<?, ?> event) { // handle UPDATED event on 'people' cache only } private void onRemoved(@Observes @Removed @ServiceName(\"Products\") EntryEvent<?, ?> event) { // handle REMOVED event on any cache on the 'Products' service } Of course, you can also remove qualifiers to broaden the scope of events your handler receives: private void onEntryEvent(@Observes EntryEvent<?, ?> event) { // handle any event on any cache } The examples above show only how to handle EntryEvent s, but the same applies to all other event types: private void onActivated(@Observes @Activated LifecycleEvent event) { // handle cache factory activation } private void onCreatedPeople(@Observes @Created @CacheName(\"people\") CacheLifecycleEvent event) { // handle creation of the 'people' cache } private void onExecuting(@Observes @Executing @CacheName(\"people\") @Processor(Uppercase.class) EntryProcessorEvent event) { // intercept 'Uppercase` entry processor execution against 'people' cache } And again, you can broaden the scope by widening the type of events you observe: private void onPartitionedCacheEvent(@Observes com.tangosol.net.events.partition.cache.Event<?> event) { // handle any/all events raised by the partitioned cache service (CacheLifecycleEvent, EntryEvent or EntryProcessorEvent) // can use @CacheName and @ServiceName as a narrowing qualifier } private void onPartitionedServiceEvent(@Observes com.tangosol.net.events.partition.Event<?> event) { // handle any/all events raised by the partitioned service (TransactionEvent, TransferEvent or UnsolicitedCommitEvent) // can use @ServiceName as a narrowing qualifier } private void onEvent(@Observes com.tangosol.net.events.Event<?> event) { // handle any/all events (all of the above, plus LifecycleEvent) } Using Asynchronous Observers All of the examples above used synchronous observers by specifying @Observes qualifier for each observer method. However, Coherence CDI fully supports asynchronous CDI observers as well. All you need to do is replace @Observes with @ObservesAsync in any of the examples above. private void onActivated(@ObservesAsync @Activated LifecycleEvent event) { // handle cache factory activation } private void onCreatedPeople(@ObservesAsync @Created @CacheName(\"people\") CacheLifecycleEvent event) { // handle creation of the 'people' cache } private void onExecuting(@ObservesAsync @Executing @CacheName(\"people\") @Processor(Uppercase.class) EntryProcessorEvent event) { // intercept 'Uppercase` entry processor execution against 'people' cache } However, there is an important caveat. Coherence events fall into two categories: pre- and post-commit events. All of the events whose name ends with ing , such as Inserting , Updating , Removing or Executing are pre-commit, which means that they can either modify the data or even completely cancel the operation by throwing an exception, but in order to do so they must be synchronous to ensure that they are executed on the same thread that is executing the operation that triggered the event. That means that you can observe them using asynchronous CDI observers, but if you want to mutate the set of entries that are part of the event payload, or veto the event by throwing an exception, you must use synchronous CDI observer. ", + "title": "Using CDI Observers to Handle Coherence Server-Side Events" + }, + { + "location": "/coherence-cdi/README", + "text": " Coherence has a number of server-side extension points, which allow users to customize application behavior in different ways, typically by configuring their extensions within various sections of the cache configuration file. For example, the users can implement event interceptors and cache stores, in order to handle server-side events and integrate with the external data stores and other services. Coherence CDI provides a way to inject named CDI beans into these extension points using custom configuration namespace handler. <cache-config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://xmlns.oracle.com/coherence/coherence-cache-config\" xmlns:cdi=\"class://com.oracle.coherence.cdi.CdiNamespaceHandler\" xsi:schemaLocation=\"http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd\"> Once you’ve declared the handler for the cdi namespace above, you can specify <cdi:bean> element in any place where you would normally use <class-name> or <class-factory-name> elements: <?xml version=\"1.0\"?> <cache-config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://xmlns.oracle.com/coherence/coherence-cache-config\" xmlns:cdi=\"class://com.oracle.coherence.cdi.CdiNamespaceHandler\" xsi:schemaLocation=\"http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd\"> <interceptors> <interceptor> <instance> <cdi:bean>registrationListener</cdi:bean> </instance> </interceptor> <interceptor> <instance> <cdi:bean>activationListener</cdi:bean> </instance> </interceptor> </interceptors> <caching-scheme-mapping> <cache-mapping> <cache-name>*</cache-name> <scheme-name>distributed-scheme</scheme-name> <interceptors> <interceptor> <instance> <cdi:bean>cacheListener</cdi:bean> </instance> </interceptor> </interceptors> </cache-mapping> </caching-scheme-mapping> <caching-schemes> <distributed-scheme> <scheme-name>distributed-scheme</scheme-name> <service-name>PartitionedCache</service-name> <local-storage system-property=\"coherence.distributed.localstorage\">true</local-storage> <partition-listener> <cdi:bean>partitionListener</cdi:bean> </partition-listener> <member-listener> <cdi:bean>memberListener</cdi:bean> </member-listener> <backing-map-scheme> <local-scheme/> </backing-map-scheme> <autostart>true</autostart> <interceptors> <interceptor> <instance> <cdi:bean>storageListener</cdi:bean> </instance> </interceptor> </interceptors> </distributed-scheme> </caching-schemes> </cache-config> Note that you can only inject named CDI beans (beans with an explicit @Named annotations) via <cdi:bean> element. For example, the cacheListener interceptor bean used above would look similar to this: @ApplicationScoped @Named(\"cacheListener\") @EntryEvents(INSERTING) public class MyCacheListener implements EventInterceptor<EntryEvent<Long, String>> { @Override public void onEvent(EntryEvent<Long, String> e) { // handle INSERTING event } } Also keep in mind that only @ApplicationScoped beans can be injected, which implies that they may be shared. For example, because we’ve used wildcard * as a cache name within the cache mapping in the example above, the same instance of cacheListener will receive events from multiple caches. This is typically fine, as the event itself provides the details about the context that raised it, including cache name and the service it was raised from, but it does imply that any shared state that you may have within your listener class shouldn’t be context-specific and it must be safe for concurrent access from multiple threads. If you can’t guarantee the latter, you may want to declare the onEvent method as synchronized , to ensure only one thread at a time can access any shared state you may have. Using CDI Observers to Handle Coherence Server-Side Events While the above examples show that you can implement any Coherence EventInterceptor as a CDI bean and register it using <cdi:bean> element within the cache configuration file, Coherence CDI also provides a much simpler way to accomplish the same goal using standard CDI Events and Observers. The first thing you need to do is register a single global interceptor within the cache config: <cache-config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:cdi=\"class://com.oracle.coherence.cdi.CdiNamespaceHandler\" xmlns=\"http://xmlns.oracle.com/coherence/coherence-cache-config\" xsi:schemaLocation=\"http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd\"> <interceptors> <interceptor> <instance> <cdi:bean>com.oracle.coherence.cdi.EventDispatcher</cdi:bean> </instance> </interceptor> </interceptors> <!-- the rest of cache config as usual --> </cache-config> Coherence CDI EventDispatcher bean will then listen to all events raised by all Coherence event dispatchers and re-raise them as CDI events that you can observe. For example, to implement the equivalent of cacheListener interceptor above, you would simply define an observer method in any CDI bean that wants to be notified when the event happens: private void onInserting(@Observes @Inserting EntryEvent<?, ?> event) { // handle INSERTING event on any cache } The observer method above will receive all INSERTING events for all the caches, across all the services, but you can use CDI qualifiers to control that behavior: private void onInserting(@Observes @Updated @CacheName(\"people\") EntryEvent<?, ?> event) { // handle UPDATED event on 'people' cache only } private void onRemoved(@Observes @Removed @ServiceName(\"Products\") EntryEvent<?, ?> event) { // handle REMOVED event on any cache on the 'Products' service } Of course, you can also remove qualifiers to broaden the scope of events your handler receives: private void onEntryEvent(@Observes EntryEvent<?, ?> event) { // handle any event on any cache } The examples above show only how to handle EntryEvent s, but the same applies to all other event types: private void onActivated(@Observes @Activated LifecycleEvent event) { // handle cache factory activation } private void onCreatedPeople(@Observes @Created @CacheName(\"people\") CacheLifecycleEvent event) { // handle creation of the 'people' cache } private void onExecuting(@Observes @Executing @CacheName(\"people\") @Processor(Uppercase.class) EntryProcessorEvent event) { // intercept 'Uppercase` entry processor execution against 'people' cache } And again, you can broaden the scope by widening the type of events you observe: private void onPartitionedCacheEvent(@Observes com.tangosol.net.events.partition.cache.Event<?> event) { // handle any/all events raised by the partitioned cache service (CacheLifecycleEvent, EntryEvent or EntryProcessorEvent) // can use @CacheName and @ServiceName as a narrowing qualifier } private void onPartitionedServiceEvent(@Observes com.tangosol.net.events.partition.Event<?> event) { // handle any/all events raised by the partitioned service (TransactionEvent, TransferEvent or UnsolicitedCommitEvent) // can use @ServiceName as a narrowing qualifier } private void onEvent(@Observes com.tangosol.net.events.Event<?> event) { // handle any/all events (all of the above, plus LifecycleEvent) } Using Asynchronous Observers All of the examples above used synchronous observers by specifying @Observes qualifier for each observer method. However, Coherence CDI fully supports asynchronous CDI observers as well. All you need to do is replace @Observes with @ObservesAsync in any of the examples above. private void onActivated(@ObservesAsync @Activated LifecycleEvent event) { // handle cache factory activation } private void onCreatedPeople(@ObservesAsync @Created @CacheName(\"people\") CacheLifecycleEvent event) { // handle creation of the 'people' cache } private void onExecuting(@ObservesAsync @Executing @CacheName(\"people\") @Processor(Uppercase.class) EntryProcessorEvent event) { // intercept 'Uppercase` entry processor execution against 'people' cache } However, there is an important caveat. Coherence events fall into two categories: pre- and post-commit events. All of the events whose name ends with ing , such as Inserting , Updating , Removing or Executing are pre-commit, which means that they can either modify the data or even completely cancel the operation by throwing an exception, but in order to do so they must be synchronous to ensure that they are executed on the same thread that is executing the operation that triggered the event. That means that you can observe them using asynchronous CDI observers, but if you want to mutate the set of entries that are part of the event payload, or veto the event by throwing an exception, you must use synchronous CDI observer. ", + "title": "Injecting CDI Beans into Coherence-managed Objects" + }, + { + "location": "/coherence-cdi/README", + "text": " While not technically a true marker interface, Injectable can be treated as such for all intents and purposes. All you need to do is add it to the implements clause of your class in order for injection on deserialization to kick in: public class InjectableBean implements Injectable, Serializable { @Inject private Converter<String, String> converter; private String text; InjectableBean() { } InjectableBean(String text) { this.text = text; } String getConvertedText() { return converter.convert(text); } } Assuming that you have the following Converter service implementation in your application, it will be injected into InjectableBean after deserialization and the getConvertedText method will return the value of the text field converted to upper case: @ApplicationScoped public class ToUpperConverter implements Converter<String, String> { @Override public String convert(String s) { return s.toUpperCase(); } } If your Injectable class has @PostConstruct callback method, it will be called after the injection. However, because we have no control over object’s lifecycle after that point, @PreDestroy callback will never be called). You should note that the above functionality is not dependent on the serialization format and will work with both Java and POF serialization (or any other custom serializer), and for any object that is deserialized on any Coherence member (or even on a remote client). While the deserialized transient objects are not true CDI managed beans, being able to inject CDI managed dependencies into them upon deserialization will likely satisfy most dependency injection requirements you will ever have in those application components. We hope you’ll find it useful. ", + "title": "Making transient classes Injectable " + }, + { + "location": "/coherence-cdi/README", + "text": " Using CDI to inject Coherence objects into your application classes, and CDI beans into Coherence-managed objects will allow you to support many use cases where dependency injection may be useful, but it doesn’t cover an important use case that is somewhat specific to Coherence. Coherence is a distributed system, and it uses serialization in order to send both the data and the processing requests from one cluster member (or remote client) to another, as well as to store data, both in memory and on disk. Processing requests, such as entry processors and aggregators, are then deserialized on a target cluster member(s) in order to be executed, and in some cases they could benefit from dependency injection in order to avoid service lookups. Similarly, while the data is stored in a serialized, binary format, it may need to be deserialized into user supplied classes for server-side processing, such as when executing entry processors and aggregators, and can also benefit from dependency injection (in order to support Domain-Driven Design (DDD), for example). While these transient objects are not managed by the CDI container, Coherence CDI does support their injection after deserialization, but for performance reasons requires that you explicitly opt-in by implementing com.oracle.coherence.cdi.Injectable interface. Making transient classes Injectable While not technically a true marker interface, Injectable can be treated as such for all intents and purposes. All you need to do is add it to the implements clause of your class in order for injection on deserialization to kick in: public class InjectableBean implements Injectable, Serializable { @Inject private Converter<String, String> converter; private String text; InjectableBean() { } InjectableBean(String text) { this.text = text; } String getConvertedText() { return converter.convert(text); } } Assuming that you have the following Converter service implementation in your application, it will be injected into InjectableBean after deserialization and the getConvertedText method will return the value of the text field converted to upper case: @ApplicationScoped public class ToUpperConverter implements Converter<String, String> { @Override public String convert(String s) { return s.toUpperCase(); } } If your Injectable class has @PostConstruct callback method, it will be called after the injection. However, because we have no control over object’s lifecycle after that point, @PreDestroy callback will never be called). You should note that the above functionality is not dependent on the serialization format and will work with both Java and POF serialization (or any other custom serializer), and for any object that is deserialized on any Coherence member (or even on a remote client). While the deserialized transient objects are not true CDI managed beans, being able to inject CDI managed dependencies into them upon deserialization will likely satisfy most dependency injection requirements you will ever have in those application components. We hope you’ll find it useful. ", + "title": "Injecting CDI Beans into Transient Objects" + }, + { + "location": "/coherence-cdi/README", + "text": " In order to use Coherence CDI, you need to declare it as a dependency in your pom.xml : <dependency> <groupId>com.oracle.coherence.ce</groupId> <artifactId>coherence-cdi</artifactId> <version>${coherence.version}</version> </dependency> Once the necessary dependency is in place, you can start using CDI to inject Coherence objects into managed CDI beans, and vice versa, as the following sections describe. Injecting Coherence Objects into CDI Beans Injecting NamedCache and related objects Injecting NamedTopic and related objects Other Supported Injection Points <←cluster—​and—​operationalcontext—​injection, Cluster and OperationalContext Injection>> <←configurablecachefactory—​and—​session—​injection, ConfigurableCacheFactory and Session Injection>> <←serializer—​injection, Serializer Injection>> Injecting CDI Beans into Coherence-managed Objects Using CDI Observers to Handle Coherence Server-Side Events Using Asynchronous Observers Injecting CDI Beans into Transient Objects Making transient classes Injectable Injecting Coherence Objects into CDI Beans CDI, and dependency injection in general, make it easy for application classes to declare the dependencies they need and let the runtime provide them when necessary. This makes the applications easier to develop, test and reason about, and the code significantly cleaner. Coherence CDI allows you to do the same for Coherence objects, such as Cluster , Session , NamedCache , ContinuousQueryCache , ConfigurableCacheFactory , etc. Injecting NamedCache and related objects In order to inject an instance of a NamedCache into your CDI bean, you simply need to define an injection point for it: @Inject private NamedCache<Long, Person> people; In the example above we’ve assumed that the cache name you want to inject is the same as the name of the field you are injecting into, people . If that’s not the case, you can use @Cache qualifier to specify the name of the cache you want to obtain explicitly: @Inject @Cache(\"people\") private NamedCache<Long, Person> m_people; This is also what you have to do if you are using constructor injection instead: @Inject public MyClass(@Cache(\"people\") NamedCache<Long, Person> people) { ... } All of the examples above assume that you want to use the default ConfigurableCacheFactory , which is often, but not always the case. For example, you may have an Extend client that connects to multiple Coherence clusters, in which case you would have multiple Coherence cache config files, and multiple ConfigurableCacheFactoriy instances. In this case you would use @CacheFactory qualifier to specify the URI of the cache configuration to use: @Inject @CacheFactory(\"products-cluster.xml\") private NamedCache<Long, Product> products; @Inject @CacheFactory(\"customers-cluster.xml\") private NamedCache<Long, Customer> customers; You can replace NamedCache in any of the examples above with AsyncNamedCache in order to inject asynchronous variant of the NamedCache API: @Inject private AsyncNamedCache<Long, Person> people; You can also inject cache views , which are effectively instances of a ContinuousQueryCache , either by declaring the injection point as ContinuousQueryCache instead of NamedCache , or by simply adding @CacheView qualifier: @Inject private ContinuousQueryCache<Long, Person> people; @Inject @CacheView private NamedCache<Long, Person> people; The examples above are equivalent, and both will bring all the data from the backing cache into a local view, as they will use AlwaysFilter when constructing CQC . If you want to limit the data in the view to a subset, you can implement a custom filter binding (recommended), or use a built-in @WhereFilter for convenience, which allows you to specify a filter using CohQL: @Inject @CacheView @WhereFilter(\"gender = 'MALE'\") @Cache(\"people\") private NamedCache<Long, Person> men; @Inject @CacheView @WhereFilter(\"gender = 'FEMALE'\") @Cache(\"people\") private NamedCache<Long, Person> women; The ContinuousQueryCache , and cache views by extension, also support transformation of the cached value on the server, in order to reduce both the amount of data stored locally and the amount of data transferred over the network. For example, you may have a complex Person objects in the backing cache, but only need their names in order to populate a drop down on the client UI. In that case, you can implement a custom extractor binding (recommended), or use a built-in @PropertyExtractor for convenience: @Inject @CacheView @PropertyExtractor(\"fullName\") @Cache(\"people\") private NamedCache<Long, String> names; Note that the value type in the example above has changed from Person to String , due to server-side transformation caused by the specified @PropertyExtractor . Injecting NamedTopic and related objects In order to inject an instance of a NamedTopic into your CDI bean, you simply need to define an injection point for it: @Inject private NamedTopic<Order> orders; In the example above we’ve assumed that the topic name you want to inject is the same as the name of the field you are injecting into, in this case orders . If that’s not the case, you can use @Topic qualifier to specify the name of the cache you want to obtain explicitly: @Inject @Topic(\"orders\") private NamedTopic<Order> m_orders; This is also what you have to do if you are using constructor injection instead: @Inject public MyClass(@Topic(\"orders\") NamedTopic<Order> orders) { ... } All of the examples above assume that you want to use the default ConfigurableCacheFactory , which is often, but not always the case. For example, you may have an Extend client that connects to multiple Coherence clusters, in which case you would have multiple Coherence cache config files, and multiple ConfigurableCacheFactoriy instances. In this case you would use @CacheFactory qualifier to specify the URI of the cache configuration to use: @Inject @CacheFactory(\"payments-cluster.xml\") private NamedTopic<PaymentRequest> payments; @Inject @CacheFactory(\"shipments-cluster.xml\") private NamedTopic<ShippingRequest> shipments; The examples above allow you to inject a NamedTopic instance into your CDI bean, but it is often simpler and more convenient to inject Publisher or Subscriber for a given topic instead. This can be easily accomplished by replacing NamedTopic<T> in any of the examples above with either Publisher<T> : @Inject private Publisher<Order> orders; @Inject @Topic(\"orders\") private Publisher<Order> m_orders; @Inject @CacheFactory(\"payments-cluster.xml\") private Publisher<PaymentRequest> payments; or Subscriber<T> : @Inject private Subscriber<Order> orders; @Inject @Topic(\"orders\") private Subscriber<Order> m_orders; @Inject @CacheFactory(\"payments-cluster.xml\") private Subscriber<PaymentRequest> payments; Basically, all topic-related details, such as topic name (based on either injection point name or the explicit name from @Topic annotation), cache factory URI and message type, will be used under the hood to retrieve the NamedTopic , and to obtain Publisher or Subscriber from it. Additionally, if you want to place your Subscriber s into a subscriber group, you can easily accomplish that by adding @SubscriberGroup qualifier to the injection point: @Inject @SubscriberGroup(\"orders-queue\") private Subscriber<Order> orders; Other Supported Injection Points While the injection of a NamedCache , NamedTopic , and related instances, as shown above, is probably the single most useful feature of Coherence CDI, it is certainly not the only one. The following sections describe other Coherence artifacts that can be injected using Coherence CDI. Cluster and OperationalContext Injection If you need an instance of a Cluster interface somewhere in your application, you can easily obtain it via injection: @Inject private Cluster cluster; You can do the same if you need an instance of an OperationalContext : @Inject private OperationalContext ctx; ConfigurableCacheFactory and Session Injection On rare occasions when you need to use either of these directly, Coherence CDI makes it trivial to do so. To obtain an instance of a default CCF or Session , all you need to do is inject them into the class that needs to use them: @Inject private ConfigurableCacheFactory ccf; @Inject private Session session; If you need a specific CCF or Session you can simply qualify them using @CacheFactory qualifier and specifying the URI of the cache config file to use: @Inject @CacheFactory(\"my-cache-config.xml\") private ConfigurableCacheFactory ccf; @Inject @CacheFactory(\"my-cache-config.xml\") private Session session; Serializer Injection While in most cases you won’t have to deal with serializers directly, Coherence CDI makes it simple to obtain named serializers (and to register new ones) when you need. To get a default Serializer for the current context class loader, you can simply inject it: @Inject private Serializer defaultSerializer; However, it may be more useful to inject one of the named serializers defined in the operational configuration, which can be easily accomplished using @SerializerFormat qualifier: @Inject @SerializerFormat(\"java\") private Serializer javaSerializer; @Inject @SerializerFormat(\"pof\") private Serializer pofSerializer; In addition to the serializers defined in the operational config, the example above will also perform BeanManager lookup for a named bean that implements Serializer interface. That means that if you implemented a custom Serializer bean, such as: @Named(\"json\") @ApplicationScoped public class JsonSerializer implements Serializer { ... } it would be automatically discovered and registered by the CDI, and you would then be able to inject it just as easily as the named serializers defined in the operational config: @Inject @SerializerFormat(\"json\") private Serializer jsonSerializer; Injecting CDI Beans into Coherence-managed Objects Coherence has a number of server-side extension points, which allow users to customize application behavior in different ways, typically by configuring their extensions within various sections of the cache configuration file. For example, the users can implement event interceptors and cache stores, in order to handle server-side events and integrate with the external data stores and other services. Coherence CDI provides a way to inject named CDI beans into these extension points using custom configuration namespace handler. <cache-config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://xmlns.oracle.com/coherence/coherence-cache-config\" xmlns:cdi=\"class://com.oracle.coherence.cdi.CdiNamespaceHandler\" xsi:schemaLocation=\"http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd\"> Once you’ve declared the handler for the cdi namespace above, you can specify <cdi:bean> element in any place where you would normally use <class-name> or <class-factory-name> elements: <?xml version=\"1.0\"?> <cache-config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://xmlns.oracle.com/coherence/coherence-cache-config\" xmlns:cdi=\"class://com.oracle.coherence.cdi.CdiNamespaceHandler\" xsi:schemaLocation=\"http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd\"> <interceptors> <interceptor> <instance> <cdi:bean>registrationListener</cdi:bean> </instance> </interceptor> <interceptor> <instance> <cdi:bean>activationListener</cdi:bean> </instance> </interceptor> </interceptors> <caching-scheme-mapping> <cache-mapping> <cache-name>*</cache-name> <scheme-name>distributed-scheme</scheme-name> <interceptors> <interceptor> <instance> <cdi:bean>cacheListener</cdi:bean> </instance> </interceptor> </interceptors> </cache-mapping> </caching-scheme-mapping> <caching-schemes> <distributed-scheme> <scheme-name>distributed-scheme</scheme-name> <service-name>PartitionedCache</service-name> <local-storage system-property=\"coherence.distributed.localstorage\">true</local-storage> <partition-listener> <cdi:bean>partitionListener</cdi:bean> </partition-listener> <member-listener> <cdi:bean>memberListener</cdi:bean> </member-listener> <backing-map-scheme> <local-scheme/> </backing-map-scheme> <autostart>true</autostart> <interceptors> <interceptor> <instance> <cdi:bean>storageListener</cdi:bean> </instance> </interceptor> </interceptors> </distributed-scheme> </caching-schemes> </cache-config> Note that you can only inject named CDI beans (beans with an explicit @Named annotations) via <cdi:bean> element. For example, the cacheListener interceptor bean used above would look similar to this: @ApplicationScoped @Named(\"cacheListener\") @EntryEvents(INSERTING) public class MyCacheListener implements EventInterceptor<EntryEvent<Long, String>> { @Override public void onEvent(EntryEvent<Long, String> e) { // handle INSERTING event } } Also keep in mind that only @ApplicationScoped beans can be injected, which implies that they may be shared. For example, because we’ve used wildcard * as a cache name within the cache mapping in the example above, the same instance of cacheListener will receive events from multiple caches. This is typically fine, as the event itself provides the details about the context that raised it, including cache name and the service it was raised from, but it does imply that any shared state that you may have within your listener class shouldn’t be context-specific and it must be safe for concurrent access from multiple threads. If you can’t guarantee the latter, you may want to declare the onEvent method as synchronized , to ensure only one thread at a time can access any shared state you may have. Using CDI Observers to Handle Coherence Server-Side Events While the above examples show that you can implement any Coherence EventInterceptor as a CDI bean and register it using <cdi:bean> element within the cache configuration file, Coherence CDI also provides a much simpler way to accomplish the same goal using standard CDI Events and Observers. The first thing you need to do is register a single global interceptor within the cache config: <cache-config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:cdi=\"class://com.oracle.coherence.cdi.CdiNamespaceHandler\" xmlns=\"http://xmlns.oracle.com/coherence/coherence-cache-config\" xsi:schemaLocation=\"http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd\"> <interceptors> <interceptor> <instance> <cdi:bean>com.oracle.coherence.cdi.EventDispatcher</cdi:bean> </instance> </interceptor> </interceptors> <!-- the rest of cache config as usual --> </cache-config> Coherence CDI EventDispatcher bean will then listen to all events raised by all Coherence event dispatchers and re-raise them as CDI events that you can observe. For example, to implement the equivalent of cacheListener interceptor above, you would simply define an observer method in any CDI bean that wants to be notified when the event happens: private void onInserting(@Observes @Inserting EntryEvent<?, ?> event) { // handle INSERTING event on any cache } The observer method above will receive all INSERTING events for all the caches, across all the services, but you can use CDI qualifiers to control that behavior: private void onInserting(@Observes @Updated @CacheName(\"people\") EntryEvent<?, ?> event) { // handle UPDATED event on 'people' cache only } private void onRemoved(@Observes @Removed @ServiceName(\"Products\") EntryEvent<?, ?> event) { // handle REMOVED event on any cache on the 'Products' service } Of course, you can also remove qualifiers to broaden the scope of events your handler receives: private void onEntryEvent(@Observes EntryEvent<?, ?> event) { // handle any event on any cache } The examples above show only how to handle EntryEvent s, but the same applies to all other event types: private void onActivated(@Observes @Activated LifecycleEvent event) { // handle cache factory activation } private void onCreatedPeople(@Observes @Created @CacheName(\"people\") CacheLifecycleEvent event) { // handle creation of the 'people' cache } private void onExecuting(@Observes @Executing @CacheName(\"people\") @Processor(Uppercase.class) EntryProcessorEvent event) { // intercept 'Uppercase` entry processor execution against 'people' cache } And again, you can broaden the scope by widening the type of events you observe: private void onPartitionedCacheEvent(@Observes com.tangosol.net.events.partition.cache.Event<?> event) { // handle any/all events raised by the partitioned cache service (CacheLifecycleEvent, EntryEvent or EntryProcessorEvent) // can use @CacheName and @ServiceName as a narrowing qualifier } private void onPartitionedServiceEvent(@Observes com.tangosol.net.events.partition.Event<?> event) { // handle any/all events raised by the partitioned service (TransactionEvent, TransferEvent or UnsolicitedCommitEvent) // can use @ServiceName as a narrowing qualifier } private void onEvent(@Observes com.tangosol.net.events.Event<?> event) { // handle any/all events (all of the above, plus LifecycleEvent) } Using Asynchronous Observers All of the examples above used synchronous observers by specifying @Observes qualifier for each observer method. However, Coherence CDI fully supports asynchronous CDI observers as well. All you need to do is replace @Observes with @ObservesAsync in any of the examples above. private void onActivated(@ObservesAsync @Activated LifecycleEvent event) { // handle cache factory activation } private void onCreatedPeople(@ObservesAsync @Created @CacheName(\"people\") CacheLifecycleEvent event) { // handle creation of the 'people' cache } private void onExecuting(@ObservesAsync @Executing @CacheName(\"people\") @Processor(Uppercase.class) EntryProcessorEvent event) { // intercept 'Uppercase` entry processor execution against 'people' cache } However, there is an important caveat. Coherence events fall into two categories: pre- and post-commit events. All of the events whose name ends with ing , such as Inserting , Updating , Removing or Executing are pre-commit, which means that they can either modify the data or even completely cancel the operation by throwing an exception, but in order to do so they must be synchronous to ensure that they are executed on the same thread that is executing the operation that triggered the event. That means that you can observe them using asynchronous CDI observers, but if you want to mutate the set of entries that are part of the event payload, or veto the event by throwing an exception, you must use synchronous CDI observer. Injecting CDI Beans into Transient Objects Using CDI to inject Coherence objects into your application classes, and CDI beans into Coherence-managed objects will allow you to support many use cases where dependency injection may be useful, but it doesn’t cover an important use case that is somewhat specific to Coherence. Coherence is a distributed system, and it uses serialization in order to send both the data and the processing requests from one cluster member (or remote client) to another, as well as to store data, both in memory and on disk. Processing requests, such as entry processors and aggregators, are then deserialized on a target cluster member(s) in order to be executed, and in some cases they could benefit from dependency injection in order to avoid service lookups. Similarly, while the data is stored in a serialized, binary format, it may need to be deserialized into user supplied classes for server-side processing, such as when executing entry processors and aggregators, and can also benefit from dependency injection (in order to support Domain-Driven Design (DDD), for example). While these transient objects are not managed by the CDI container, Coherence CDI does support their injection after deserialization, but for performance reasons requires that you explicitly opt-in by implementing com.oracle.coherence.cdi.Injectable interface. Making transient classes Injectable While not technically a true marker interface, Injectable can be treated as such for all intents and purposes. All you need to do is add it to the implements clause of your class in order for injection on deserialization to kick in: public class InjectableBean implements Injectable, Serializable { @Inject private Converter<String, String> converter; private String text; InjectableBean() { } InjectableBean(String text) { this.text = text; } String getConvertedText() { return converter.convert(text); } } Assuming that you have the following Converter service implementation in your application, it will be injected into InjectableBean after deserialization and the getConvertedText method will return the value of the text field converted to upper case: @ApplicationScoped public class ToUpperConverter implements Converter<String, String> { @Override public String convert(String s) { return s.toUpperCase(); } } If your Injectable class has @PostConstruct callback method, it will be called after the injection. However, because we have no control over object’s lifecycle after that point, @PreDestroy callback will never be called). You should note that the above functionality is not dependent on the serialization format and will work with both Java and POF serialization (or any other custom serializer), and for any object that is deserialized on any Coherence member (or even on a remote client). While the deserialized transient objects are not true CDI managed beans, being able to inject CDI managed dependencies into them upon deserialization will likely satisfy most dependency injection requirements you will ever have in those application components. We hope you’ll find it useful. ", + "title": "Usage" + }, + { + "location": "/coherence-mp/README", + "text": " Coherence provides a number of additional modules that provide support for different Microprofile APIs. Microprofile Config Using Coherence as a Microprofile config source. Microprofile Metrics Configure Coherence to publish metrics via the Microprofile metrics API. ", + "title": "Coherence MP" + }, + { + "location": "/coherence-mp/metrics/README", + "text": " Coherence MP Metrics provides support for [Eclipse MicroProfile Metrics] ( https://microprofile.io/project/eclipse/microprofile-metrics ) within Coherence cluster members. This is a very simple module that allows you to publish Coherence metrics into MicroProfile Metric Registries available at runtime, and adds Coherence-specific tags to all the metrics published within the process, in order to distinguish them on the monitoring server, such as Prometheus. ", + "title": "Coherence MicroProfile Metrics" + }, + { + "location": "/coherence-mp/metrics/README", + "text": " In order to use Coherence MP Metrics, you need to declare it as a dependency in your pom.xml : <dependency> <groupId>com.oracle.coherence.ce</groupId> <artifactId>coherence-mp-metrics</artifactId> <version>${coherence.version}</version> </dependency> That’s it — once the module above is in the class path, Coherence will discover MpMetricRegistryAdapter service it provides, and use it to publish all standard Coherence metrics to the vendor registry, and any user-defined application metrics to the application registry. All the metrics will be published as gauges, because they represent point-in-time values of various MBean attributes. ", + "title": "Usage" + }, + { + "location": "/coherence-mp/metrics/README", + "text": " There could be hundreds of members in a Coherence cluster, with each member publishing potentially the same set of metrics. There could also be many Coherence clusters in the environment, possibly publishing to the same monitoring server instance. In order to distinguish metrics coming from different clusters, as well as from different members of the same cluster, Coherence MP Metrics will automatically add several tags to ALL the metrics published within the process. The tags added are: Tag Name Tag Value cluster the cluster name site the site the member belongs to (if set) machine the machine member is on (if set) member the name of the member (if set) node_id the node ID of the member role the member’s role This ensures that the metrics published by one member do not collide with and overwrite the metrics published by another members, and allows you to query and aggregate metrics based on the values of the tags above if desired. ", + "title": "Coherence Global Tags" + }, + { + "location": "/coherence-grpc-proxy/README", + "text": "", + "title": "Coherence gRPC Server" + }, + { + "location": "/docs/README", + "text": " To build the docs, run the following Maven command from the top-level prj/ directory: mvn clean install -DskipTests -P docs -pl docs ", + "title": "Build the Docs" + }, + { + "location": "/docs/README", + "text": " To view the documentation after building it, run the following command from the top-level prj/ directory: mvn exec:exec -P docs -pl docs The installation requires you to install Python, which runs a small Pythin http server from the directory where the docs are built. ", + "title": "View the Docs" + }, + { + "location": "/docs/README", + "text": " This is the module that builds the Coherence documentation. The module is not part of the default build and must be built separately. Build the Docs To build the docs, run the following Maven command from the top-level prj/ directory: mvn clean install -DskipTests -P docs -pl docs View the Docs To view the documentation after building it, run the following command from the top-level prj/ directory: mvn exec:exec -P docs -pl docs The installation requires you to install Python, which runs a small Pythin http server from the directory where the docs are built. ", + "title": "Coherence Documentation Module" + }, + { + "location": "/coherence-mp/config/README", + "text": " Coherence MP Config provides support for [Eclipse MicroProfile Config] ( https://microprofile.io/project/eclipse/microprofile-config ) within Coherence cluster members. It allows you both to configure various Coherence parameters from the values specified in any of the supported config sources, and to use Coherence cache as another, mutable config source. ", + "title": "Coherence MicroProfile Config" + }, + { + "location": "/coherence-mp/config/README", + "text": " In order to use Coherence MP Config, you need to declare it as a dependency in your pom.xml : <dependency> <groupId>com.oracle.coherence.ce</groupId> <artifactId>coherence-mp-config</artifactId> <version>${coherence.version}</version> </dependency> You will also need an implementation of the Eclipse MP Config specification as a dependency. For example, if you are using Helidon , add the following to your pom.xml : <dependency> <groupId>io.helidon.microprofile.config</groupId> <artifactId>helidon-microprofile-config</artifactId> <version>${helidon.version}</version> </dependency> <!-- optional: add it if you want YAML config file support --> <dependency> <groupId>io.helidon.config</groupId> <artifactId>helidon-config-yaml</artifactId> <version>${helidon.version}</version> </dependency> ", + "title": "Usage" + }, + { + "location": "/coherence-mp/config/README", + "text": " Coherence provides a number of configuration properties that can be specified by the users in order to define certain attributes or to customize cluster member behavior at runtime. For example, attributes such as cluster and role name, as well as whether a cluster member should or should not store data, can be specified via system properties: -Dcoherence.cluster=MyCluster -Dcoherence.role=Proxy -Dcoherence.distributed.localstorage=false Most of these attributes can also be defined within the operational or cache configuration file. For example, you could define first two attributes, cluster name and role, within the operational config override file: <cluster-config> <member-identity> <cluster-name>MyCluster</cluster-name> <role-name>Proxy</role-name> </member-identity> </cluster-config> While these two options are more than enough in most cases, there are some issues with them being the only way to configure Coherence: When you are using one of Eclipse MicroProfile implementations, such as Helidon as the foundation of your application, it would be nice to define some of Coherence configuration parameters along with your other configuration parameters, and not in the separate file or via system properties. In some environments, such as Kubernetes, Java system properties are cumbersome to use, and environment variables are a preferred way of passing configuration properties to containers. Unfortunately, neither of the two use cases above is supported out of the box, but that’s the gap Coherence MP Config is designed to fill. As long as you have coherence-mp-config and an implementation of Eclipse MP Config specification to your class path, Coherence will use any of the standard or custom config sources to resolve various configuration options it understands. Standard config sources in MP Config include META-INF/microprofile-config.properties file, if present in the class path, environment variables, and system properties (in that order, with the properties in the latter overriding the ones from the former). That will directly address problem #2 above, and allow you to specify Coherence configuration options via environment variables within Kubernetes YAML files, for example: containers: - name: my-app image: my-company/my-app:1.0.0 env: - name: COHERENCE_CLUSTER value: \"MyCluster\" - name: COHERENCE_ROLE value: \"Proxy\" - name: COHERENCE_DISTRIBUTED_LOCALSTORAGE value: \"false\" Of course, the above is just an example — if you are running your Coherence cluster in Kubernetes, you should really be using Coherence Operator instead, as it will make both the configuration and the operation of your Coherence cluster much easier. You will also be able to specify Coherence configuration properties along with the other configuration properties of your application, which will allow you to keep everything in one place, and not scattered across many files. For example, if you are writing a Helidon application, you can simply add coherence section to your application.yaml : coherence: cluster: MyCluster role: Proxy distributed: localstorage: false ", + "title": "Configuring Coherence using MP Config" + }, + { + "location": "/coherence-mp/config/README", + "text": " Coherence MP Config also provides an implementation of Eclipse MP Config ConfigSource interface, which allows you to store configuration parameters in a Coherence cache. This has several benefits: Unlike pretty much all of the default configuration sources, which are static, configuration options stored in a Coherence cache can be modified without forcing you to rebuild your application JARs or Docker images. You can change the value in one place, and it will automatically be visible and up to date on all the members. While the features above give you incredible amount of flexibility, we also understand that such flexibility is not always desired, and the feature is disabled by default. If you want to enable it, you need to do so explicitly, by registering CoherenceConfigSource as a global interceptor in your cache configuration file: <cache-config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://xmlns.oracle.com/coherence/coherence-cache-config\" xsi:schemaLocation=\"http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd\"> <interceptors> <interceptor> <instance> <class-name>com.oracle.coherence.mp.config.CoherenceConfigSource</class-name> </instance> </interceptor> </interceptors> <!-- your cache mappings and schemes... --> </cache-config> Once you do that, CoherenceConfigSource will be activated as soon as your cache factory is initialized, and injected into the list of available config sources for your application to use via standard MP Config APIs. By default, it will be configured with a priority (ordinal) of 500, making it higher priority than all the standard config sources, thus allowing you to override the values provided via config files, environment variables and system properties. However, you have full control over that behavior and can specify different ordinal via coherence.mp.config.source.ordinal configuration property. ", + "title": "Using Coherence Cache as a Config Source" + }, + { + "location": "/docs/about/01_overview", + "text": " Coherence is scalable, fault-tolerant, cloud-ready, distributed platform for building grid-based applications and reliably storing data. The product is used at scale, for both compute and raw storage, in a vast array of industries such as critical financial trading systems, high performance telecommunication products, and eCommerce applications. Typically, these deployments do not tolerate any downtime and Coherence is chosen due its novel features in death detection, application data evolvability, and the robust, battle-hardened core of the product that enables it to be seamlessly deployed and adapted within any ecosystem. At a high level, Coherence provides an implementation of the familiar Map<K,V> interface but rather than storing the associated data in the local process, it is partitioned (or sharded) across a number of designated remote nodes. This partitioning enables applications to not only distribute (and therefore scale) their storage across multiple processes, machines, racks, and data centers, but also to perform grid-based processing to truly harness the CPU resources of the machines. The Coherence interface 'NamedCache<K,V>' (an extension of 'Map<K,V>' provides methods to query, aggregate (map/reduce style), and compute (send functions to storage nodes for locally executed mutations) the data set. These capabilities, in addition to numerous other features, enable Coherence to be used as a framework to write robust, distributed applications. ", + "title": "Overview" + }, + { + "location": "/docs/about/01_overview", + "text": " explore Coherence What is Oracle Coherence? fa-rocket Quick Start A quick-start guide to using Coherence. import_contacts Docs Oracle Coherence commercial edition product documentation. library_books API Docs Browse the Coherence CE API Docs. fa-th Images Example Coherence OCI container (Docker) images. ", + "title": "Get Going" + }, + { + "location": "/docs/about/02_introduction", + "text": " First and foremost, Coherence provides a fundamental service that is responsible for all facets of clustering and is a common denominator / building block for all other Coherence services. This service, referred to as 'service 0' internally, ensures that the mesh of members is maintained and responsive, taking action to collaboratively evict, shun, or in some cases, voluntarily depart the cluster when deemed necessary. As members join and leave the cluster, other Coherence services are notified, thus enabling those services to react accordingly. This part of the Coherence product has been in production for more than 10 years, being the subject of some extensive and imaginative testing. While this feature has been discussed here, it certainly is not something that customers, generally, interact with directly, but is important to be aware of. Coherence services build on top of the clustering service. The key implementations to be aware of are PartitionedService, InvocationService, and ProxyService. In the majority of cases, customers will deal with caches; a cache is represented by an implementation of NamedCache<K,V> . Cache is an unfortunate name, as many Coherence customers use Coherence as a system-of-record rather than a lossy store of data. A cache is hosted by a service, generally the PartitionedService, and is the entry point to store, retrieve, aggregate, query, and stream data. Caches provide a number of features: Fundamental key-based access : get/put getAll/putAll. Client-side and storage-side events: MapListeners to asynchronously notify clients of changes to data. EventInterceptors (either sync or async) to notify storage level events, including mutations, partition transfer, failover, and so on. NearCaches - Locally cached data based on previous requests with local content invalidated upon changes in the storage tier. ViewCaches - Locally stored view of remote data that can be a subset based on a predicate and is kept in sync, real time. Queries - Distributed, parallel query evaluation to return matching key, values, or entries with potential to optimize performance with indices. Aggregations - A map/reduce style aggregation where data is aggregated in parallel on all storage nodes, and results streamed back to the client for aggregation of those results to produce a final result. Data local processing - Ability to send a function to the relevant storage node to execute processing logic for the appropriate entries with exclusive access. Partition local transactions - Ability to perform scalable transactions by associating data (thus being on the same partition) and manipulating other entries on the same partition, potentially across caches. Non-blocking / async NamedCache API C++ and .NET clients - Access the same NamedCache API from either C++ or .NET. Portable Object Format - Optimized serialization format, with the ability to navigate the serialized form for optimized queries, aggregations, or data processing. Integration with Databases - Database and third party data integration with CacheStores, including synchronous or asynchronous writes. CohQL - Ansi-style query language with a console for adhoc queries. Topics - Distributed topics implementation that offers pub/sub messaging with the storage capacity, the cluster, and parallelizable subscribers. Coherence also provides a number of non-functional features: Rock solid clustering - Highly tuned and robust clustering stack that enables Coherence to scale to thousands of members in a cluster with thousands of partitions and terabytes of data being accessed, mutated, queried, and aggregated concurrently Safety first - Resilient data management that ensures backup copies are on distinct machines, racks, or sites, and the ability to maintain multiple backups. 24/7 Availability - Zero downtime with rolling redeployment of cluster members to upgrade application or product versions. Backward and forward compatibility of product upgrades, including major versions. Persistent Caches - Ability to use local file system persistence (thus avoid extra network hops) and leverage Coherence consensus protocols to perform distributed disk recovery when appropriate. Distributed State Snapshot - Ability to perform distributed point-in-time snapshot of cluster state, and recover snapshot in this or a different cluster (leverages persistence feature). Lossy redundancy - Ability to reduce the redundancy guarantee by making backups and/or persistence asynchronous from a client perspective. Single Mangement View - Provides insight into the cluster with a single JMX server that provides a view of all members of the cluster. Management over REST - All JMX data and operations can be performed over REST, including cluster wide thread dumps and heapdumps. Non-cluster Access - Provides access to the cluster from the outside via proxies, for distant (high latency) clients and for non-java languages such as C++ and .NET. Kubernetes friendly - Enables seemless and safe deployment of applications to k8s with our own operator . ", + "title": "Introduction" + }, + { + "location": "/docs/about/02_introduction", + "text": " The following Oracle Coherence features are not included in Coherence Community Edition: Management of Coherence via the Oracle WebLogic Management Framework WebLogic Server Multitenancy Support Deployment of Grid Archives (GARs) HTTP Session Management for Application Servers (Coherence*Web) GoldenGate HotCache TopLink-based CacheLoaders and CacheStores Elastic Data Federation and WAN (wide area network) Support Transaction Framework CommonJ Work Manager ", + "title": "Features Not Included in Coherence Community Edition" + } + ] +} \ No newline at end of file diff --git a/14.1.1-0-20/docs/main/utils.js b/14.1.1-0-20/docs/main/utils.js new file mode 100644 index 0000000000000..b791eaf0b41e6 --- /dev/null +++ b/14.1.1-0-20/docs/main/utils.js @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * + * 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. + */ + +/* global superagent, Vue */ + +function createPageTemplateElt(id, text){ + if(document.getElementById(id) === null){ + const scriptElt = document.createElement("script"); + scriptElt.id = id; + scriptElt.type = "text/x-template"; + scriptElt.text = text; + document.body.appendChild(scriptElt); + } +} + +function createPageCustomElt(id, src, onload){ + if(document.getElementById(id) === null){ + const scriptElt = document.createElement("script"); + scriptElt.id = id; + scriptElt.src = src; + scriptElt.onload = onload; + document.body.appendChild(scriptElt); + } +} + +function loadPage(id, targetPath, compDef, customJsPath) { + if (compDef === undefined) { + compDef = {}; + } + compDef.template = "#" + id; + return Vue.component(id, () => { + return new Promise(function (resolve, reject) { + + // template exists in the dom + if (document.getElementById(id) !== null) { + resolve(compDef); + return; + } + + // download the template + const page = "pages" + targetPath + ".js"; + superagent.get(page).end(function (error, response) { + + // error loading page + if (error) { + reject("load of " + page + " failed" + error); + return; + } + + // resolve template only + if(customJsPath === undefined){ + createPageTemplateElt(id, response.text); + resolve(compDef); + return; + } + + // resolve template and custom bindings + createPageCustomElt(id + "_custom", "pages/" + customJsPath, function(){ + const custom = window.allCustoms[id]; + if(custom.methods !== undefined){ + compDef.methods = custom.methods; + } + if(custom.data !== undefined){ + compDef.data = custom.data; + } + createPageTemplateElt(id, response.text); + resolve(compDef); + }); + }); + }); + }); +} + +function capitalize(src) { + return `${src.charAt(0).toUpperCase()}${src.slice(1)}`; +} \ No newline at end of file diff --git a/14.1.1-0-20/docs/pages/coherence-cdi/README.js b/14.1.1-0-20/docs/pages/coherence-cdi/README.js new file mode 100644 index 0000000000000..2ebb47c9eda02 --- /dev/null +++ b/14.1.1-0-20/docs/pages/coherence-cdi/README.js @@ -0,0 +1,692 @@ + + +

Coherence CDI

+
+

Coherence CDI provides support for CDI (Contexts and Dependency Injection) within Coherence cluster members.

+ +

It allows you both to inject Coherence-managed resources, such as NamedCache and Session instances into CDI managed beans, and to inject CDI beans into Coherence-managed resources, such as event interceptors and cache stores, and to handle Coherence server-side events using CDI observer methods.

+ +

In addition, Coherence CDI provides support for automatic injection of transient objects upon deserialization. +This allows you to inject CDI managed beans such as services and repositories (to use DDD nomenclature) into transient objects, such as entry processor and even data class instances, greatly simplifying implementation of true Domain Driven applications.

+ +
+ +

Usage

+
+

In order to use Coherence CDI, you need to declare it as a dependency in your pom.xml:

+ + <dependency> + <groupId>com.oracle.coherence.ce</groupId> + <artifactId>coherence-cdi</artifactId> + <version>${coherence.version}</version> + </dependency> + +

Once the necessary dependency is in place, you can start using CDI to inject Coherence objects into managed CDI beans, and vice versa, as the following sections describe.

+ +
    +
  • +

    Injecting Coherence Objects into CDI Beans

    +
      +
    • +

      Injecting NamedCache and related objects

      + +
    • +
    • +

      Injecting NamedTopic and related objects

      + +
    • +
    • +

      Other Supported Injection Points

      +
        +
      • +

        <←cluster—​and—​operationalcontext—​injection,Cluster and OperationalContext Injection>>

        + +
      • +
      • +

        <←configurablecachefactory—​and—​session—​injection,ConfigurableCacheFactory and Session Injection>>

        + +
      • +
      • +

        <←serializer—​injection,Serializer Injection>>

        + +
      • +
      +
    • +
    +
  • +
  • +

    Injecting CDI Beans into Coherence-managed Objects

    +
      +
    • +

      Using CDI Observers to Handle Coherence Server-Side Events

      +
        +
      • +

        Using Asynchronous Observers

        + +
      • +
      +
    • +
    +
  • +
  • +

    Injecting CDI Beans into Transient Objects

    +
      +
    • +

      Making transient classes Injectable

      + +
    • +
    +
  • +
+ +

Injecting Coherence Objects into CDI Beans

+
+

CDI, and dependency injection in general, make it easy for application classes to declare the dependencies they need and let the runtime provide them when necessary. +This makes the applications easier to develop, test and reason about, and the code significantly cleaner.

+ +

Coherence CDI allows you to do the same for Coherence objects, such as Cluster, Session, NamedCache, ContinuousQueryCache, ConfigurableCacheFactory, etc.

+ + + +
+

In order to inject an instance of a NamedCache into your CDI bean, you simply need to define an injection point for it:

+ +@Inject +private NamedCache<Long, Person> people; + +

In the example above we’ve assumed that the cache name you want to inject is the same as the name of the field you are injecting into, people. +If that’s not the case, you can use @Cache qualifier to specify the name of the cache you want to obtain explicitly:

+ +@Inject +@Cache("people") +private NamedCache<Long, Person> m_people; + +

This is also what you have to do if you are using constructor injection instead:

+ +@Inject +public MyClass(@Cache("people") NamedCache<Long, Person> people) { + ... +} + +

All of the examples above assume that you want to use the default ConfigurableCacheFactory, which is often, but not always the case. +For example, you may have an Extend client that connects to multiple Coherence clusters, in which case you would have multiple Coherence cache config files, and multiple ConfigurableCacheFactoriy instances.

+ +

In this case you would use @CacheFactory qualifier to specify the URI of the cache configuration to use:

+ +@Inject +@CacheFactory("products-cluster.xml") +private NamedCache<Long, Product> products; + +@Inject +@CacheFactory("customers-cluster.xml") +private NamedCache<Long, Customer> customers; + +

You can replace NamedCache in any of the examples above with AsyncNamedCache in order to inject asynchronous variant of the NamedCache API:

+ +@Inject +private AsyncNamedCache<Long, Person> people; + +

You can also inject cache views, which are effectively instances of a ContinuousQueryCache, either by declaring the injection point as ContinuousQueryCache instead of NamedCache, or by simply adding @CacheView qualifier:

+ +@Inject +private ContinuousQueryCache<Long, Person> people; + +@Inject +@CacheView +private NamedCache<Long, Person> people; + +

The examples above are equivalent, and both will bring all the data from the backing cache into a local view, as they will use AlwaysFilter when constructing CQC. +If you want to limit the data in the view to a subset, you can implement a custom filter binding (recommended), or use a built-in @WhereFilter for convenience, which allows you to specify a filter using CohQL:

+ +@Inject +@CacheView +@WhereFilter("gender = 'MALE'") +@Cache("people") +private NamedCache<Long, Person> men; + +@Inject +@CacheView +@WhereFilter("gender = 'FEMALE'") +@Cache("people") +private NamedCache<Long, Person> women; + +

The ContinuousQueryCache, and cache views by extension, also support transformation of the cached value on the server, in order to reduce both the amount of data stored locally and the amount of data transferred over the network. +For example, you may have a complex Person objects in the backing cache, but only need their names in order to populate a drop down on the client UI.

+ +

In that case, you can implement a custom extractor binding (recommended), or use a built-in @PropertyExtractor for convenience:

+ +@Inject +@CacheView +@PropertyExtractor("fullName") +@Cache("people") +private NamedCache<Long, String> names; + +

Note that the value type in the example above has changed from Person to String, due to server-side transformation caused by the specified @PropertyExtractor.

+ +
+ + +
+

In order to inject an instance of a NamedTopic into your CDI bean, you simply need to define an injection point for it:

+ +@Inject +private NamedTopic<Order> orders; + +

In the example above we’ve assumed that the topic name you want to inject is the same as the name of the field you are injecting into, in this caseorders. +If that’s not the case, you can use @Topic qualifier to specify the name of the cache you want to obtain explicitly:

+ +@Inject +@Topic("orders") +private NamedTopic<Order> m_orders; + +

This is also what you have to do if you are using constructor injection instead:

+ +@Inject +public MyClass(@Topic("orders") NamedTopic<Order> orders) { + ... +} + +

All of the examples above assume that you want to use the default ConfigurableCacheFactory, which is often, but not always the case. +For example, you may have an Extend client that connects to multiple Coherence clusters, in which case you would have multiple Coherence cache config files, and multiple ConfigurableCacheFactoriy instances.

+ +

In this case you would use @CacheFactory qualifier to specify the URI of the cache configuration to use:

+ +@Inject +@CacheFactory("payments-cluster.xml") +private NamedTopic<PaymentRequest> payments; + +@Inject +@CacheFactory("shipments-cluster.xml") +private NamedTopic<ShippingRequest> shipments; + +

The examples above allow you to inject a NamedTopic instance into your CDI bean, but it is often simpler and more convenient to inject Publisher or Subscriber for a given topic instead.

+ +

This can be easily accomplished by replacing NamedTopic<T> in any of the examples above with either Publisher<T>:

+ +@Inject +private Publisher<Order> orders; + +@Inject +@Topic("orders") +private Publisher<Order> m_orders; + +@Inject +@CacheFactory("payments-cluster.xml") +private Publisher<PaymentRequest> payments; + +

or Subscriber<T>:

+ +@Inject +private Subscriber<Order> orders; + +@Inject +@Topic("orders") +private Subscriber<Order> m_orders; + +@Inject +@CacheFactory("payments-cluster.xml") +private Subscriber<PaymentRequest> payments; + +

Basically, all topic-related details, such as topic name (based on either injection point name or the explicit name from @Topic annotation), cache factory URI and message type, will be used under the hood to retrieve the NamedTopic, and to obtain Publisher or Subscriber from it.

+ +

Additionally, if you want to place your Subscribers into a subscriber group, you can easily accomplish that by adding @SubscriberGroup qualifier to the injection point:

+ +@Inject +@SubscriberGroup("orders-queue") +private Subscriber<Order> orders; + +
+ +

Other Supported Injection Points

+
+

While the injection of a NamedCache, NamedTopic, and related instances, as shown above, is probably the single most useful feature of Coherence CDI, it is certainly not the only one. +The following sections describe other Coherence artifacts that can be injected using Coherence CDI.

+ + +
Cluster and OperationalContext Injection
+
+

If you need an instance of a Cluster interface somewhere in your application, you can easily obtain it via injection:

+ +@Inject +private Cluster cluster; + +

You can do the same if you need an instance of an OperationalContext:

+ +@Inject +private OperationalContext ctx; + +
+ +
ConfigurableCacheFactory and Session Injection
+
+

On rare occasions when you need to use either of these directly, Coherence CDI makes it trivial to do so.

+ +

To obtain an instance of a default CCF or Session, all you need to do is inject them into the class that needs to use them:

+ +@Inject +private ConfigurableCacheFactory ccf; + +@Inject +private Session session; + +

If you need a specific CCF or Session you can simply qualify them using @CacheFactory qualifier and specifying the URI of the cache config file to use:

+ +@Inject +@CacheFactory("my-cache-config.xml") +private ConfigurableCacheFactory ccf; + +@Inject +@CacheFactory("my-cache-config.xml") +private Session session; + +
+ +
Serializer Injection
+
+

While in most cases you won’t have to deal with serializers directly, Coherence CDI makes it simple to obtain named serializers (and to register new ones) when you need.

+ +

To get a default Serializer for the current context class loader, you can simply inject it:

+ +@Inject +private Serializer defaultSerializer; + +

However, it may be more useful to inject one of the named serializers defined in the operational configuration, which can be easily accomplished using @SerializerFormat qualifier:

+ +@Inject +@SerializerFormat("java") +private Serializer javaSerializer; + +@Inject +@SerializerFormat("pof") +private Serializer pofSerializer; + +

In addition to the serializers defined in the operational config, the example above will also perform BeanManager lookup for a named bean that implements Serializer interface.

+ +

That means that if you implemented a custom Serializer bean, such as:

+ +@Named("json") +@ApplicationScoped +public class JsonSerializer implements Serializer { + ... +} + +

it would be automatically discovered and registered by the CDI, and you would then be able to inject it just as easily as the named serializers defined in the operational config:

+ +@Inject +@SerializerFormat("json") +private Serializer jsonSerializer; + +
+
+
+ +

Injecting CDI Beans into Coherence-managed Objects

+
+

Coherence has a number of server-side extension points, which allow users to customize application behavior in different ways, typically by configuring their extensions within various sections of the cache configuration file. +For example, the users can implement event interceptors and cache stores, in order to handle server-side events and integrate with the external data stores and other services.

+ +

Coherence CDI provides a way to inject named CDI beans into these extension points using custom configuration namespace handler.

+ +<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config" + xmlns:cdi="class://com.oracle.coherence.cdi.CdiNamespaceHandler" + xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd"> + +

Once you’ve declared the handler for the cdi namespace above, you can specify <cdi:bean> element in any place where you would normally use <class-name> or <class-factory-name> elements:

+ +<?xml version="1.0"?> + +<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config" + xmlns:cdi="class://com.oracle.coherence.cdi.CdiNamespaceHandler" + xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd"> + + <interceptors> + <interceptor> + <instance> + <cdi:bean>registrationListener</cdi:bean> + </instance> + </interceptor> + <interceptor> + <instance> + <cdi:bean>activationListener</cdi:bean> + </instance> + </interceptor> + </interceptors> + + <caching-scheme-mapping> + <cache-mapping> + <cache-name>*</cache-name> + <scheme-name>distributed-scheme</scheme-name> + <interceptors> + <interceptor> + <instance> + <cdi:bean>cacheListener</cdi:bean> + </instance> + </interceptor> + </interceptors> + </cache-mapping> + </caching-scheme-mapping> + + <caching-schemes> + <distributed-scheme> + <scheme-name>distributed-scheme</scheme-name> + <service-name>PartitionedCache</service-name> + <local-storage system-property="coherence.distributed.localstorage">true</local-storage> + <partition-listener> + <cdi:bean>partitionListener</cdi:bean> + </partition-listener> + <member-listener> + <cdi:bean>memberListener</cdi:bean> + </member-listener> + <backing-map-scheme> + <local-scheme/> + </backing-map-scheme> + <autostart>true</autostart> + <interceptors> + <interceptor> + <instance> + <cdi:bean>storageListener</cdi:bean> + </instance> + </interceptor> + </interceptors> + </distributed-scheme> + </caching-schemes> +</cache-config> + +

Note that you can only inject named CDI beans (beans with an explicit @Named annotations) via <cdi:bean> element. +For example, the cacheListener interceptor bean used above would look similar to this:

+ +@ApplicationScoped +@Named("cacheListener") +@EntryEvents(INSERTING) +public class MyCacheListener + implements EventInterceptor<EntryEvent<Long, String>> { + @Override + public void onEvent(EntryEvent<Long, String> e) { + // handle INSERTING event + } +} + +

Also keep in mind that only @ApplicationScoped beans can be injected, which implies that they may be shared. +For example, because we’ve used wildcard * as a cache name within the cache mapping in the example above, the same instance of cacheListener will receive events from multiple caches.

+ +

This is typically fine, as the event itself provides the details about the context that raised it, including cache name and the service it was raised from, but it does imply that any shared state that you may have within your listener class shouldn’t be context-specific and it must be safe for concurrent access from multiple threads. +If you can’t guarantee the latter, you may want to declare the onEvent method as synchronized, to ensure only one thread at a time can access any shared state you may have.

+ + +

Using CDI Observers to Handle Coherence Server-Side Events

+
+

While the above examples show that you can implement any Coherence EventInterceptor as a CDI bean and register it using <cdi:bean> element within the cache configuration file, Coherence CDI also provides a much simpler way to accomplish the same goal using standard CDI Events and Observers.

+ +

The first thing you need to do is register a single global interceptor within the cache config:

+ +<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:cdi="class://com.oracle.coherence.cdi.CdiNamespaceHandler" + xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config" + xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd"> + + <interceptors> + <interceptor> + <instance> + <cdi:bean>com.oracle.coherence.cdi.EventDispatcher</cdi:bean> + </instance> + </interceptor> + </interceptors> + + <!-- the rest of cache config as usual --> +</cache-config> + +

Coherence CDI EventDispatcher bean will then listen to all events raised by all Coherence event dispatchers and re-raise them as CDI events that you can observe. +For example, to implement the equivalent of cacheListener interceptor above, you would simply define an observer method in any CDI bean that wants to be notified when the event happens:

+ +private void onInserting(@Observes @Inserting EntryEvent<?, ?> event) { + // handle INSERTING event on any cache +} + +

The observer method above will receive all INSERTING events for all the caches, across all the services, but you can use CDI qualifiers to control that behavior:

+ +private void onInserting(@Observes @Updated @CacheName("people") EntryEvent<?, ?> event) { + // handle UPDATED event on 'people' cache only +} + +private void onRemoved(@Observes @Removed @ServiceName("Products") EntryEvent<?, ?> event) { + // handle REMOVED event on any cache on the 'Products' service +} + +

Of course, you can also remove qualifiers to broaden the scope of events your handler receives:

+ +private void onEntryEvent(@Observes EntryEvent<?, ?> event) { + // handle any event on any cache +} + +

The examples above show only how to handle EntryEvents, but the same applies to all other event types:

+ +private void onActivated(@Observes @Activated LifecycleEvent event) { + // handle cache factory activation +} + +private void onCreatedPeople(@Observes @Created @CacheName("people") CacheLifecycleEvent event) { + // handle creation of the 'people' cache +} + +private void onExecuting(@Observes @Executing @CacheName("people") @Processor(Uppercase.class) EntryProcessorEvent event) { + // intercept 'Uppercase` entry processor execution against 'people' cache +} + +

And again, you can broaden the scope by widening the type of events you observe:

+ +private void onPartitionedCacheEvent(@Observes com.tangosol.net.events.partition.cache.Event<?> event) { + // handle any/all events raised by the partitioned cache service (CacheLifecycleEvent, EntryEvent or EntryProcessorEvent) + // can use @CacheName and @ServiceName as a narrowing qualifier +} + +private void onPartitionedServiceEvent(@Observes com.tangosol.net.events.partition.Event<?> event) { + // handle any/all events raised by the partitioned service (TransactionEvent, TransferEvent or UnsolicitedCommitEvent) + // can use @ServiceName as a narrowing qualifier +} + +private void onEvent(@Observes com.tangosol.net.events.Event<?> event) { + // handle any/all events (all of the above, plus LifecycleEvent) +} + + +
Using Asynchronous Observers
+
+

All of the examples above used synchronous observers by specifying @Observes qualifier for each observer method. +However, Coherence CDI fully supports asynchronous CDI observers as well. +All you need to do is replace @Observes with @ObservesAsync in any of the examples above.

+ +private void onActivated(@ObservesAsync @Activated LifecycleEvent event) { + // handle cache factory activation +} + +private void onCreatedPeople(@ObservesAsync @Created @CacheName("people") CacheLifecycleEvent event) { + // handle creation of the 'people' cache +} + +private void onExecuting(@ObservesAsync @Executing @CacheName("people") @Processor(Uppercase.class) EntryProcessorEvent event) { + // intercept 'Uppercase` entry processor execution against 'people' cache +} + +

However, there is an important caveat.

+ +

Coherence events fall into two categories: pre- and post-commit events. +All of the events whose name ends with ing, such as Inserting, Updating, Removing or Executing are pre-commit, which means that they can either modify the data or even completely cancel the operation by throwing an exception, but in order to do so they must be synchronous to ensure that they are executed on the same thread that is executing the operation that triggered the event.

+ +

That means that you can observe them using asynchronous CDI observers, but if you want to mutate the set of entries that are part of the event payload, or veto the event by throwing an exception, you must use synchronous CDI observer.

+ +
+
+
+ +

Injecting CDI Beans into Transient Objects

+
+

Using CDI to inject Coherence objects into your application classes, and CDI beans into Coherence-managed objects will allow you to support many use cases where dependency injection may be useful, but it doesn’t cover an important use case that is somewhat specific to Coherence.

+ +

Coherence is a distributed system, and it uses serialization in order to send both the data and the processing requests from one cluster member (or remote client) to another, as well as to store data, both in memory and on disk.

+ +

Processing requests, such as entry processors and aggregators, are then deserialized on a target cluster member(s) in order to be executed, and in some cases they could benefit from dependency injection in order to avoid service lookups.

+ +

Similarly, while the data is stored in a serialized, binary format, it may need to be deserialized into user supplied classes for server-side processing, such as when executing entry processors and aggregators, and can also benefit from dependency injection (in order to support Domain-Driven Design (DDD), for example).

+ +

While these transient objects are not managed by the CDI container, Coherence CDI does support their injection after deserialization, but for performance reasons requires that you explicitly opt-in by implementing com.oracle.coherence.cdi.Injectable interface.

+ + +

Making transient classes Injectable

+
+

While not technically a true marker interface, Injectable can be treated as such for all intents and purposes. +All you need to do is add it to the implements clause of your class in order for injection on deserialization to kick in:

+ +public class InjectableBean + implements Injectable, Serializable { + + @Inject + private Converter<String, String> converter; + + private String text; + + InjectableBean() { + } + + InjectableBean(String text) { + this.text = text; + } + + String getConvertedText() { + return converter.convert(text); + } +} + +

Assuming that you have the following Converter service implementation in your application, it will be injected into InjectableBean after deserialization and the getConvertedText method will return the value of the text field converted to upper case:

+ +@ApplicationScoped +public class ToUpperConverter + implements Converter<String, String> { + @Override + public String convert(String s) { + return s.toUpperCase(); + } +} + +
+

If your Injectable class has @PostConstruct callback method, it will be called after the injection. +However, because we have no control over object’s lifecycle after that point, @PreDestroy callback will never be called).

+
+

You should note that the above functionality is not dependent on the serialization format and will work with both Java and POF serialization (or any other custom serializer), and for any object that is deserialized on any Coherence member (or even on a remote client).

+ +

While the deserialized transient objects are not true CDI managed beans, being able to inject CDI managed dependencies into them upon deserialization will likely satisfy most dependency injection requirements you will ever have in those application components. +We hope you’ll find it useful.

+ +
+
+
+
diff --git a/14.1.1-0-20/docs/pages/coherence-docker/README.js b/14.1.1-0-20/docs/pages/coherence-docker/README.js new file mode 100644 index 0000000000000..42b7262a93988 --- /dev/null +++ b/14.1.1-0-20/docs/pages/coherence-docker/README.js @@ -0,0 +1,335 @@ + + +

Coherence OCI Image

+
+

This module builds an example Coherence OCI compatible image.

+ +
+

The image built in this module is a demo and example of how to build a Coherence image using +the JIB Maven Plugin. +The image is not intended to be used in production deployments or as a base image, it is specifically +for demos, experimentation and learning purposes.

+
+ +

Image Contents

+
+

The Coherence image uses a distroless base image containing OpenJDK. +There are many advantages of a distroless image, security being the main one. +Of course, you are free to use whatever base image or build mechanism you want for your own images.

+ +

The image built by the coherence-docker module contains the following Coherence components:

+ + +
+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ComponentDescription
CoherenceThe core Coherence server
Coherence ExtendA Coherence*Extend proxy, exposed on port 20000
Coherence ManagementCoherence Management over REST, exposed on port 30000
Coherence MetricsCoherence Metrics, exposed on port 9612
Coherence TracingCoherence tracing is configured to use a Jaeger tracing server. See the Tracing section below.
+
+
+ +

Building the Image

+
+

Assuming that you have first cloned the Coherence CE project the to build the Coherence image run the following command +from the top-level Maven prj/ folder:

+ +mvn clean install -P docker -pl coherence-docker + +

The name of the image produced comes from properties in the coherence-docker module pom.xml file.

+ +

${docker.registry}/coherence-ce:<version>

+ +

Where <version> is the version of the product from the pom.xml file. +The ${docker.registry} property is the name of the registry that the image will be published to, by default +this is oraclecoherence.

+ +

So, if the version in the pom.xml is 14.1.1-0-20 the image produced will be +oraclecoherence/coherence-ce:14.1.1-0-20

+ +

To change the registry name the image can be built by specifying the docker.registry property, for example:

+ +mvn clean install -P docker -pl coherence-docker -Ddocker.registry=foo + +

The example above would build an image named foo/coherence:14.1.1-0-20

+ +
+ +

Run the image

+
+

Run the image just like any other image. In Docker this command would be:

+ +docker run -d -P oraclecoherence/coherence-ce:{project-version} + +

The -P parameter will ensure that the Extend, gRPC, management and metrics ports will all be exposed.

+ + +

Run the Image in Kubernetes

+
+

This image can be run in Kubernetes using the Coherence Operator.

+ +
+

The sections below on additional configurations do not apply when using the Coherence Operator to run the image +in Kubernetes. The operator provides functionality to configure the container correctly.

+
+
+
+ +

Specifying Coherence System Properties

+
+

Many options in Coherence can be set from System properties prefiexed with coherence.. +The issue here is that System properties are not very easy to pass into the JVM in the container, whereas environment +variables are. To help with this the main class which runs in the container will convert any environment variable +prefixed with coherence. into a System property before it starts Coherence.

+ +docker run -d -P \ + -e coherence.cluster=testing \ + -e coherence.role=storage \ + oraclecoherence/coherence-ce:{project-version} + +

The example above sets two environment variables, coherence.cluster=testing and coherence.role=storage. +These will be converted to System properties so Coherence will start the same as it would if the variables +had been passed to the JVM command line as -Dcoherence.cluster=testing -Dcoherence.role=storage

+ +
+

This only applies to environment variables prefixed with coherence. that have not already set as System +properties some other way.

+
+
+ +

Specifying JVM Options

+
+

Images built with JIB have a fixed entrypoint configured to run the application. This is not very flexible if additional +options need to be passed to the JVM. The Coherence image makes use of the JVM’s ability to load options at start-up +from a file by using a JVM option @<file-name>. The Coherence image entrypoint contains @/args/jvm-args.txt, so the +JVM will load additional options on start-up from a file named /args/jvm-args.txt. This means that additional +options can be provided by adding a volume mapping that adds this file to the container.

+ +

For example, to set the heap to 5g, the Coherence cluster name to test-cluster and role name to storage then +additional JVM arguments will be required. Create a file named jvm-args.txt containing these properties:

+ +-Xms5g +-Xmx5g +-Dcoherence.cluster=test-cluster +-Dcoherence.role=storage + +

If the file has been created in a local directory named /home/oracle/test-args then the image can be run with the following +command:

+ +docker run -d -P -v /home/oracle/test-args:/args oraclecoherence/coherence-ce:{project-version} + +

This will cause Docker to mount the local /home/oracle/test-args directory to the /args directory in the container +where the JVM will find the jvm-args.txt file.

+ +
+ +

Adding to the Classpath

+
+

Images built with JIB have a fixed classpath configured, which is not very flexible if additional resources need to be +added to the classpath. The Coherence image maps two additional directories to the classpath that are empty in the image +and may be used to add items to the classpath by mapping external volumes to these directories.

+ +

The additional classpath entries are:

+ +
    +
  • +

    /coherence/ext/lib/* - this will add all .jar files under the /coherence/ext/lib/ directory to the classpath

    + +
  • +
  • +

    /coherence/ext/conf - this adds /coherence/ext/conf to the classpath so that any classes, packages or other +resource files in this directory will be added to the classpath.

    + +
  • +
+

For example:

+ +

On the local Docker host there is a folder called /dev/my-app/lib that contains .jar files to be added to the +container classpath.

+ +docker run -d -P -v /dev/my-app/lib:/coherence/ext/lib oraclecoherence/coherence-ce:{project-version} + +

The command above maps the local directory /dev/my-app/lib to the /coherence/ext/lib in the container so that any +.jar files in the /dev/my-app/lib directory will now be on the Coherence JVM’s classpath.

+ +

On the local Docker host there is a folder called /dev/my-app/classes that contains .class files and other +application resources to be added to the container classpath.

+ +docker run -d -P -v /dev/my-app/classes:/coherence/ext/conf oraclecoherence/coherence-ce:{project-version} + +

The command above maps the local directory /dev/my-app/classes to the /coherence/ext/conf in the container so that +any classes and resource files in the /dev/my-app/classes directory will now be on the Coherence JVM’s classpath.

+ +
+
+ +

Clustering

+
+

Multiple containers can be started to form a cluster. By default, Coherence uses multi-cast for cluster discovery but +in containers this either will not work, or is not reliable, so well-known-addressing can be used.

+ +

This example is going to use basic Docker commands and links between containers. +There are other ways to achieve the same sort of functionality depending on the network configurations you want to +use in Docker.

+ +

First, determine the name to be used for the first container, in this example it will be storage-1.

+ +

Next, create a ` +Start the first container in the cluster:

+ +docker run -d -P \ + --name storage-1 \ + --hostname storage-1 \ + -e coherence.wka=storage-1 \ + -e coherence.cluster=testing \ + oraclecoherence/coherence-ce:{project-version} + +

The first container has been started with a container name of storage-1, and the host name also set to storage-1. +The container sets the WKA host name to storage-1 using -e coherence.wka=storage-1 (this will be converted to the +System property coherence.wka=storage-1 see Specifying Coherence System Properties above). +The container sets the Coherence cluster name to testing using -e coherence.cluster=testing (this will be converted +to the System property coherence.cluster=testing see Specifying Coherence System Properties above).

+ +
+

The important part here is that the container has a name, and the --hostname option has also been set. +This will allow the subsequent cluster members to find this container.

+
+

Now, subsequent containers can be started using the same cluster name and WKA host name, but with different container +names and a link to the first container, all the containers will form a single Coherence cluster:

+ +docker run -d -P \ + --name storage-2 \ + --link storage-1 \ + -e coherence.wka=storage-1 \ + -e coherence.cluster=testing \ + oraclecoherence/coherence-ce:{project-version} + +docker run -d -P \ + --name storage-3 \ + --link storage-1 \ + -e coherence.wka=storage-1 \ + -e coherence.cluster=testing \ + oraclecoherence/coherence-ce:{project-version} + +

Two more containers, storage-2 and storage-3 will now be part of the cluster.

+ +
+

All the members must have a --link option to the first container and have the same WKA and cluster name properties.

+
+
+ +

Tracing

+
+

The Coherence image comes with tracing already configured, it just requires a suitable Jaeger server to send spans to.

+ +

The simplest way to start is deploy the Jaeger all-in-one server, for example:

+ +docker run -d --name jaeger \ + -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \ + -p 5775:5775/udp \ + -p 6831:6831/udp \ + -p 6832:6832/udp \ + -p 5778:5778 \ + -p 16686:16686 \ + -p 14268:14268 \ + -p 14250:14250 \ + -p 9411:9411 \ + jaegertracing/all-in-one:latest + +

The Jaeger UI will be available to browse to at http://127.0.0.1:16686

+ +

Jaeger has been started with a container name of jaeger, so it will be discoverable using that host name by the Coherence +containers. Start the Coherence container with a link to the Jaeger container and set the JAEGER_AGENT_HOST +environment variable to jaeger:

+ +docker run -d -P --link jaeger \ + -e JAEGER_AGENT_HOST=jaeger \ + oraclecoherence/coherence-ce:{project-version} + +

Once the Coherence container is running perform some interations with it using one of the exposed services, i.e Extend +or gRPC, and spans will be sent to the Jaeger collector and will be visible in the UI by querying for the coherence +service name. The service name used can be changed by setting the JAEGER_SERVICE_NAME environment variable when +starting the container, for example:

+ +docker run -d -P --link jaeger \ + -e JAEGER_AGENT_HOST=jaeger \ + -e JAEGER_SERVICE_NAME=coherence-test + oraclecoherence/coherence-ce:{project-version} + +

Spans will now be sent to Jaeger with the service name coherence-test.

+ +

Tracing is very useful to show what happens under the covers for a given Coherence API call. Traces are more interesting +when they come from a Coherence cluster with multiple members, where the traces span different cluster members. +This can easily be done by running multiple containers with tracing enabled and configuring Clustering as +described above.

+ +
+
diff --git a/14.1.1-0-20/docs/pages/coherence-grpc-proxy-client/README.js b/14.1.1-0-20/docs/pages/coherence-grpc-proxy-client/README.js new file mode 100644 index 0000000000000..0090a76327992 --- /dev/null +++ b/14.1.1-0-20/docs/pages/coherence-grpc-proxy-client/README.js @@ -0,0 +1,7 @@ + + +

Coherence gRPC Client

+
+ +
+
diff --git a/14.1.1-0-20/docs/pages/coherence-grpc-proxy/README.js b/14.1.1-0-20/docs/pages/coherence-grpc-proxy/README.js new file mode 100644 index 0000000000000..c6dfbdc75637f --- /dev/null +++ b/14.1.1-0-20/docs/pages/coherence-grpc-proxy/README.js @@ -0,0 +1,7 @@ + + +

Coherence gRPC Server

+
+ +
+
diff --git a/14.1.1-0-20/docs/pages/coherence-mp/README.js b/14.1.1-0-20/docs/pages/coherence-mp/README.js new file mode 100644 index 0000000000000..fe77ae1714228 --- /dev/null +++ b/14.1.1-0-20/docs/pages/coherence-mp/README.js @@ -0,0 +1,42 @@ + + +

Coherence MP

+
+

Coherence provides a number of additional modules that provide support for different Microprofile APIs.

+ + + + + + + +